Implement cloning of commands

This commit is contained in:
Kovid Goyal 2022-09-20 20:44:16 +05:30
parent bc38bd75fd
commit 1811949706
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
2 changed files with 86 additions and 20 deletions

View File

@ -36,7 +36,7 @@ func (self *Command) parse_args(ctx *Context, args []string) error {
return nil return nil
} }
for len(self.args) > 0 { for len(self.Args) > 0 {
arg := consume_arg() arg := consume_arg()
if expecting_arg_for == nil { if expecting_arg_for == nil {
@ -67,7 +67,7 @@ func (self *Command) parse_args(ctx *Context, args []string) error {
} }
} else { } else {
// handle non option arg // handle non option arg
if self.AllowOptionsAfterArgs <= len(self.args) { if self.AllowOptionsAfterArgs <= len(self.Args) {
options_allowed = false options_allowed = false
} }
if self.HasSubCommands() { 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) return sc.parse_args(ctx, args_to_parse)
} }
} }
self.args = append(self.args, arg) self.Args = append(self.Args, arg)
} }
} else { } else {
// handle option value // handle option value

View File

@ -4,6 +4,7 @@ package cli
import ( import (
"fmt" "fmt"
"os"
"regexp" "regexp"
"strconv" "strconv"
"strings" "strings"
@ -132,24 +133,50 @@ type CommandGroup struct {
Title string 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) { func (self *CommandGroup) AddSubCommand(parent *Command, name string) (*Command, error) {
for _, c := range self.SubCommands { for _, c := range self.SubCommands {
if c.Name == name { if c.Name == name {
return nil, fmt.Errorf("A subcommand with the name %#v already exists in the parent command: %#v", name, parent.Name) return nil, fmt.Errorf("A subcommand with the name %#v already exists in the parent command: %#v", name, parent.Name)
} }
} }
ans := Command{ ans := NewRootCommand()
Name: name, ans.Parent = parent
Parent: parent, self.SubCommands = append(self.SubCommands, ans)
} return ans, nil
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 Options []*Option
Title string 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) { func (self *OptionGroup) AddOption(parent *Command, items ...string) (*Option, error) {
ans, err := OptionFromString(items...) ans, err := OptionFromString(items...)
if err == nil { if err == nil {
@ -169,25 +196,53 @@ func (self *OptionGroup) FindOption(name_with_hyphens string) *Option {
return nil return nil
} }
// }}}
type Command struct { type Command struct {
Name string Name string
Usage, HelpText string Usage, HelpText string
Hidden bool Hidden bool
AllowOptionsAfterArgs int
SubCommandIsOptional bool
SubCommandGroups []*CommandGroup SubCommandGroups []*CommandGroup
OptionGroups []*OptionGroup OptionGroups []*OptionGroup
Parent *Command Parent *Command
AllowOptionsAfterArgs int Args []string
SubCommandIsOptional bool }
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 { func NewRootCommand() *Command {
ans := Command{ ans := Command{
SubCommandGroups: make([]*CommandGroup, 0, 8), SubCommandGroups: make([]*CommandGroup, 0, 8),
OptionGroups: make([]*OptionGroup, 0, 8), OptionGroups: make([]*OptionGroup, 0, 8),
args: make([]string, 0, 8), Args: make([]string, 0, 8),
} }
return &ans return &ans
} }
@ -207,6 +262,18 @@ func (self *Command) AddSubCommand(group string, name string) (*Command, error)
return self.AddSubCommandGroup(group).AddSubCommand(self, name) 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 { func (self *Command) HasSubCommands() bool {
for _, g := range self.SubCommandGroups { for _, g := range self.SubCommandGroups {
if len(g.SubCommands) > 0 { if len(g.SubCommands) > 0 {
@ -218,12 +285,11 @@ func (self *Command) HasSubCommands() bool {
func (self *Command) FindSubCommand(name string) *Command { func (self *Command) FindSubCommand(name string) *Command {
for _, g := range self.SubCommandGroups { for _, g := range self.SubCommandGroups {
for _, c := range g.SubCommands { c := g.FindSubCommand(name)
if c.Name == name { if c != nil {
return c return c
} }
} }
}
return nil return nil
} }