diff --git a/app.go b/app.go new file mode 100644 index 0000000..9e540b2 --- /dev/null +++ b/app.go @@ -0,0 +1,52 @@ +package cli + +import ( + "os" +) + +type App struct { + // The name of the program. Defaults to os.Args[0] + Name string + // Description of the program. + Usage string + // Version of the program + Version string + // List of commands to execute + Commands []Command + Flags []Flag + // The action to execute when no subcommands are specified + Action Handler +} + +func NewApp() *App { + return &App{ + Name: os.Args[0], + Usage: "A new cli application", + Version: "0.0.0", + Action: helpCommand.Action, + } +} + +func (a *App) Run(arguments []string) { + // parse flags + set := flagSet(a.Flags) + set.Parse(arguments[1:]) + + // append help to commands + a.Commands = append(a.Commands, helpCommand) + + context := NewContext(a, set, set) + args := context.Args() + if len(args) > 0 { + name := args[0] + for _, c := range a.Commands { + if c.HasName(name) { + c.Run(context) + return + } + } + } + + // Run default Action + a.Action(context) +} diff --git a/cli.go b/cli.go index e97e18e..567e04a 100644 --- a/cli.go +++ b/cli.go @@ -1,43 +1,3 @@ package cli -import "os" - -// The name of the program. Defaults to os.Args[0] -var Name = os.Args[0] - -// Description of the program. -var Usage = "" - -// Version of the program -var Version = "0.0.0" - -// List of commands to execute -var Commands []Command - -var Flags []Flag - -// The action to execute when no subcommands are specified -var Action = ShowHelp - -func Run(arguments []string) { - - set := flagSet(Flags) - set.Parse(arguments[1:]) - - context := NewContext(set, set) - args := context.Args() - if len(args) > 0 { - name := args[0] - for _, c := range append(Commands, HelpCommand) { - if c.HasName(name) { - c.Run(context) - return - } - } - } - - // Run default Action - Action(context) -} - type Handler func(context *Context) diff --git a/cli_test.go b/cli_test.go index 1c083bb..1b362d3 100644 --- a/cli_test.go +++ b/cli_test.go @@ -6,38 +6,47 @@ import ( ) func Test_SettingFlags(t *testing.T) { - Flags = []Flag{ + msg := "" + app := NewApp() + app.Flags = []Flag{ StringFlag{"foo", "default", "a string flag"}, IntFlag{"bar", 42, "an int flag"}, BoolFlag{"bat", "a bool flag"}, } - Action = func(c *Context) { + app.Action = func(c *Context) { expect(t, c.String("foo"), "hello world") expect(t, c.Int("bar"), 245) expect(t, c.Bool("bat"), true) + msg = "foobar" } - Run([]string{"command", "--foo", "hello world", "--bar", "245", "--bat"}) + app.Run([]string{"command", "--foo", "hello world", "--bar", "245", "--bat"}) + expect(t, msg, "foobar") } func Test_FlagDefaults(t *testing.T) { - Flags = []Flag{ + msg := "" + app := NewApp() + app.Flags = []Flag{ StringFlag{"foo", "default", "a string flag"}, IntFlag{"bar", 42, "an int flag"}, BoolFlag{"bat", "a bool flag"}, } - Action = func(c *Context) { + app.Action = func(c *Context) { expect(t, c.String("foo"), "default") expect(t, c.Int("bar"), 42) expect(t, c.Bool("bat"), false) + msg = "foobar" } - Run([]string{"command"}) + app.Run([]string{"command"}) + expect(t, msg, "foobar") } func TestCommands(t *testing.T) { - Flags = []Flag{ + app := NewApp() + app.Flags = []Flag{ StringFlag{"name", "jeremy", "a name to print"}, } - Commands = []Command{ + app.Commands = []Command{ { Name: "print", Flags: []Flag{ @@ -49,10 +58,10 @@ func TestCommands(t *testing.T) { }, }, } - Action = func(c *Context) { + app.Action = func(c *Context) { t.Error("default action should not be called") } - Run([]string{"command", "--name", "jordie", "print", "--age", "21"}) + app.Run([]string{"command", "--name", "jordie", "print", "--age", "21"}) } /* Test Helpers */ diff --git a/command.go b/command.go index 0a9a7ec..d8e2011 100644 --- a/command.go +++ b/command.go @@ -12,7 +12,7 @@ type Command struct { func (command Command) Run(c *Context) { set := flagSet(command.Flags) set.Parse(c.Args()[1:]) - command.Action(NewContext(set, c.globalSet)) + command.Action(NewContext(c.App, set, c.globalSet)) } func (command Command) HasName(name string) bool { diff --git a/context.go b/context.go index ae50463..6c4cbad 100644 --- a/context.go +++ b/context.go @@ -10,12 +10,13 @@ import ( // can be used to retrieve context-specific Args and // parsed command-line options. type Context struct { + App *App flagSet *flag.FlagSet globalSet *flag.FlagSet } -func NewContext(set *flag.FlagSet, globalSet *flag.FlagSet) *Context { - return &Context{set, globalSet} +func NewContext(app *App, set *flag.FlagSet, globalSet *flag.FlagSet) *Context { + return &Context{app, set, globalSet} } // Looks up the value of a local int flag, returns 0 if no int flag exists diff --git a/context_test.go b/context_test.go index d859179..3c6bab5 100644 --- a/context_test.go +++ b/context_test.go @@ -10,7 +10,7 @@ func Test_New(t *testing.T) { set.Int("myflag", 12, "doc") globalSet := flag.NewFlagSet("test", 0) globalSet.Int("myflag", 42, "doc") - c := NewContext(set, globalSet) + c := NewContext(nil, set, globalSet) expect(t, c.Int("myflag"), 12) expect(t, c.GlobalInt("myflag"), 42) } @@ -18,28 +18,28 @@ func Test_New(t *testing.T) { func Test_Int(t *testing.T) { set := flag.NewFlagSet("test", 0) set.Int("myflag", 12, "doc") - c := NewContext(set, set) + c := NewContext(nil, 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, set) + c := NewContext(nil, 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, set) + c := NewContext(nil, 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, set) + c := NewContext(nil, set, 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 0e28ea0..4601236 100644 --- a/flag.go +++ b/flag.go @@ -9,7 +9,7 @@ type Flag interface { } func flagSet(flags []Flag) *flag.FlagSet { - set := flag.NewFlagSet(Name, flag.ExitOnError) + set := flag.NewFlagSet("cli", flag.ExitOnError) for _, f := range flags { f.Apply(set) } diff --git a/help.go b/help.go index b5cb95e..39f7d7f 100644 --- a/help.go +++ b/help.go @@ -4,51 +4,31 @@ import "os" import "text/tabwriter" import "text/template" -type HelpData struct { - Name string - Usage string - Version string - Commands []Command - Flags []Flag -} - -var HelpCommand = Command{ +var helpCommand = Command{ Name: "help", ShortName: "h", Usage: "Shows a list of commands or help for one command", -} - -func init() { - HelpCommand.Action = ShowHelp -} - -func ShowHelp(c *Context) { - helpTemplate := `NAME: - {{.Name}} - {{.Usage}} + Action: func(c *Context) { + helpTemplate := `NAME: + {{.Name}} - {{.Usage}} USAGE: - {{.Name}} [global options] command [command options] [arguments...] + {{.Name}} [global options] command [command options] [arguments...] VERSION: - {{.Version}} + {{.Version}} COMMANDS: - {{range .Commands}}{{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}} - {{end}} -GLOBAL OPTIONS - {{range .Flags}}{{.}} - {{end}} + {{range .Commands}}{{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}} + {{end}} +GLOBAL OPTIONS: + {{range .Flags}}{{.}} + {{end}} ` - data := HelpData{ - Name, - Usage, - Version, - append(Commands, HelpCommand), - Flags, - } - w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0) - t := template.Must(template.New("help").Parse(helpTemplate)) - t.Execute(w, data) - w.Flush() + w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0) + t := template.Must(template.New("help").Parse(helpTemplate)) + t.Execute(w, c.App) + w.Flush() + }, }