166 lines
4.1 KiB
Go
166 lines
4.1 KiB
Go
// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>
|
|
|
|
package cli
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
var _ = fmt.Print
|
|
|
|
/*
|
|
Create an [Option] from a string. Syntax is::
|
|
|
|
--option-name, --option-alias, -s
|
|
type: string
|
|
dest: destination
|
|
choices: choice1, choice2, choice 3
|
|
depth: 0
|
|
Help text on multiple lines. Indented lines are peserved as indented blocks. Blank lines
|
|
are preserved as blank lines. #placeholder_for_formatting# is replaced by the empty string.
|
|
|
|
Available types are: string, str, list, int, float, count, bool-set, bool-reset, choices
|
|
The default dest is the first --option-name which must be a long option.
|
|
If choices are specified type is set to choices automatically.
|
|
If depth is negative option is added to all subcommands. If depth is positive option is added to sub-commands upto
|
|
the specified depth.
|
|
Set the help text to "!" to have an option hidden.
|
|
*/
|
|
func OptionFromString(entries ...string) (*Option, error) {
|
|
if mpat == nil {
|
|
mpat = regexp.MustCompile("^([a-z]+)=(.+)")
|
|
}
|
|
ans := Option{
|
|
values_from_cmdline: make([]string, 0, 1),
|
|
parsed_values_from_cmdline: make([]interface{}, 0, 1),
|
|
}
|
|
scanner := bufio.NewScanner(strings.NewReader(strings.Join(entries, "\n")))
|
|
in_help := false
|
|
prev_indent := 0
|
|
help := strings.Builder{}
|
|
help.Grow(2048)
|
|
|
|
indent_of_line := func(x string) int {
|
|
return len(x) - len(strings.TrimLeft(x, " \n\t\v\f"))
|
|
}
|
|
|
|
set_default := func(x string) {
|
|
if ans.Default == "" {
|
|
ans.Default = x
|
|
}
|
|
}
|
|
|
|
for scanner.Scan() {
|
|
line := scanner.Text()
|
|
if ans.Aliases == nil {
|
|
if strings.HasPrefix(line, "--") {
|
|
parts := strings.Split(line, " ")
|
|
ans.Name = strings.ReplaceAll(parts[0], "-", "_")
|
|
ans.Aliases = make([]Alias, 0, len(parts))
|
|
for i, x := range parts {
|
|
ans.Aliases[i] = Alias{NameWithoutHyphens: strings.TrimLeft(x, "-"), IsShort: !strings.HasPrefix(x, "--")}
|
|
}
|
|
}
|
|
} else if in_help {
|
|
if line != "" {
|
|
current_indent := indent_of_line(line)
|
|
if current_indent > 1 {
|
|
if prev_indent == 0 {
|
|
help.WriteString("\n")
|
|
} else {
|
|
line = strings.TrimSpace(line)
|
|
}
|
|
}
|
|
prev_indent = current_indent
|
|
if !strings.HasSuffix(help.String(), "\n") {
|
|
help.WriteString(" ")
|
|
}
|
|
help.WriteString(line)
|
|
} else {
|
|
prev_indent = 0
|
|
help.WriteString("\n")
|
|
if !strings.HasSuffix(help.String(), "::") {
|
|
help.WriteString("\n")
|
|
}
|
|
}
|
|
} else {
|
|
matches := mpat.FindStringSubmatch(line)
|
|
if matches == nil {
|
|
continue
|
|
}
|
|
k, v := matches[1], matches[2]
|
|
switch k {
|
|
case "choices":
|
|
parts := strings.Split(v, ",")
|
|
ans.Choices = make(map[string]bool, len(parts))
|
|
ans.OptionType = StringOption
|
|
for i, x := range parts {
|
|
x = strings.TrimSpace(x)
|
|
ans.Choices[x] = true
|
|
if i == 0 && ans.Default == "" {
|
|
ans.Default = x
|
|
}
|
|
}
|
|
case "default":
|
|
ans.Default = v
|
|
case "dest":
|
|
ans.Name = v
|
|
case "depth":
|
|
depth, err := strconv.ParseInt(v, 0, 0)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
ans.Depth = int(depth)
|
|
case "condition", "completion":
|
|
default:
|
|
return nil, fmt.Errorf("Unknown option metadata key: %s", k)
|
|
case "type":
|
|
switch v {
|
|
case "choice", "choices":
|
|
ans.OptionType = StringOption
|
|
case "int":
|
|
ans.OptionType = IntegerOption
|
|
set_default("0")
|
|
case "float":
|
|
ans.OptionType = FloatOption
|
|
set_default("0")
|
|
case "count":
|
|
ans.OptionType = CountOption
|
|
set_default("0")
|
|
case "bool-set":
|
|
ans.OptionType = BoolOption
|
|
set_default("false")
|
|
case "bool-reset":
|
|
ans.OptionType = BoolOption
|
|
set_default("true")
|
|
for _, a := range ans.Aliases {
|
|
a.IsUnset = true
|
|
}
|
|
case "list":
|
|
ans.IsList = true
|
|
fallthrough
|
|
case "str", "string":
|
|
ans.OptionType = StringOption
|
|
default:
|
|
return nil, fmt.Errorf("Unknown option type: %s", v)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
ans.HelpText = help.String()
|
|
ans.Hidden = ans.HelpText == "!"
|
|
pval, err := ans.parse_value(ans.Default)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
ans.parsed_default = pval
|
|
if ans.IsList {
|
|
ans.parsed_default = []string{}
|
|
}
|
|
return &ans, nil
|
|
}
|