diff --git a/app_test.go b/app_test.go index ab6d3fb..20a568a 100644 --- a/app_test.go +++ b/app_test.go @@ -84,3 +84,61 @@ func TestApp_CommandWithArgBeforeFlags(t *testing.T) { expect(t, parsedOption, "my-option") expect(t, firstArg, "my-arg") } + +func TestApp_ParseSliceFlags(t *testing.T) { + var parsedOption, firstArg string + var parsedIntSlice []int + var parsedStringSlice []string + + app := cli.NewApp() + command := cli.Command{ + Name: "cmd", + Flags: []cli.Flag{ + cli.IntSliceFlag{"p", &cli.IntSlice{}, "set one or more ip addr"}, + cli.StringSliceFlag{"ip", &cli.StringSlice{}, "set one or more ports to open"}, + }, + Action: func(c *cli.Context) { + parsedIntSlice = c.IntSlice("p") + parsedStringSlice = c.StringSlice("ip") + parsedOption = c.String("option") + firstArg = c.Args()[0] + }, + } + app.Commands = []cli.Command{command} + + app.Run([]string{"", "cmd", "my-arg", "-p", "22", "-p", "80", "-ip", "8.8.8.8", "-ip", "8.8.4.4"}) + + IntsEquals := func(a, b []int) bool { + if len(a) != len(b) { + return false + } + for i, v := range a { + if v != b[i] { + return false + } + } + return true + } + + StrsEquals := func(a, b []string) bool { + if len(a) != len(b) { + return false + } + for i, v := range a { + if v != b[i] { + return false + } + } + return true + } + var expectedIntSlice = []int{22, 80} + var expectedStringSlice = []string{"8.8.8.8", "8.8.4.4"} + + if !IntsEquals(parsedIntSlice, expectedIntSlice) { + t.Errorf("%s does not match %s", parsedIntSlice, expectedIntSlice) + } + + if !StrsEquals(parsedStringSlice, expectedStringSlice) { + t.Errorf("%s does not match %s", parsedStringSlice, expectedStringSlice) + } +} diff --git a/command.go b/command.go index d1ca8e4..ce67258 100644 --- a/command.go +++ b/command.go @@ -1,19 +1,19 @@ package cli import ( - "os" "fmt" "io/ioutil" + "os" "strings" ) type Command struct { - Name string - ShortName string - Usage string - Description string - Action func (context *Context) - Flags []Flag + Name string + ShortName string + Usage string + Description string + Action func(context *Context) + Flags []Flag } func (c Command) Run(ctx *Context) { @@ -52,7 +52,6 @@ func (c Command) Run(ctx *Context) { context := NewContext(ctx.App, set, ctx.globalSet) checkCommandHelp(context, c.Name) - c.Action(context) } diff --git a/context.go b/context.go index 6e3867a..4574cf7 100644 --- a/context.go +++ b/context.go @@ -34,6 +34,16 @@ func (c *Context) String(name string) string { return c.lookupString(name, c.flagSet) } +// Looks up the value of a local string slice flag, returns nil if no string slice flag exists +func (c *Context) StringSlice(name string) []string { + return c.lookupStringSlice(name, c.flagSet) +} + +// Looks up the value of a local int slice flag, returns nil if no int slice flag exists +func (c *Context) IntSlice(name string) []int { + return c.lookupIntSlice(name, c.flagSet) +} + // Looks up the value of a global int flag, returns 0 if no int flag exists func (c *Context) GlobalInt(name string) int { return c.lookupInt(name, c.globalSet) @@ -49,6 +59,16 @@ func (c *Context) GlobalString(name string) string { return c.lookupString(name, c.globalSet) } +// Looks up the value of a global string slice flag, returns nil if no string slice flag exists +func (c *Context) GlobalStringSlice(name string) []string { + return c.lookupStringSlice(name, c.globalSet) +} + +// Looks up the value of a global int slice flag, returns nil if no int slice flag exists +func (c *Context) GlobalIntSlice(name string) []int { + return c.lookupIntSlice(name, c.globalSet) +} + func (c *Context) Args() []string { return c.flagSet.Args() } @@ -75,6 +95,26 @@ func (c *Context) lookupString(name string, set *flag.FlagSet) string { return "" } +func (c *Context) lookupStringSlice(name string, set *flag.FlagSet) []string { + f := set.Lookup(name) + if f != nil { + return (f.Value.(*StringSlice)).Value() + + } + + return nil +} + +func (c *Context) lookupIntSlice(name string, set *flag.FlagSet) []int { + f := set.Lookup(name) + if f != nil { + return (f.Value.(*IntSlice)).Value() + + } + + return nil +} + func (c *Context) lookupBool(name string, set *flag.FlagSet) bool { f := set.Lookup(name) if f != nil { diff --git a/flag.go b/flag.go index e37d2a0..1085184 100644 --- a/flag.go +++ b/flag.go @@ -2,6 +2,7 @@ package cli import "fmt" import "flag" +import "strconv" type Flag interface { fmt.Stringer @@ -10,12 +11,77 @@ type Flag interface { func flagSet(name string, flags []Flag) *flag.FlagSet { set := flag.NewFlagSet(name, flag.ContinueOnError) + for _, f := range flags { f.Apply(set) } return set } +type StringSlice []string + +func (f *StringSlice) Set(value string) error { + *f = append(*f, value) + return nil +} + +func (f *StringSlice) String() string { + return fmt.Sprintf("%s", *f) +} + +func (f *StringSlice) Value() []string { + return *f +} + +type StringSliceFlag struct { + Name string + Value *StringSlice + Usage string +} + +func (f StringSliceFlag) String() string { + return fmt.Sprintf("%s%v '%v'\t%v", prefixFor(f.Name), f.Name, "-"+f.Name+" option -"+f.Name+" option", f.Usage) +} + +func (f StringSliceFlag) Apply(set *flag.FlagSet) { + set.Var(f.Value, f.Name, f.Usage) +} + +type IntSlice []int + +func (f *IntSlice) Set(value string) error { + + tmp, err := strconv.Atoi(value) + if err != nil { + return err + } else { + *f = append(*f, tmp) + } + return nil +} + +func (f *IntSlice) String() string { + return fmt.Sprintf("%d", *f) +} + +func (f *IntSlice) Value() []int { + return *f +} + +type IntSliceFlag struct { + Name string + Value *IntSlice + Usage string +} + +func (f IntSliceFlag) String() string { + return fmt.Sprintf("%s%v '%v'\t%v", prefixFor(f.Name), f.Name, "-"+f.Name+" option -"+f.Name+" option", f.Usage) +} + +func (f IntSliceFlag) Apply(set *flag.FlagSet) { + set.Var(f.Value, f.Name, f.Usage) +} + type BoolFlag struct { Name string Usage string