diff --git a/tools/cli/option-from-string.go b/tools/cli/option-from-string.go index d0595614e..4ee92f71f 100644 --- a/tools/cli/option-from-string.go +++ b/tools/cli/option-from-string.go @@ -117,15 +117,18 @@ func option_from_spec(spec OptionSpec) (*Option, error) { ans.Depth = spec.Depth if spec.Choices != "" { parts := strings.Split(spec.Choices, ",") - 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 + if len(parts) == 1 { + parts = strings.Split(spec.Choices, " ") + } else { + for i, x := range parts { + parts[i] = strings.TrimSpace(x) } } + ans.Choices = parts + ans.OptionType = StringOption + if ans.Default == "" { + ans.Default = parts[0] + } } else { switch spec.Type { case "choice", "choices": @@ -151,7 +154,7 @@ func option_from_spec(spec OptionSpec) (*Option, error) { case "list": ans.IsList = true fallthrough - case "str", "string": + case "str", "string", "": ans.OptionType = StringOption default: return nil, fmt.Errorf("Unknown option type: %s", spec.Type) diff --git a/tools/cli/parse-args.go b/tools/cli/parse-args.go index 7ddb3b4be..04e3aa131 100644 --- a/tools/cli/parse-args.go +++ b/tools/cli/parse-args.go @@ -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 } - 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) if opt == nil { return &ParseError{Message: fmt.Sprintf("Unknown option: :yellow:`%s`", opt_str)} } opt.seen_option = opt_str 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 !needs_arg { 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_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 { - for _, sl := range opt_str[1:] { - err := handle_option("-"+string(sl), false, "") + runes := []rune(opt_str[1:]) + for i, sl := range runes { + err := handle_option("-"+string(sl), false, "", i < len(runes)-1) if err != nil { return err } diff --git a/tools/cli/types.go b/tools/cli/types.go index c723a5958..3c7d14fd7 100644 --- a/tools/cli/types.go +++ b/tools/cli/types.go @@ -11,6 +11,8 @@ import ( "strings" "kitty/tools/utils" + + "golang.org/x/exp/slices" ) var _ = fmt.Print @@ -52,7 +54,7 @@ type OptionSpec struct { type Option struct { Name string Aliases []Alias - Choices map[string]bool + Choices []string Default string OptionType OptionType Hidden bool @@ -157,13 +159,9 @@ func (self *Option) add_value(val string) error { } } case StringOption: - if self.Choices != nil && !self.Choices[val] { - c := make([]string, len(self.Choices)) - for k := range self.Choices { - c = append(c, k) - } + if self.Choices != nil && !slices.Contains(self.Choices, val) { 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) @@ -377,20 +375,14 @@ func (self *Command) Validate() error { 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)} } - _, err := self.Add(OptionSpec{Name: "--help -h", Type: "bool-set", Help: "Show help for this command"}) - if err != nil { - return err - } + self.Add(OptionSpec{Name: "--help -h", Type: "bool-set", Help: "Show help for this command"}) } if self.Parent == nil && !seen_dests["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)} } - _, err := self.Add(OptionSpec{Name: "--version", Type: "bool-set", Help: "Show version"}) - if err != nil { - return err - } + self.Add(OptionSpec{Name: "--version", Type: "bool-set", Help: "Show version"}) } return nil @@ -522,20 +514,29 @@ func (self *Command) AddOptionGroup(title string) *OptionGroup { return &ans } -func (self *Command) AddOptionFromString(items ...string) (*Option, error) { - return self.AddOptionGroup("").AddOptionFromString(self, items...) +func (self *Command) AddOptionToGroupFromString(group string, items ...string) *Option { + ans, err := self.AddOptionGroup(group).AddOptionFromString(self, items...) + if err != nil { + panic(err) + } + return ans + } -func (self *Command) Add(s OptionSpec) (*Option, error) { - return self.AddOptionGroup("").AddOption(self, s) +func (self *Command) AddToGroup(group string, s OptionSpec) *Option { + 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) { - return self.AddOptionGroup(group).AddOptionFromString(self, items...) +func (self *Command) AddOptionFromString(items ...string) *Option { + return self.AddOptionToGroupFromString("", items...) } -func (self *Command) AddToGroup(group string, s OptionSpec) (*Option, error) { - return self.AddOptionGroup(group).AddOption(self, s) +func (self *Command) Add(s OptionSpec) *Option { + return self.AddToGroup("", s) } func (self *Command) FindOption(name_with_hyphens string) *Option {