From 4b4324bb473fa2370994a9c8817660eb6aa4f85b Mon Sep 17 00:00:00 2001 From: Jeremy Saenz Date: Tue, 16 Jul 2013 18:53:53 -0700 Subject: [PATCH 01/15] JMS #4: Ditched the value for bool flags --- flag.go | 1 - 1 file changed, 1 deletion(-) diff --git a/flag.go b/flag.go index c619453..8003659 100644 --- a/flag.go +++ b/flag.go @@ -8,7 +8,6 @@ type Flag interface { type BoolFlag struct { Name string - Value bool Usage string } From e2733adc452e66dd7dfca4ad722ab17116009fa6 Mon Sep 17 00:00:00 2001 From: Jeremy Saenz Date: Tue, 16 Jul 2013 19:59:04 -0700 Subject: [PATCH 02/15] JMS #4: Kinda parsing flags now --- cli.go | 8 ++++++-- flag.go | 18 ++++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/cli.go b/cli.go index 6c86876..b864865 100644 --- a/cli.go +++ b/cli.go @@ -1,7 +1,7 @@ package cli +import "fmt" import "os" -import "flag" // The name of the program. Defaults to os.Args[0] var Name = os.Args[0] @@ -21,6 +21,10 @@ var Flags []Flag var Action = ShowHelp func Run(args []string) { + + set := flagSet(Flags) + set.Parse(args[1:]) + context := Context{} if len(args) > 1 { name := args[1] @@ -42,7 +46,7 @@ type Command struct { Usage string Description string Action Handler - Flags flag.FlagSet + Flags []Flag } type Context struct { diff --git a/flag.go b/flag.go index 8003659..aabb86c 100644 --- a/flag.go +++ b/flag.go @@ -1,9 +1,11 @@ package cli import "fmt" +import "flag" type Flag interface { fmt.Stringer + Apply(*flag.FlagSet) } type BoolFlag struct { @@ -21,6 +23,22 @@ func (f StringFlag) String() string { return fmt.Sprintf("--%v 'string'\t%v", f.Name, f.Usage) } +func (f StringFlag) Apply(set *flag.FlagSet) { + set.String(f.Name, f.Value, f.Usage) +} + func (f BoolFlag) String() string { return fmt.Sprintf("--%v\t%v", f.Name, f.Usage) } + +func (f BoolFlag) Apply(set *flag.FlagSet) { + set.Bool(f.Name, false, f.Usage) +} + +func flagSet(flags []Flag) *flag.FlagSet { + set := flag.NewFlagSet("set", flag.PanicOnError) + for _, f := range flags { + f.Apply(set) + } + return set +} From 5f85917dc2d6418a4ad722f1495d657b4f8dd8e6 Mon Sep 17 00:00:00 2001 From: Jeremy Saenz Date: Wed, 17 Jul 2013 08:50:02 -0700 Subject: [PATCH 03/15] JMS #4: TDD Our options map --- cli.go | 1 - options.go | 30 ++++++++++++++++++++++++++++ options_test.go | 52 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 options.go create mode 100644 options_test.go diff --git a/cli.go b/cli.go index b864865..9d477d2 100644 --- a/cli.go +++ b/cli.go @@ -1,6 +1,5 @@ package cli -import "fmt" import "os" // The name of the program. Defaults to os.Args[0] diff --git a/options.go b/options.go new file mode 100644 index 0000000..20ccce5 --- /dev/null +++ b/options.go @@ -0,0 +1,30 @@ +package cli + +type Options map[string] interface{} + +func (o Options) Int(key string) int { + val := o[key] + if val != nil { + return val.(int) + } else { + return 0 + } +} + +func (o Options) String(key string) string { + val := o[key] + if val != nil { + return val.(string) + } else { + return "" + } +} + +func (o Options) Bool(key string) bool { + val := o[key] + if val != nil { + return val.(bool) + } else { + return false + } +} diff --git a/options_test.go b/options_test.go new file mode 100644 index 0000000..ac4167a --- /dev/null +++ b/options_test.go @@ -0,0 +1,52 @@ +package cli + +import "testing" +import "reflect" + +func expect(t *testing.T, a interface{}, b interface{}) { + if a != b { + t.Errorf("Expected %v (%v) - Got %v (%v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a)) + } +} + +func refute(t *testing.T, a interface{}, b interface{}) { + if a == b { + t.Errorf("Did not expect %v (%v) - Got %v (%v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a)) + } +} + +func Test_Int(t *testing.T) { + opts := Options{ + "foo": 1, + "bar": 2, + "bat": 3, + } + + expect(t, opts.Int("foo"), 1) + expect(t, opts.Int("bar"), 2) + expect(t, opts.Int("bat"), 3) + refute(t, opts.Int("foo"), "1") + expect(t, opts.Int("nope"), 0) +} + +func Test_String(t *testing.T) { + opts := Options{ + "foo": "bar", + "bat": "baz", + } + + expect(t, opts.String("foo"), "bar") + expect(t, opts.String("bat"), "baz") + expect(t, opts.String("nope"), "") +} + +func Test_Bool(t *testing.T) { + opts := Options{ + "foo": false, + "bar": true, + } + + expect(t, opts.Bool("foo"), false) + expect(t, opts.Bool("bar"), true) + expect(t, opts.Bool("nope"), false) +} From 6f223150825d76f90a9aa2f6a772c439b104cdec Mon Sep 17 00:00:00 2001 From: Jeremy Saenz Date: Wed, 17 Jul 2013 17:00:02 -0700 Subject: [PATCH 04/15] JMS #4: Parsing command flags with context --- cli.go | 3 --- context.go | 57 +++++++++++++++++++++++++++++++++++++++++++++++++ context_test.go | 49 ++++++++++++++++++++++++++++++++++++++++++ options.go | 30 -------------------------- options_test.go | 52 -------------------------------------------- 5 files changed, 106 insertions(+), 85 deletions(-) create mode 100644 context.go create mode 100644 context_test.go delete mode 100644 options.go delete mode 100644 options_test.go diff --git a/cli.go b/cli.go index 9d477d2..ff06d75 100644 --- a/cli.go +++ b/cli.go @@ -48,7 +48,4 @@ type Command struct { Flags []Flag } -type Context struct { -} - type Handler func(context Context) diff --git a/context.go b/context.go new file mode 100644 index 0000000..9674d2f --- /dev/null +++ b/context.go @@ -0,0 +1,57 @@ +package cli + +import ( + "flag" + "strconv" +) + +// Context is a type that is passed through to +// each Handler action in a cli application. Context +// can be used to retrieve context-specific Args and +// parsed command-line options. +type Context struct { + flagSet *flag.FlagSet +} + +func NewContext(flagSet *flag.FlagSet) *Context { + return &Context{flagSet} +} + +func (c *Context) IntFlag(name string) int { + flag := c.flagSet.Lookup(name) + if flag != nil { + val, err := strconv.Atoi(flag.Value.String()) + if err != nil { + panic(err) + } + return val + } else { + return 0 + } +} + +func (c *Context) BoolFlag(name string) bool { + flag := c.flagSet.Lookup(name) + if flag != nil { + val, err := strconv.ParseBool(flag.Value.String()) + if err != nil { + panic(err) + } + return val + } else { + return false + } +} + +func (c *Context) StringFlag(name string) string { + flag := c.flagSet.Lookup(name) + if flag != nil { + return flag.Value.String() + } else { + return "" + } +} + +func (c *Context) Args() []string { + return c.flagSet.Args() +} diff --git a/context_test.go b/context_test.go new file mode 100644 index 0000000..1ce6a35 --- /dev/null +++ b/context_test.go @@ -0,0 +1,49 @@ +package cli + +import ( + "flag" + "reflect" + "testing" +) + +func expect(t *testing.T, a interface{}, b interface{}) { + if a != b { + t.Errorf("Expected %v (type %v) - Got %v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a)) + } +} + +func refute(t *testing.T, a interface{}, b interface{}) { + if a == b { + t.Errorf("Did not expect %v (type %v) - Got %v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a)) + } +} + +func Test_IntFlag(t *testing.T) { + set := flag.NewFlagSet("test", 0) + set.Int("myflag", 12, "doc") + c := NewContext(set) + expect(t, c.IntFlag("myflag"), 12) +} + +func Test_StringFlag(t *testing.T) { + set := flag.NewFlagSet("test", 0) + set.String("myflag", "hello world", "doc") + c := NewContext(set) + expect(t, c.StringFlag("myflag"), "hello world") +} + +func Test_BoolFlag(t *testing.T) { + set := flag.NewFlagSet("test", 0) + set.Bool("myflag", false, "doc") + c := NewContext(set) + expect(t, c.BoolFlag("myflag"), false) +} + +func Test_Args(t *testing.T) { + set := flag.NewFlagSet("test", 0) + set.Bool("myflag", false, "doc") + c := NewContext(set) + set.Parse([]string{"--myflag", "bat", "baz"}) + expect(t, len(c.Args()), 2) + expect(t, c.BoolFlag("myflag"), true) +} diff --git a/options.go b/options.go deleted file mode 100644 index 20ccce5..0000000 --- a/options.go +++ /dev/null @@ -1,30 +0,0 @@ -package cli - -type Options map[string] interface{} - -func (o Options) Int(key string) int { - val := o[key] - if val != nil { - return val.(int) - } else { - return 0 - } -} - -func (o Options) String(key string) string { - val := o[key] - if val != nil { - return val.(string) - } else { - return "" - } -} - -func (o Options) Bool(key string) bool { - val := o[key] - if val != nil { - return val.(bool) - } else { - return false - } -} diff --git a/options_test.go b/options_test.go deleted file mode 100644 index ac4167a..0000000 --- a/options_test.go +++ /dev/null @@ -1,52 +0,0 @@ -package cli - -import "testing" -import "reflect" - -func expect(t *testing.T, a interface{}, b interface{}) { - if a != b { - t.Errorf("Expected %v (%v) - Got %v (%v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a)) - } -} - -func refute(t *testing.T, a interface{}, b interface{}) { - if a == b { - t.Errorf("Did not expect %v (%v) - Got %v (%v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a)) - } -} - -func Test_Int(t *testing.T) { - opts := Options{ - "foo": 1, - "bar": 2, - "bat": 3, - } - - expect(t, opts.Int("foo"), 1) - expect(t, opts.Int("bar"), 2) - expect(t, opts.Int("bat"), 3) - refute(t, opts.Int("foo"), "1") - expect(t, opts.Int("nope"), 0) -} - -func Test_String(t *testing.T) { - opts := Options{ - "foo": "bar", - "bat": "baz", - } - - expect(t, opts.String("foo"), "bar") - expect(t, opts.String("bat"), "baz") - expect(t, opts.String("nope"), "") -} - -func Test_Bool(t *testing.T) { - opts := Options{ - "foo": false, - "bar": true, - } - - expect(t, opts.Bool("foo"), false) - expect(t, opts.Bool("bar"), true) - expect(t, opts.Bool("nope"), false) -} From ff4e064e21479e15b04c9ec8d7ee8aab4bd34c80 Mon Sep 17 00:00:00 2001 From: Jeremy Saenz Date: Wed, 17 Jul 2013 17:03:19 -0700 Subject: [PATCH 05/15] JMS #4: Formatting --- context.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/context.go b/context.go index 9674d2f..cbfe2f1 100644 --- a/context.go +++ b/context.go @@ -22,7 +22,7 @@ func (c *Context) IntFlag(name string) int { if flag != nil { val, err := strconv.Atoi(flag.Value.String()) if err != nil { - panic(err) + panic(err) } return val } else { @@ -35,7 +35,7 @@ func (c *Context) BoolFlag(name string) bool { if flag != nil { val, err := strconv.ParseBool(flag.Value.String()) if err != nil { - panic(err) + panic(err) } return val } else { @@ -48,10 +48,10 @@ func (c *Context) StringFlag(name string) string { if flag != nil { return flag.Value.String() } else { - return "" + return "" } } func (c *Context) Args() []string { - return c.flagSet.Args() + return c.flagSet.Args() } From 3b0fc03a6721fa4daffb6deda9723280e857201f Mon Sep 17 00:00:00 2001 From: Jeremy Saenz Date: Wed, 17 Jul 2013 17:14:25 -0700 Subject: [PATCH 06/15] JMS #4: Support for flag handling on command line --- cli.go | 4 ++-- help.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cli.go b/cli.go index ff06d75..a5ae417 100644 --- a/cli.go +++ b/cli.go @@ -24,7 +24,7 @@ func Run(args []string) { set := flagSet(Flags) set.Parse(args[1:]) - context := Context{} + context := NewContext(set) if len(args) > 1 { name := args[1] for _, c := range append(Commands, HelpCommand) { @@ -48,4 +48,4 @@ type Command struct { Flags []Flag } -type Handler func(context Context) +type Handler func(context *Context) diff --git a/help.go b/help.go index 72a4303..b5cb95e 100644 --- a/help.go +++ b/help.go @@ -22,7 +22,7 @@ func init() { HelpCommand.Action = ShowHelp } -func ShowHelp(c Context) { +func ShowHelp(c *Context) { helpTemplate := `NAME: {{.Name}} - {{.Usage}} From 04496e4364fb395ccfe2fa0fa3f61a4a8e694e5f Mon Sep 17 00:00:00 2001 From: Jeremy Saenz Date: Wed, 17 Jul 2013 17:22:30 -0700 Subject: [PATCH 07/15] JMS #4: More WIP --- cli.go | 6 +++++- flag.go | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/cli.go b/cli.go index a5ae417..e925872 100644 --- a/cli.go +++ b/cli.go @@ -22,7 +22,11 @@ var Action = ShowHelp func Run(args []string) { set := flagSet(Flags) - set.Parse(args[1:]) + err := set.Parse(args[1:]) + if err != nil { + println(err) + return + } context := NewContext(set) if len(args) > 1 { diff --git a/flag.go b/flag.go index aabb86c..28430a6 100644 --- a/flag.go +++ b/flag.go @@ -36,7 +36,7 @@ func (f BoolFlag) Apply(set *flag.FlagSet) { } func flagSet(flags []Flag) *flag.FlagSet { - set := flag.NewFlagSet("set", flag.PanicOnError) + set := flag.NewFlagSet(Name, 0) for _, f := range flags { f.Apply(set) } From 3f76896c4f02d0f9fe83200b855304a47ee63128 Mon Sep 17 00:00:00 2001 From: Jeremy Saenz Date: Thu, 18 Jul 2013 17:29:06 -0700 Subject: [PATCH 08/15] JMS #4: Getting some cli tests going. Testing out flags --- cli.go | 15 +-------------- cli_test.go | 30 ++++++++++++++++++++++++++++++ command.go | 10 ++++++++++ command_test.go | 12 ++++++++++++ context.go | 6 +++--- context_test.go | 27 +++++++-------------------- flag.go | 2 +- 7 files changed, 64 insertions(+), 38 deletions(-) create mode 100644 cli_test.go create mode 100644 command.go create mode 100644 command_test.go diff --git a/cli.go b/cli.go index e925872..049b471 100644 --- a/cli.go +++ b/cli.go @@ -22,11 +22,7 @@ var Action = ShowHelp func Run(args []string) { set := flagSet(Flags) - err := set.Parse(args[1:]) - if err != nil { - println(err) - return - } + set.Parse(args[1:]) context := NewContext(set) if len(args) > 1 { @@ -43,13 +39,4 @@ func Run(args []string) { Action(context) } -type Command struct { - Name string - ShortName string - Usage string - Description string - Action Handler - Flags []Flag -} - type Handler func(context *Context) diff --git a/cli_test.go b/cli_test.go new file mode 100644 index 0000000..2309a5f --- /dev/null +++ b/cli_test.go @@ -0,0 +1,30 @@ +package cli + +import ( + "reflect" + "testing" +) + +func Test_SimpleCLIFlags(t *testing.T) { + Flags = []Flag{ + StringFlag{"foo", "default", "a foo flag"}, + } + Action = func(c *Context) { + expect(t, c.String("foo"), "hello world") + } + Run([]string{ "command", "--foo", "hello world" }) +} + +/* Helpers */ + +func expect(t *testing.T, a interface{}, b interface{}) { + if a != b { + t.Errorf("Expected %v (type %v) - Got %v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a)) + } +} + +func refute(t *testing.T, a interface{}, b interface{}) { + if a == b { + t.Errorf("Did not expect %v (type %v) - Got %v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a)) + } +} diff --git a/command.go b/command.go new file mode 100644 index 0000000..f790515 --- /dev/null +++ b/command.go @@ -0,0 +1,10 @@ +package cli + +type Command struct { + Name string + ShortName string + Usage string + Description string + Action Handler + Flags []Flag +} diff --git a/command_test.go b/command_test.go new file mode 100644 index 0000000..81c75c0 --- /dev/null +++ b/command_test.go @@ -0,0 +1,12 @@ +package cli + +import ( + "testing" +) + +func Test_True(t *testing.T) { + expect(t, true, true) +} + +func Test_Run(t *testing.T) { +} diff --git a/context.go b/context.go index cbfe2f1..2f94568 100644 --- a/context.go +++ b/context.go @@ -17,7 +17,7 @@ func NewContext(flagSet *flag.FlagSet) *Context { return &Context{flagSet} } -func (c *Context) IntFlag(name string) int { +func (c *Context) Int(name string) int { flag := c.flagSet.Lookup(name) if flag != nil { val, err := strconv.Atoi(flag.Value.String()) @@ -30,7 +30,7 @@ func (c *Context) IntFlag(name string) int { } } -func (c *Context) BoolFlag(name string) bool { +func (c *Context) Bool(name string) bool { flag := c.flagSet.Lookup(name) if flag != nil { val, err := strconv.ParseBool(flag.Value.String()) @@ -43,7 +43,7 @@ func (c *Context) BoolFlag(name string) bool { } } -func (c *Context) StringFlag(name string) string { +func (c *Context) String(name string) string { flag := c.flagSet.Lookup(name) if flag != nil { return flag.Value.String() diff --git a/context_test.go b/context_test.go index 1ce6a35..7df9793 100644 --- a/context_test.go +++ b/context_test.go @@ -2,41 +2,28 @@ package cli import ( "flag" - "reflect" "testing" ) -func expect(t *testing.T, a interface{}, b interface{}) { - if a != b { - t.Errorf("Expected %v (type %v) - Got %v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a)) - } -} - -func refute(t *testing.T, a interface{}, b interface{}) { - if a == b { - t.Errorf("Did not expect %v (type %v) - Got %v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a)) - } -} - -func Test_IntFlag(t *testing.T) { +func Test_Int(t *testing.T) { set := flag.NewFlagSet("test", 0) set.Int("myflag", 12, "doc") c := NewContext(set) - expect(t, c.IntFlag("myflag"), 12) + expect(t, c.Int("myflag"), 12) } -func Test_StringFlag(t *testing.T) { +func Test_String(t *testing.T) { set := flag.NewFlagSet("test", 0) set.String("myflag", "hello world", "doc") c := NewContext(set) - expect(t, c.StringFlag("myflag"), "hello world") + expect(t, c.String("myflag"), "hello world") } -func Test_BoolFlag(t *testing.T) { +func Test_Bool(t *testing.T) { set := flag.NewFlagSet("test", 0) set.Bool("myflag", false, "doc") c := NewContext(set) - expect(t, c.BoolFlag("myflag"), false) + expect(t, c.Bool("myflag"), false) } func Test_Args(t *testing.T) { @@ -45,5 +32,5 @@ func Test_Args(t *testing.T) { c := NewContext(set) set.Parse([]string{"--myflag", "bat", "baz"}) expect(t, len(c.Args()), 2) - expect(t, c.BoolFlag("myflag"), true) + expect(t, c.Bool("myflag"), true) } diff --git a/flag.go b/flag.go index 28430a6..528f9d1 100644 --- a/flag.go +++ b/flag.go @@ -36,7 +36,7 @@ func (f BoolFlag) Apply(set *flag.FlagSet) { } func flagSet(flags []Flag) *flag.FlagSet { - set := flag.NewFlagSet(Name, 0) + set := flag.NewFlagSet(Name, flag.ExitOnError) for _, f := range flags { f.Apply(set) } From 3119b075d65c7d1cd859527e38f6ad192d529151 Mon Sep 17 00:00:00 2001 From: Jeremy Saenz Date: Thu, 18 Jul 2013 17:39:42 -0700 Subject: [PATCH 09/15] JMS #4: Flag testing --- cli.go | 4 +-- cli_test.go | 39 ++++++++++++++++------- command_test.go | 4 +-- context_test.go | 40 ++++++++++++------------ flag.go | 82 +++++++++++++++++++++++++++++-------------------- 5 files changed, 100 insertions(+), 69 deletions(-) diff --git a/cli.go b/cli.go index 049b471..5ac02ca 100644 --- a/cli.go +++ b/cli.go @@ -21,8 +21,8 @@ var Action = ShowHelp func Run(args []string) { - set := flagSet(Flags) - set.Parse(args[1:]) + set := flagSet(Flags) + set.Parse(args[1:]) context := NewContext(set) if len(args) > 1 { diff --git a/cli_test.go b/cli_test.go index 2309a5f..0955590 100644 --- a/cli_test.go +++ b/cli_test.go @@ -1,22 +1,39 @@ package cli import ( - "reflect" - "testing" + "reflect" + "testing" ) -func Test_SimpleCLIFlags(t *testing.T) { - Flags = []Flag{ - StringFlag{"foo", "default", "a foo flag"}, - } - Action = func(c *Context) { - expect(t, c.String("foo"), "hello world") - } - Run([]string{ "command", "--foo", "hello world" }) +func Test_SettingFlags(t *testing.T) { + Flags = []Flag{ + StringFlag{"foo", "default", "a string flag"}, + IntFlag{"bar", 42, "an int flag"}, + BoolFlag{"bat", "a bool flag"}, + } + Action = func(c *Context) { + expect(t, c.String("foo"), "hello world") + expect(t, c.Int("bar"), 245) + expect(t, c.Bool("bat"), true) + } + Run([]string{"command", "--foo", "hello world", "--bar", "245", "--bat"}) } -/* Helpers */ +func Test_FlagDefaults(t *testing.T) { + Flags = []Flag{ + StringFlag{"foo", "default", "a string flag"}, + IntFlag{"bar", 42, "an int flag"}, + BoolFlag{"bat", "a bool flag"}, + } + Action = func(c *Context) { + expect(t, c.String("foo"), "default") + expect(t, c.Int("bar"), 42) + expect(t, c.Bool("bat"), false) + } + Run([]string{"command"}) +} +/* Test Helpers */ func expect(t *testing.T, a interface{}, b interface{}) { if a != b { t.Errorf("Expected %v (type %v) - Got %v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a)) diff --git a/command_test.go b/command_test.go index 81c75c0..9730c6d 100644 --- a/command_test.go +++ b/command_test.go @@ -1,11 +1,11 @@ package cli import ( - "testing" + "testing" ) func Test_True(t *testing.T) { - expect(t, true, true) + expect(t, true, true) } func Test_Run(t *testing.T) { diff --git a/context_test.go b/context_test.go index 7df9793..042ddaa 100644 --- a/context_test.go +++ b/context_test.go @@ -1,36 +1,36 @@ package cli import ( - "flag" - "testing" + "flag" + "testing" ) func Test_Int(t *testing.T) { - set := flag.NewFlagSet("test", 0) - set.Int("myflag", 12, "doc") - c := NewContext(set) - expect(t, c.Int("myflag"), 12) + set := flag.NewFlagSet("test", 0) + set.Int("myflag", 12, "doc") + c := NewContext(set) + expect(t, c.Int("myflag"), 12) } func Test_String(t *testing.T) { - set := flag.NewFlagSet("test", 0) - set.String("myflag", "hello world", "doc") - c := NewContext(set) - expect(t, c.String("myflag"), "hello world") + set := flag.NewFlagSet("test", 0) + set.String("myflag", "hello world", "doc") + c := NewContext(set) + expect(t, c.String("myflag"), "hello world") } func Test_Bool(t *testing.T) { - set := flag.NewFlagSet("test", 0) - set.Bool("myflag", false, "doc") - c := NewContext(set) - expect(t, c.Bool("myflag"), false) + set := flag.NewFlagSet("test", 0) + set.Bool("myflag", false, "doc") + c := NewContext(set) + expect(t, c.Bool("myflag"), false) } func Test_Args(t *testing.T) { - set := flag.NewFlagSet("test", 0) - set.Bool("myflag", false, "doc") - c := NewContext(set) - set.Parse([]string{"--myflag", "bat", "baz"}) - expect(t, len(c.Args()), 2) - expect(t, c.Bool("myflag"), true) + set := flag.NewFlagSet("test", 0) + set.Bool("myflag", false, "doc") + c := NewContext(set) + set.Parse([]string{"--myflag", "bat", "baz"}) + expect(t, len(c.Args()), 2) + expect(t, c.Bool("myflag"), true) } diff --git a/flag.go b/flag.go index 528f9d1..0e28ea0 100644 --- a/flag.go +++ b/flag.go @@ -4,41 +4,55 @@ import "fmt" import "flag" type Flag interface { - fmt.Stringer - Apply(*flag.FlagSet) -} - -type BoolFlag struct { - Name string - Usage string -} - -type StringFlag struct { - Name string - Value string - Usage string -} - -func (f StringFlag) String() string { - return fmt.Sprintf("--%v 'string'\t%v", f.Name, f.Usage) -} - -func (f StringFlag) Apply(set *flag.FlagSet) { - set.String(f.Name, f.Value, f.Usage) -} - -func (f BoolFlag) String() string { - return fmt.Sprintf("--%v\t%v", f.Name, f.Usage) -} - -func (f BoolFlag) Apply(set *flag.FlagSet) { - set.Bool(f.Name, false, f.Usage) + fmt.Stringer + Apply(*flag.FlagSet) } func flagSet(flags []Flag) *flag.FlagSet { - set := flag.NewFlagSet(Name, flag.ExitOnError) - for _, f := range flags { - f.Apply(set) - } - return set + set := flag.NewFlagSet(Name, flag.ExitOnError) + for _, f := range flags { + f.Apply(set) + } + return set +} + +type BoolFlag struct { + Name string + Usage string +} + +func (f BoolFlag) String() string { + return fmt.Sprintf("--%v\t%v", f.Name, f.Usage) +} + +func (f BoolFlag) Apply(set *flag.FlagSet) { + set.Bool(f.Name, false, f.Usage) +} + +type StringFlag struct { + Name string + Value string + Usage string +} + +func (f StringFlag) String() string { + return fmt.Sprintf("--%v '%v'\t%v", f.Name, f.Value, f.Usage) +} + +func (f StringFlag) Apply(set *flag.FlagSet) { + set.String(f.Name, f.Value, f.Usage) +} + +type IntFlag struct { + Name string + Value int + Usage string +} + +func (f IntFlag) String() string { + return fmt.Sprintf("--%v '%v'\t%v", f.Name, f.Value, f.Usage) +} + +func (f IntFlag) Apply(set *flag.FlagSet) { + set.Int(f.Name, f.Value, f.Usage) } From 59b0ce24ef5340c9ba1d529c83cd9dbb6debfdf2 Mon Sep 17 00:00:00 2001 From: Jeremy Saenz Date: Thu, 18 Jul 2013 18:00:52 -0700 Subject: [PATCH 10/15] JMS #4: Support for global flags in cli.Context --- cli.go | 2 +- context.go | 81 ++++++++++++++++++++++++++++++++++--------------- context_test.go | 18 ++++++++--- 3 files changed, 71 insertions(+), 30 deletions(-) diff --git a/cli.go b/cli.go index 5ac02ca..b67250d 100644 --- a/cli.go +++ b/cli.go @@ -24,7 +24,7 @@ func Run(args []string) { set := flagSet(Flags) set.Parse(args[1:]) - context := NewContext(set) + context := NewContext(set, set) if len(args) > 1 { name := args[1] for _, c := range append(Commands, HelpCommand) { diff --git a/context.go b/context.go index 2f94568..ae50463 100644 --- a/context.go +++ b/context.go @@ -10,19 +10,54 @@ import ( // can be used to retrieve context-specific Args and // parsed command-line options. type Context struct { - flagSet *flag.FlagSet + flagSet *flag.FlagSet + globalSet *flag.FlagSet } -func NewContext(flagSet *flag.FlagSet) *Context { - return &Context{flagSet} +func NewContext(set *flag.FlagSet, globalSet *flag.FlagSet) *Context { + return &Context{set, globalSet} } +// Looks up the value of a local int flag, returns 0 if no int flag exists func (c *Context) Int(name string) int { - flag := c.flagSet.Lookup(name) - if flag != nil { - val, err := strconv.Atoi(flag.Value.String()) + return c.lookupInt(name, c.flagSet) +} + +// Looks up the value of a local bool flag, returns false if no bool flag exists +func (c *Context) Bool(name string) bool { + return c.lookupBool(name, c.flagSet) +} + +// Looks up the value of a local string flag, returns "" if no string flag exists +func (c *Context) String(name string) string { + return c.lookupString(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) +} + +// Looks up the value of a global bool flag, returns false if no bool flag exists +func (c *Context) GlobalBool(name string) bool { + return c.lookupBool(name, c.globalSet) +} + +// Looks up the value of a global string flag, returns "" if no string flag exists +func (c *Context) GlobalString(name string) string { + return c.lookupString(name, c.globalSet) +} + +func (c *Context) Args() []string { + return c.flagSet.Args() +} + +func (c *Context) lookupInt(name string, set *flag.FlagSet) int { + f := set.Lookup(name) + if f != nil { + val, err := strconv.Atoi(f.Value.String()) if err != nil { - panic(err) + return 0 } return val } else { @@ -30,28 +65,24 @@ func (c *Context) Int(name string) int { } } -func (c *Context) Bool(name string) bool { - flag := c.flagSet.Lookup(name) - if flag != nil { - val, err := strconv.ParseBool(flag.Value.String()) +func (c *Context) lookupString(name string, set *flag.FlagSet) string { + f := set.Lookup(name) + if f != nil { + return f.Value.String() + } else { + return "" + } +} + +func (c *Context) lookupBool(name string, set *flag.FlagSet) bool { + f := set.Lookup(name) + if f != nil { + val, err := strconv.ParseBool(f.Value.String()) if err != nil { - panic(err) + return false } return val } else { return false } } - -func (c *Context) String(name string) string { - flag := c.flagSet.Lookup(name) - if flag != nil { - return flag.Value.String() - } else { - return "" - } -} - -func (c *Context) Args() []string { - return c.flagSet.Args() -} diff --git a/context_test.go b/context_test.go index 042ddaa..d859179 100644 --- a/context_test.go +++ b/context_test.go @@ -5,31 +5,41 @@ import ( "testing" ) +func Test_New(t *testing.T) { + set := flag.NewFlagSet("test", 0) + set.Int("myflag", 12, "doc") + globalSet := flag.NewFlagSet("test", 0) + globalSet.Int("myflag", 42, "doc") + c := NewContext(set, globalSet) + expect(t, c.Int("myflag"), 12) + expect(t, c.GlobalInt("myflag"), 42) +} + func Test_Int(t *testing.T) { set := flag.NewFlagSet("test", 0) set.Int("myflag", 12, "doc") - c := NewContext(set) + c := NewContext(set, set) expect(t, c.Int("myflag"), 12) } func Test_String(t *testing.T) { set := flag.NewFlagSet("test", 0) set.String("myflag", "hello world", "doc") - c := NewContext(set) + c := NewContext(set, set) expect(t, c.String("myflag"), "hello world") } func Test_Bool(t *testing.T) { set := flag.NewFlagSet("test", 0) set.Bool("myflag", false, "doc") - c := NewContext(set) + c := NewContext(set, set) expect(t, c.Bool("myflag"), false) } func Test_Args(t *testing.T) { set := flag.NewFlagSet("test", 0) set.Bool("myflag", false, "doc") - c := NewContext(set) + c := NewContext(set, set) set.Parse([]string{"--myflag", "bat", "baz"}) expect(t, len(c.Args()), 2) expect(t, c.Bool("myflag"), true) From 393a533b662efa7c844f125ec33e2ec7773320fd Mon Sep 17 00:00:00 2001 From: Jeremy Saenz Date: Thu, 18 Jul 2013 18:03:40 -0700 Subject: [PATCH 11/15] JMS #4: Removing tests from command_test --- command_test.go | 7 ------- 1 file changed, 7 deletions(-) diff --git a/command_test.go b/command_test.go index 9730c6d..86098b9 100644 --- a/command_test.go +++ b/command_test.go @@ -3,10 +3,3 @@ package cli import ( "testing" ) - -func Test_True(t *testing.T) { - expect(t, true, true) -} - -func Test_Run(t *testing.T) { -} From 5bb6efd8bdfc3fcfbf6dcb6ae5265773c1919df3 Mon Sep 17 00:00:00 2001 From: Jeremy Saenz Date: Thu, 18 Jul 2013 18:04:30 -0700 Subject: [PATCH 12/15] JMS #4: Fixed compile error --- command_test.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/command_test.go b/command_test.go index 86098b9..7f1e458 100644 --- a/command_test.go +++ b/command_test.go @@ -1,5 +1 @@ package cli - -import ( - "testing" -) From 87d3b81d555b411d4c9a67f1e0b5c0a43bee4161 Mon Sep 17 00:00:00 2001 From: Jeremy Saenz Date: Thu, 18 Jul 2013 19:17:33 -0700 Subject: [PATCH 13/15] JMS #4: Parsing arguments for subcommands as well --- cli.go | 13 ++++++++----- cli_test.go | 22 ++++++++++++++++++++++ 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/cli.go b/cli.go index b67250d..e77cc4c 100644 --- a/cli.go +++ b/cli.go @@ -19,17 +19,20 @@ var Flags []Flag // The action to execute when no subcommands are specified var Action = ShowHelp -func Run(args []string) { +func Run(arguments []string) { set := flagSet(Flags) - set.Parse(args[1:]) + set.Parse(arguments[1:]) context := NewContext(set, set) - if len(args) > 1 { - name := args[1] + args := context.Args() + if len(args) > 0 { + name := args[0] for _, c := range append(Commands, HelpCommand) { if c.Name == name || c.ShortName == name { - c.Action(context) + locals := flagSet(c.Flags) + locals.Parse(args[1:]) + c.Action(NewContext(locals, set)) return } } diff --git a/cli_test.go b/cli_test.go index 0955590..1c083bb 100644 --- a/cli_test.go +++ b/cli_test.go @@ -33,6 +33,28 @@ func Test_FlagDefaults(t *testing.T) { Run([]string{"command"}) } +func TestCommands(t *testing.T) { + Flags = []Flag{ + StringFlag{"name", "jeremy", "a name to print"}, + } + Commands = []Command{ + { + Name: "print", + Flags: []Flag{ + IntFlag{"age", 50, "the age of the person"}, + }, + Action: func(c *Context) { + expect(t, c.GlobalString("name"), "jordie") + expect(t, c.Int("age"), 21) + }, + }, + } + Action = func(c *Context) { + t.Error("default action should not be called") + } + Run([]string{"command", "--name", "jordie", "print", "--age", "21"}) +} + /* Test Helpers */ func expect(t *testing.T, a interface{}, b interface{}) { if a != b { From 4495e2e996c7906e7724f399b1e5c6c65cdad7a0 Mon Sep 17 00:00:00 2001 From: Jeremy Saenz Date: Thu, 18 Jul 2013 19:23:00 -0700 Subject: [PATCH 14/15] JMS #4: Delegating to Command.Run --- cli.go | 4 +--- command.go | 6 ++++++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/cli.go b/cli.go index e77cc4c..32eb1de 100644 --- a/cli.go +++ b/cli.go @@ -30,9 +30,7 @@ func Run(arguments []string) { name := args[0] for _, c := range append(Commands, HelpCommand) { if c.Name == name || c.ShortName == name { - locals := flagSet(c.Flags) - locals.Parse(args[1:]) - c.Action(NewContext(locals, set)) + c.Run(context) return } } diff --git a/command.go b/command.go index f790515..3ebb22b 100644 --- a/command.go +++ b/command.go @@ -8,3 +8,9 @@ type Command struct { Action Handler Flags []Flag } + +func (command Command) Run(c *Context) { + set := flagSet(command.Flags) + set.Parse(c.Args()[1:]) + command.Action(NewContext(set, c.globalSet)) +} From c15c42a7a840f930ffc7fd98415ab78fd47d2b64 Mon Sep 17 00:00:00 2001 From: Jeremy Saenz Date: Thu, 18 Jul 2013 19:30:18 -0700 Subject: [PATCH 15/15] JMS #4: Command.HasName --- cli.go | 2 +- command.go | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/cli.go b/cli.go index 32eb1de..e97e18e 100644 --- a/cli.go +++ b/cli.go @@ -29,7 +29,7 @@ func Run(arguments []string) { if len(args) > 0 { name := args[0] for _, c := range append(Commands, HelpCommand) { - if c.Name == name || c.ShortName == name { + if c.HasName(name) { c.Run(context) return } diff --git a/command.go b/command.go index 3ebb22b..0a9a7ec 100644 --- a/command.go +++ b/command.go @@ -14,3 +14,7 @@ func (command Command) Run(c *Context) { set.Parse(c.Args()[1:]) command.Action(NewContext(set, c.globalSet)) } + +func (command Command) HasName(name string) bool { + return command.Name == name || command.ShortName == name +}