From 4d9038a156af4f41d55e51685c91a8499882b832 Mon Sep 17 00:00:00 2001 From: Thesyncim Date: Tue, 24 Sep 2013 02:41:31 +0100 Subject: [PATCH 1/2] add ability to parse []string types --- command.go | 15 +++++++-------- context.go | 13 +++++++++++++ flag.go | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 54 insertions(+), 8 deletions(-) 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..f7e88d8 100644 --- a/context.go +++ b/context.go @@ -34,6 +34,10 @@ func (c *Context) String(name string) string { return c.lookupString(name, c.flagSet) } +func (c *Context) StringSlice(name string) flag.Value { + return c.lookupStringSlice(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) @@ -75,6 +79,15 @@ func (c *Context) lookupString(name string, set *flag.FlagSet) string { return "" } +func (c *Context) lookupStringSlice(name string, set *flag.FlagSet) flag.Value { + f := set.Lookup(name) + if f != nil { + return f.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..fc8eb14 100644 --- a/flag.go +++ b/flag.go @@ -8,14 +8,48 @@ type Flag interface { Apply(*flag.FlagSet) } +type SliceFlag interface { + Value() []string +} + 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 (i *StringSlice) Set(value string) error { + *i = append(*i, value) + return nil +} + +func (i *StringSlice) String() string { + return fmt.Sprintf("%s", *i) +} + +func (i *StringSlice) Value() []string { + return *i +} + +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 BoolFlag struct { Name string Usage string From ed96efff1bb1f94add59d22f329f196e6c6ec701 Mon Sep 17 00:00:00 2001 From: Thesyncim Date: Tue, 24 Sep 2013 20:36:01 +0100 Subject: [PATCH 2/2] add simple test , fix errors and unused interface SliceFlag --- app_test.go | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++++ context.go | 33 +++++++++++++++++++++++++++--- flag.go | 52 ++++++++++++++++++++++++++++++++++++++--------- 3 files changed, 130 insertions(+), 13 deletions(-) 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/context.go b/context.go index f7e88d8..4574cf7 100644 --- a/context.go +++ b/context.go @@ -34,10 +34,16 @@ func (c *Context) String(name string) string { return c.lookupString(name, c.flagSet) } -func (c *Context) StringSlice(name string) flag.Value { +// 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) @@ -53,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() } @@ -79,10 +95,21 @@ func (c *Context) lookupString(name string, set *flag.FlagSet) string { return "" } -func (c *Context) lookupStringSlice(name string, set *flag.FlagSet) flag.Value { +func (c *Context) lookupStringSlice(name string, set *flag.FlagSet) []string { f := set.Lookup(name) if f != nil { - return f.Value + 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 diff --git a/flag.go b/flag.go index fc8eb14..1085184 100644 --- a/flag.go +++ b/flag.go @@ -2,16 +2,13 @@ package cli import "fmt" import "flag" +import "strconv" type Flag interface { fmt.Stringer Apply(*flag.FlagSet) } -type SliceFlag interface { - Value() []string -} - func flagSet(name string, flags []Flag) *flag.FlagSet { set := flag.NewFlagSet(name, flag.ContinueOnError) @@ -23,17 +20,17 @@ func flagSet(name string, flags []Flag) *flag.FlagSet { type StringSlice []string -func (i *StringSlice) Set(value string) error { - *i = append(*i, value) +func (f *StringSlice) Set(value string) error { + *f = append(*f, value) return nil } -func (i *StringSlice) String() string { - return fmt.Sprintf("%s", *i) +func (f *StringSlice) String() string { + return fmt.Sprintf("%s", *f) } -func (i *StringSlice) Value() []string { - return *i +func (f *StringSlice) Value() []string { + return *f } type StringSliceFlag struct { @@ -50,6 +47,41 @@ 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