Various fixes to CLI parsing

This commit is contained in:
Kovid Goyal 2022-09-23 17:17:32 +05:30
parent 79cfc1e70a
commit 04022ed363
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
3 changed files with 46 additions and 35 deletions

View File

@ -117,15 +117,18 @@ func option_from_spec(spec OptionSpec) (*Option, error) {
ans.Depth = spec.Depth ans.Depth = spec.Depth
if spec.Choices != "" { if spec.Choices != "" {
parts := strings.Split(spec.Choices, ",") parts := strings.Split(spec.Choices, ",")
ans.Choices = make(map[string]bool, len(parts)) if len(parts) == 1 {
ans.OptionType = StringOption parts = strings.Split(spec.Choices, " ")
for i, x := range parts { } else {
x = strings.TrimSpace(x) for i, x := range parts {
ans.Choices[x] = true parts[i] = strings.TrimSpace(x)
if i == 0 && ans.Default == "" {
ans.Default = x
} }
} }
ans.Choices = parts
ans.OptionType = StringOption
if ans.Default == "" {
ans.Default = parts[0]
}
} else { } else {
switch spec.Type { switch spec.Type {
case "choice", "choices": case "choice", "choices":
@ -151,7 +154,7 @@ func option_from_spec(spec OptionSpec) (*Option, error) {
case "list": case "list":
ans.IsList = true ans.IsList = true
fallthrough fallthrough
case "str", "string": case "str", "string", "":
ans.OptionType = StringOption ans.OptionType = StringOption
default: default:
return nil, fmt.Errorf("Unknown option type: %s", spec.Type) return nil, fmt.Errorf("Unknown option type: %s", spec.Type)

View File

@ -19,13 +19,16 @@ func (self *Command) parse_args(ctx *Context, args []string) error {
consume_arg := func() string { ans := args_to_parse[0]; args_to_parse = args_to_parse[1:]; return ans } consume_arg := func() string { ans := args_to_parse[0]; args_to_parse = args_to_parse[1:]; return ans }
handle_option := func(opt_str string, has_val bool, opt_val string) error { handle_option := func(opt_str string, has_val bool, opt_val string, val_not_allowed bool) error {
opt := self.FindOption(opt_str) opt := self.FindOption(opt_str)
if opt == nil { if opt == nil {
return &ParseError{Message: fmt.Sprintf("Unknown option: :yellow:`%s`", opt_str)} return &ParseError{Message: fmt.Sprintf("Unknown option: :yellow:`%s`", opt_str)}
} }
opt.seen_option = opt_str opt.seen_option = opt_str
needs_arg := opt.needs_argument() needs_arg := opt.needs_argument()
if needs_arg && val_not_allowed {
return &ParseError{Message: fmt.Sprintf("The option : :yellow:`%s` must be followed by a value not another option", opt_str)}
}
if has_val { if has_val {
if !needs_arg { if !needs_arg {
return &ParseError{Message: fmt.Sprintf("The option: :yellow:`%s` does not take values", opt_str)} return &ParseError{Message: fmt.Sprintf("The option: :yellow:`%s` does not take values", opt_str)}
@ -59,10 +62,14 @@ func (self *Command) parse_args(ctx *Context, args []string) error {
opt_val = parts[1] opt_val = parts[1]
} }
opt_str = parts[0] opt_str = parts[0]
handle_option(opt_str, has_val, opt_val) err := handle_option(opt_str, has_val, opt_val, false)
if err != nil {
return err
}
} else { } else {
for _, sl := range opt_str[1:] { runes := []rune(opt_str[1:])
err := handle_option("-"+string(sl), false, "") for i, sl := range runes {
err := handle_option("-"+string(sl), false, "", i < len(runes)-1)
if err != nil { if err != nil {
return err return err
} }

View File

@ -11,6 +11,8 @@ import (
"strings" "strings"
"kitty/tools/utils" "kitty/tools/utils"
"golang.org/x/exp/slices"
) )
var _ = fmt.Print var _ = fmt.Print
@ -52,7 +54,7 @@ type OptionSpec struct {
type Option struct { type Option struct {
Name string Name string
Aliases []Alias Aliases []Alias
Choices map[string]bool Choices []string
Default string Default string
OptionType OptionType OptionType OptionType
Hidden bool Hidden bool
@ -157,13 +159,9 @@ func (self *Option) add_value(val string) error {
} }
} }
case StringOption: case StringOption:
if self.Choices != nil && !self.Choices[val] { if self.Choices != nil && !slices.Contains(self.Choices, val) {
c := make([]string, len(self.Choices))
for k := range self.Choices {
c = append(c, k)
}
return &ParseError{Option: self, Message: fmt.Sprintf(":yellow:`%s` is not a valid value for :bold:`%s`. Valid values: %s", return &ParseError{Option: self, Message: fmt.Sprintf(":yellow:`%s` is not a valid value for :bold:`%s`. Valid values: %s",
val, self.seen_option, strings.Join(c, ", "), val, self.seen_option, strings.Join(self.Choices, ", "),
)} )}
} }
self.values_from_cmdline = append(self.values_from_cmdline, val) self.values_from_cmdline = append(self.values_from_cmdline, val)
@ -377,20 +375,14 @@ func (self *Command) Validate() error {
if seen_flags["-h"] || seen_flags["--help"] { if seen_flags["-h"] || seen_flags["--help"] {
return &ParseError{Message: fmt.Sprintf("The --help or -h flags are assigned to an option other than Help in %s", self.Name)} return &ParseError{Message: fmt.Sprintf("The --help or -h flags are assigned to an option other than Help in %s", self.Name)}
} }
_, err := self.Add(OptionSpec{Name: "--help -h", Type: "bool-set", Help: "Show help for this command"}) self.Add(OptionSpec{Name: "--help -h", Type: "bool-set", Help: "Show help for this command"})
if err != nil {
return err
}
} }
if self.Parent == nil && !seen_dests["Version"] { if self.Parent == nil && !seen_dests["Version"] {
if seen_flags["--version"] { if seen_flags["--version"] {
return &ParseError{Message: fmt.Sprintf("The --version flag is assigned to an option other than Version in %s", self.Name)} return &ParseError{Message: fmt.Sprintf("The --version flag is assigned to an option other than Version in %s", self.Name)}
} }
_, err := self.Add(OptionSpec{Name: "--version", Type: "bool-set", Help: "Show version"}) self.Add(OptionSpec{Name: "--version", Type: "bool-set", Help: "Show version"})
if err != nil {
return err
}
} }
return nil return nil
@ -522,20 +514,29 @@ func (self *Command) AddOptionGroup(title string) *OptionGroup {
return &ans return &ans
} }
func (self *Command) AddOptionFromString(items ...string) (*Option, error) { func (self *Command) AddOptionToGroupFromString(group string, items ...string) *Option {
return self.AddOptionGroup("").AddOptionFromString(self, items...) ans, err := self.AddOptionGroup(group).AddOptionFromString(self, items...)
if err != nil {
panic(err)
}
return ans
} }
func (self *Command) Add(s OptionSpec) (*Option, error) { func (self *Command) AddToGroup(group string, s OptionSpec) *Option {
return self.AddOptionGroup("").AddOption(self, s) ans, err := self.AddOptionGroup(group).AddOption(self, s)
if err != nil {
panic(err)
}
return ans
} }
func (self *Command) AddOptionToGroupFromString(group string, items ...string) (*Option, error) { func (self *Command) AddOptionFromString(items ...string) *Option {
return self.AddOptionGroup(group).AddOptionFromString(self, items...) return self.AddOptionToGroupFromString("", items...)
} }
func (self *Command) AddToGroup(group string, s OptionSpec) (*Option, error) { func (self *Command) Add(s OptionSpec) *Option {
return self.AddOptionGroup(group).AddOption(self, s) return self.AddToGroup("", s)
} }
func (self *Command) FindOption(name_with_hyphens string) *Option { func (self *Command) FindOption(name_with_hyphens string) *Option {