From 1811949706a2ba41ab49c2bc18f65744204022dd Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 20 Sep 2022 20:44:16 +0530 Subject: [PATCH] Implement cloning of commands --- tools/cli/parse-args.go | 6 +-- tools/cli/types.go | 100 +++++++++++++++++++++++++++++++++------- 2 files changed, 86 insertions(+), 20 deletions(-) diff --git a/tools/cli/parse-args.go b/tools/cli/parse-args.go index cb7bb69c6..af5e9b93f 100644 --- a/tools/cli/parse-args.go +++ b/tools/cli/parse-args.go @@ -36,7 +36,7 @@ func (self *Command) parse_args(ctx *Context, args []string) error { return nil } - for len(self.args) > 0 { + for len(self.Args) > 0 { arg := consume_arg() if expecting_arg_for == nil { @@ -67,7 +67,7 @@ func (self *Command) parse_args(ctx *Context, args []string) error { } } else { // handle non option arg - if self.AllowOptionsAfterArgs <= len(self.args) { + if self.AllowOptionsAfterArgs <= len(self.Args) { options_allowed = false } if self.HasSubCommands() { @@ -80,7 +80,7 @@ func (self *Command) parse_args(ctx *Context, args []string) error { return sc.parse_args(ctx, args_to_parse) } } - self.args = append(self.args, arg) + self.Args = append(self.Args, arg) } } else { // handle option value diff --git a/tools/cli/types.go b/tools/cli/types.go index 36d84ca7d..ba387ae62 100644 --- a/tools/cli/types.go +++ b/tools/cli/types.go @@ -4,6 +4,7 @@ package cli import ( "fmt" + "os" "regexp" "strconv" "strings" @@ -132,24 +133,50 @@ type CommandGroup struct { Title string } +func (self *CommandGroup) Clone(parent *Command) *CommandGroup { + ans := CommandGroup{Title: self.Title, SubCommands: make([]*Command, 0, len(self.SubCommands))} + for i, o := range self.SubCommands { + self.SubCommands[i] = o.Clone(parent) + } + return &ans +} + func (self *CommandGroup) AddSubCommand(parent *Command, name string) (*Command, error) { for _, c := range self.SubCommands { if c.Name == name { return nil, fmt.Errorf("A subcommand with the name %#v already exists in the parent command: %#v", name, parent.Name) } } - ans := Command{ - Name: name, - Parent: parent, - } - return &ans, nil + ans := NewRootCommand() + ans.Parent = parent + self.SubCommands = append(self.SubCommands, ans) + return ans, nil } -type OptionGroup struct { +func (self *CommandGroup) FindSubCommand(name string) *Command { + for _, c := range self.SubCommands { + if c.Name == name { + return c + } + } + return nil +} + +type OptionGroup struct { // {{{ Options []*Option Title string } +func (self *OptionGroup) Clone(parent *Command) *OptionGroup { + ans := OptionGroup{Title: self.Title, Options: make([]*Option, 0, len(self.Options))} + for i, o := range self.Options { + c := *o + c.Parent = parent + self.Options[i] = &c + } + return &ans +} + func (self *OptionGroup) AddOption(parent *Command, items ...string) (*Option, error) { ans, err := OptionFromString(items...) if err == nil { @@ -169,25 +196,53 @@ func (self *OptionGroup) FindOption(name_with_hyphens string) *Option { return nil } +// }}} + type Command struct { - Name string - Usage, HelpText string - Hidden bool + Name string + Usage, HelpText string + Hidden bool + AllowOptionsAfterArgs int + SubCommandIsOptional bool + SubCommandGroups []*CommandGroup OptionGroups []*OptionGroup Parent *Command - AllowOptionsAfterArgs int - SubCommandIsOptional bool + Args []string +} - args []string +func (self *Command) Clone(parent *Command) *Command { + ans := *self + ans.Args = make([]string, 0, 8) + ans.Parent = parent + ans.SubCommandGroups = make([]*CommandGroup, 0, len(self.SubCommandGroups)) + ans.OptionGroups = make([]*OptionGroup, 0, len(self.OptionGroups)) + + for i, o := range self.OptionGroups { + ans.OptionGroups[i] = o.Clone(&ans) + } + for i, g := range self.SubCommandGroups { + ans.SubCommandGroups[i] = g.Clone(&ans) + } + return &ans +} + +func (self *Command) AddClone(group string, src *Command) (*Command, error) { + c := src.Clone(self) + g := self.AddSubCommandGroup(group) + if g.FindSubCommand(c.Name) != nil { + return nil, fmt.Errorf("A sub command with the name: %s already exists in %s", c.Name, self.Name) + } + g.SubCommands = append(g.SubCommands, c) + return c, nil } func NewRootCommand() *Command { ans := Command{ SubCommandGroups: make([]*CommandGroup, 0, 8), OptionGroups: make([]*OptionGroup, 0, 8), - args: make([]string, 0, 8), + Args: make([]string, 0, 8), } return &ans } @@ -207,6 +262,18 @@ func (self *Command) AddSubCommand(group string, name string) (*Command, error) return self.AddSubCommandGroup(group).AddSubCommand(self, name) } +func (self *Command) ParseArgs(args []string) (*Command, error) { + if args == nil { + args = os.Args + } + ctx := Context{SeenCommands: make([]*Command, 0, 4)} + err := self.parse_args(&ctx, args[1:]) + if err != nil { + return nil, err + } + return ctx.SeenCommands[len(ctx.SeenCommands)-1], nil +} + func (self *Command) HasSubCommands() bool { for _, g := range self.SubCommandGroups { if len(g.SubCommands) > 0 { @@ -218,10 +285,9 @@ func (self *Command) HasSubCommands() bool { func (self *Command) FindSubCommand(name string) *Command { for _, g := range self.SubCommandGroups { - for _, c := range g.SubCommands { - if c.Name == name { - return c - } + c := g.FindSubCommand(name) + if c != nil { + return c } } return nil