From 30d83a70a74d10fb102eef50c131a91607ccb90b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20R=C3=B8dseth?= Date: Fri, 15 Nov 2013 11:30:45 +0100 Subject: [PATCH 1/6] Added two convenience functions for retrieving arguments --- context.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/context.go b/context.go index 877aaa1..d944d70 100644 --- a/context.go +++ b/context.go @@ -129,3 +129,17 @@ func (c *Context) lookupBool(name string, set *flag.FlagSet) bool { return false } + +// Returns the nth argument, or just a blank string +func (c *Context) GetArg(n int) string { + args := c.Args() + if len(args) < n { + return args[n] + } + return "" +} + +// Returns the first argument, or just a blank string, for convenience +func (c *Context) FirstArg() string { + return c.GetArg(0) +} From f93652a890fa3f726d6d3091ef836862d5f9e4da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20R=C3=B8dseth?= Date: Fri, 15 Nov 2013 11:35:24 +0100 Subject: [PATCH 2/6] Fixed all problems/warnings reported by "go vet" --- app.go | 2 +- app_test.go | 8 ++++---- command.go | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app.go b/app.go index c5ff05a..65cbf49 100644 --- a/app.go +++ b/app.go @@ -51,7 +51,7 @@ func (a *App) Run(arguments []string) error { context := NewContext(a, set, set) if err != nil { - fmt.Println("Incorrect Usage.\n") + fmt.Printf("Incorrect Usage.\n\n") ShowAppHelp(context) fmt.Println("") return err diff --git a/app_test.go b/app_test.go index 6cc488b..f3646e9 100644 --- a/app_test.go +++ b/app_test.go @@ -14,7 +14,7 @@ func ExampleApp() { app := cli.NewApp() app.Name = "greet" app.Flags = []cli.Flag{ - cli.StringFlag{"name", "bob", "a name to say"}, + cli.StringFlag{Name: "name", Value: "bob", Usage: "a name to say"}, } app.Action = func(c *cli.Context) { fmt.Printf("Hello %v\n", c.String("name")) @@ -72,7 +72,7 @@ func TestApp_CommandWithArgBeforeFlags(t *testing.T) { command := cli.Command{ Name: "cmd", Flags: []cli.Flag{ - cli.StringFlag{"option", "", "some option"}, + cli.StringFlag{Name: "option", Value: "", Usage: "some option"}, }, Action: func(c *cli.Context) { parsedOption = c.String("option") @@ -96,8 +96,8 @@ func TestApp_ParseSliceFlags(t *testing.T) { 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"}, + cli.IntSliceFlag{Name: "p", Value: &cli.IntSlice{}, Usage: "set one or more ip addr"}, + cli.StringSliceFlag{Name: "ip", Value: &cli.StringSlice{}, Usage: "set one or more ports to open"}, }, Action: func(c *cli.Context) { parsedIntSlice = c.IntSlice("p") diff --git a/command.go b/command.go index 03f1c11..79af87e 100644 --- a/command.go +++ b/command.go @@ -51,7 +51,7 @@ func (c Command) Run(ctx *Context) error { } if err != nil { - fmt.Println("Incorrect Usage.\n") + fmt.Printf("Incorrect Usage.\n\n") ShowCommandHelp(ctx, c.Name) fmt.Println("") return err From 7050f048d1da6b1c3e50289fa76578d3530e368b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20R=C3=B8dseth?= Date: Fri, 15 Nov 2013 12:40:18 +0100 Subject: [PATCH 3/6] Added simple man page support --- app.go | 28 ++++++++++++++++++++++++---- help.go | 36 +++++++++++++++++++++++++++++++++++- 2 files changed, 59 insertions(+), 5 deletions(-) diff --git a/app.go b/app.go index 65cbf49..db9e984 100644 --- a/app.go +++ b/app.go @@ -4,6 +4,7 @@ import ( "fmt" "io/ioutil" "os" + "time" ) // App is the main structure of a cli application. It is recomended that @@ -21,15 +22,34 @@ type App struct { Flags []Flag // The action to execute when no subcommands are specified Action func(context *Context) + // Compilation date + Compiled time.Time + // Author + Author string + // Author e-mail + Email string +} + +// Tries to find out when this binary was compiled. +// Returns the current time if it fails to find it. +func compileTime() time.Time { + info, err := os.Stat(os.Args[0]) + if err != nil { + return time.Now() + } + return info.ModTime() } // Creates a new cli Application with some reasonable defaults for Name, Usage, Version and Action. func NewApp() *App { return &App{ - Name: os.Args[0], - Usage: "A new cli application", - Version: "0.0.0", - Action: helpCommand.Action, + Name: os.Args[0], + Usage: "A new cli application", + Version: "0.0.0", + Action: helpCommand.Action, + Compiled: compileTime(), + Author: "Author", + Email: "unknown@email", } } diff --git a/help.go b/help.go index 31e0611..9ac997b 100644 --- a/help.go +++ b/help.go @@ -44,6 +44,32 @@ OPTIONS: {{end}} ` +var ManPageTemplate = `.\" -*-Nroff-*- +.\" +.TH "{{.Name}}" 1 "{{.Compiled.Day}} {{.Compiled.Month}} {{.Compiled.Year}}" "" "" +.SH NAME +{{.Name}} \- {{.Usage}} +.SH SYNOPSIS +.B {{.Name}} +.nf +command {{.Name}} [command options] [arguments...] +.fi +.SH COMMANDS +.nf +{{range .Commands}}{{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}} +{{end}} +.fi +.SH OPTIONS +.nf +{{range .Flags}}{{.}} +{{end}} +.fi +.SH VERSION +.B {{.Version}} +.SH AUTHOR +.B {{.Name}} was written by {{.Author}} <{{.Email}}> +` + var helpCommand = Command{ Name: "help", ShortName: "h", @@ -63,6 +89,11 @@ func ShowAppHelp(c *Context) { printHelp(AppHelpTemplate, c.App) } +// Generates (prints) man page for the App +func GenerateManPage(c *Context) { + printHelp(ManPageTemplate, c.App) +} + // Prints help for the given command func ShowCommandHelp(c *Context, command string) { for _, c := range c.App.Commands { @@ -83,7 +114,10 @@ func ShowVersion(c *Context) { func printHelp(templ string, data interface{}) { w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0) t := template.Must(template.New("help").Parse(templ)) - t.Execute(w, data) + err := t.Execute(w, data) + if err != nil { + panic(err) + } w.Flush() } From fbbda7a90219b8d5abe086fcbec51ea58ad4d1bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20R=C3=B8dseth?= Date: Fri, 15 Nov 2013 12:51:53 +0100 Subject: [PATCH 4/6] Minor changes --- README.md | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 3fb272c..0917bb9 100644 --- a/README.md +++ b/README.md @@ -30,8 +30,10 @@ One of the philosophies behind cli.go is that an API should be playful and full ``` go package main -import "os" -import "github.com/codegangsta/cli" +import ( + "os" + "github.com/codegangsta/cli" +) func main() { cli.NewApp().Run(os.Args) @@ -43,8 +45,10 @@ This app will run and show help text, but is not very useful. Let's give an acti ``` go package main -import "os" -import "github.com/codegangsta/cli" +import ( + "os" + "github.com/codegangsta/cli" +) func main() { app := cli.NewApp() @@ -68,8 +72,10 @@ Being a programmer can be a lonely job. Thankfully by the power of automation th /* greet.go */ package main -import "os" -import "github.com/codegangsta/cli" +import ( + "os" + "github.com/codegangsta/cli" +) func main() { app := cli.NewApp() @@ -158,7 +164,7 @@ app.Commands = []cli.Command{ ShortName: "a", Usage: "add a task to the list", Action: func(c *cli.Context) { - println("added task: ", c.Args()[0]) + println("added task: ", c.FirstArg()) }, }, { @@ -166,7 +172,7 @@ app.Commands = []cli.Command{ ShortName: "c", Usage: "complete a task on the list", Action: func(c *cli.Context) { - println("completed task: ", c.Args()[0]) + println("completed task: ", c.FirstArg()) }, }, } From 1bea6dcbf001db9ecd0276d066de01f1c9018b86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20R=C3=B8dseth?= Date: Sun, 24 Nov 2013 14:40:21 +0100 Subject: [PATCH 5/6] Custom type for arguments --- app.go | 4 ++-- app_test.go | 6 +++--- cli_test.go | 4 ++-- command.go | 9 +++++---- context.go | 36 ++++++++++++++++++++++++++---------- help.go | 4 ++-- 6 files changed, 40 insertions(+), 23 deletions(-) diff --git a/app.go b/app.go index db9e984..b8ea635 100644 --- a/app.go +++ b/app.go @@ -86,8 +86,8 @@ func (a *App) Run(arguments []string) error { } args := context.Args() - if len(args) > 0 { - name := args[0] + if args.Present() { + name := args.First() c := a.Command(name) if c != nil { return c.Run(context) diff --git a/app_test.go b/app_test.go index f3646e9..893b799 100644 --- a/app_test.go +++ b/app_test.go @@ -29,7 +29,7 @@ func TestApp_Run(t *testing.T) { app := cli.NewApp() app.Action = func(c *cli.Context) { - s = s + c.Args()[0] + s = s + c.Args().First() } err := app.Run([]string{"command", "foo"}) @@ -76,7 +76,7 @@ func TestApp_CommandWithArgBeforeFlags(t *testing.T) { }, Action: func(c *cli.Context) { parsedOption = c.String("option") - firstArg = c.Args()[0] + firstArg = c.Args().First() }, } app.Commands = []cli.Command{command} @@ -103,7 +103,7 @@ func TestApp_ParseSliceFlags(t *testing.T) { parsedIntSlice = c.IntSlice("p") parsedStringSlice = c.StringSlice("ip") parsedOption = c.String("option") - firstArg = c.Args()[0] + firstArg = c.Args().First() }, } app.Commands = []cli.Command{command} diff --git a/cli_test.go b/cli_test.go index 8e535ec..772e90f 100644 --- a/cli_test.go +++ b/cli_test.go @@ -15,7 +15,7 @@ func Example() { ShortName: "a", Usage: "add a task to the list", Action: func(c *cli.Context) { - println("added task: ", c.Args()[0]) + println("added task: ", c.Args().First()) }, }, { @@ -23,7 +23,7 @@ func Example() { ShortName: "c", Usage: "complete a task on the list", Action: func(c *cli.Context) { - println("completed task: ", c.Args()[0]) + println("completed task: ", c.Args().First()) }, }, } diff --git a/command.go b/command.go index 79af87e..7ef7b01 100644 --- a/command.go +++ b/command.go @@ -43,11 +43,12 @@ func (c Command) Run(ctx *Context) error { var err error if firstFlagIndex > -1 { - args := ctx.Args()[1:firstFlagIndex] - flags := ctx.Args()[firstFlagIndex:] - err = set.Parse(append(flags, args...)) + args := ctx.Args() + regularArgs := args[1:firstFlagIndex] + flagArgs := args[firstFlagIndex:] + err = set.Parse(append(flagArgs, regularArgs...)) } else { - err = set.Parse(ctx.Args()[1:]) + err = set.Parse(ctx.Args().Tail()) } if err != nil { diff --git a/context.go b/context.go index d944d70..695ebe3 100644 --- a/context.go +++ b/context.go @@ -15,6 +15,8 @@ type Context struct { globalSet *flag.FlagSet } +type Args []string + // Creates a new context. For use in when invoking an App or Command action. func NewContext(app *App, set *flag.FlagSet, globalSet *flag.FlagSet) *Context { return &Context{app, set, globalSet} @@ -71,8 +73,9 @@ func (c *Context) GlobalIntSlice(name string) []int { } // Returns the command line arguments associated with the context. -func (c *Context) Args() []string { - return c.flagSet.Args() +func (c *Context) Args() Args { + args := Args(c.flagSet.Args()) + return args } func (c *Context) lookupInt(name string, set *flag.FlagSet) int { @@ -130,16 +133,29 @@ func (c *Context) lookupBool(name string, set *flag.FlagSet) bool { return false } -// Returns the nth argument, or just a blank string -func (c *Context) GetArg(n int) string { - args := c.Args() - if len(args) < n { - return args[n] +// Returns the nth argument, or else a blank string +func (a Args) Get(n int) string { + if len(a) > n { + return a[n] } return "" } -// Returns the first argument, or just a blank string, for convenience -func (c *Context) FirstArg() string { - return c.GetArg(0) +// Returns the first argument, or else a blank string +func (a Args) First() string { + return a.Get(0) +} + +// Return the rest of the arguments (not the first one) +// or else an empty string slice +func (a Args) Tail() []string { + if len(a) >= 2 { + return []string(a)[1:] + } + return []string{} +} + +// Checks if there are any arguments present +func (a Args) Present() bool { + return len(a) != 0 } diff --git a/help.go b/help.go index 9ac997b..e91497c 100644 --- a/help.go +++ b/help.go @@ -76,8 +76,8 @@ var helpCommand = Command{ Usage: "Shows a list of commands or help for one command", Action: func(c *Context) { args := c.Args() - if len(args) > 0 { - ShowCommandHelp(c, args[0]) + if args.Present() { + ShowCommandHelp(c, args.First()) } else { ShowAppHelp(c) } From 22a56266c5891e52f7377a3f94536db8e8addb15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20R=C3=B8dseth?= Date: Sun, 24 Nov 2013 14:41:42 +0100 Subject: [PATCH 6/6] Removed the function and data structure for generating man pages --- help.go | 31 ------------------------------- 1 file changed, 31 deletions(-) diff --git a/help.go b/help.go index e91497c..41b58af 100644 --- a/help.go +++ b/help.go @@ -44,32 +44,6 @@ OPTIONS: {{end}} ` -var ManPageTemplate = `.\" -*-Nroff-*- -.\" -.TH "{{.Name}}" 1 "{{.Compiled.Day}} {{.Compiled.Month}} {{.Compiled.Year}}" "" "" -.SH NAME -{{.Name}} \- {{.Usage}} -.SH SYNOPSIS -.B {{.Name}} -.nf -command {{.Name}} [command options] [arguments...] -.fi -.SH COMMANDS -.nf -{{range .Commands}}{{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}} -{{end}} -.fi -.SH OPTIONS -.nf -{{range .Flags}}{{.}} -{{end}} -.fi -.SH VERSION -.B {{.Version}} -.SH AUTHOR -.B {{.Name}} was written by {{.Author}} <{{.Email}}> -` - var helpCommand = Command{ Name: "help", ShortName: "h", @@ -89,11 +63,6 @@ func ShowAppHelp(c *Context) { printHelp(AppHelpTemplate, c.App) } -// Generates (prints) man page for the App -func GenerateManPage(c *Context) { - printHelp(ManPageTemplate, c.App) -} - // Prints help for the given command func ShowCommandHelp(c *Context, command string) { for _, c := range c.App.Commands {