git-subtree-dir: vendor/github.com/codegangsta/cli
git-subtree-split: 565493f259
master
| @ -0,0 +1,6 @@ | |||||
| language: go | |||||
| go: 1.1 | |||||
| script: | |||||
| - go vet ./... | |||||
| - go test -v ./... | |||||
| @ -0,0 +1,21 @@ | |||||
| Copyright (C) 2013 Jeremy Saenz | |||||
| All Rights Reserved. | |||||
| MIT LICENSE | |||||
| Permission is hereby granted, free of charge, to any person obtaining a copy of | |||||
| this software and associated documentation files (the "Software"), to deal in | |||||
| the Software without restriction, including without limitation the rights to | |||||
| use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of | |||||
| the Software, and to permit persons to whom the Software is furnished to do so, | |||||
| subject to the following conditions: | |||||
| The above copyright notice and this permission notice shall be included in all | |||||
| copies or substantial portions of the Software. | |||||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS | |||||
| FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR | |||||
| COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER | |||||
| IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |||||
| CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
| @ -0,0 +1,280 @@ | |||||
| [](https://travis-ci.org/codegangsta/cli) | |||||
| # cli.go | |||||
| cli.go is simple, fast, and fun package for building command line apps in Go. The goal is to enable developers to write fast and distributable command line applications in an expressive way. | |||||
| You can view the API docs here: | |||||
| http://godoc.org/github.com/codegangsta/cli | |||||
| ## Overview | |||||
| Command line apps are usually so tiny that there is absolutely no reason why your code should *not* be self-documenting. Things like generating help text and parsing command flags/options should not hinder productivity when writing a command line app. | |||||
| This is where cli.go comes into play. cli.go makes command line programming fun, organized, and expressive! | |||||
| ## Installation | |||||
| Make sure you have a working Go environment (go 1.1 is *required*). [See the install instructions](http://golang.org/doc/install.html). | |||||
| To install cli.go, simply run: | |||||
| ``` | |||||
| $ go get github.com/codegangsta/cli | |||||
| ``` | |||||
| Make sure your PATH includes to the `$GOPATH/bin` directory so your commands can be easily used: | |||||
| ``` | |||||
| export PATH=$PATH:$GOPATH/bin | |||||
| ``` | |||||
| ## Getting Started | |||||
| One of the philosophies behind cli.go is that an API should be playful and full of discovery. So a cli.go app can be as little as one line of code in `main()`. | |||||
| ``` go | |||||
| package main | |||||
| import ( | |||||
| "os" | |||||
| "github.com/codegangsta/cli" | |||||
| ) | |||||
| func main() { | |||||
| cli.NewApp().Run(os.Args) | |||||
| } | |||||
| ``` | |||||
| This app will run and show help text, but is not very useful. Let's give an action to execute and some help documentation: | |||||
| ``` go | |||||
| package main | |||||
| import ( | |||||
| "os" | |||||
| "github.com/codegangsta/cli" | |||||
| ) | |||||
| func main() { | |||||
| app := cli.NewApp() | |||||
| app.Name = "boom" | |||||
| app.Usage = "make an explosive entrance" | |||||
| app.Action = func(c *cli.Context) { | |||||
| println("boom! I say!") | |||||
| } | |||||
| app.Run(os.Args) | |||||
| } | |||||
| ``` | |||||
| Running this already gives you a ton of functionality, plus support for things like subcommands and flags, which are covered below. | |||||
| ## Example | |||||
| Being a programmer can be a lonely job. Thankfully by the power of automation that is not the case! Let's create a greeter app to fend off our demons of loneliness! | |||||
| ``` go | |||||
| /* greet.go */ | |||||
| package main | |||||
| import ( | |||||
| "os" | |||||
| "github.com/codegangsta/cli" | |||||
| ) | |||||
| func main() { | |||||
| app := cli.NewApp() | |||||
| app.Name = "greet" | |||||
| app.Usage = "fight the loneliness!" | |||||
| app.Action = func(c *cli.Context) { | |||||
| println("Hello friend!") | |||||
| } | |||||
| app.Run(os.Args) | |||||
| } | |||||
| ``` | |||||
| Install our command to the `$GOPATH/bin` directory: | |||||
| ``` | |||||
| $ go install | |||||
| ``` | |||||
| Finally run our new command: | |||||
| ``` | |||||
| $ greet | |||||
| Hello friend! | |||||
| ``` | |||||
| cli.go also generates some bitchass help text: | |||||
| ``` | |||||
| $ greet help | |||||
| NAME: | |||||
| greet - fight the loneliness! | |||||
| USAGE: | |||||
| greet [global options] command [command options] [arguments...] | |||||
| VERSION: | |||||
| 0.0.0 | |||||
| COMMANDS: | |||||
| help, h Shows a list of commands or help for one command | |||||
| GLOBAL OPTIONS | |||||
| --version Shows version information | |||||
| ``` | |||||
| ### Arguments | |||||
| You can lookup arguments by calling the `Args` function on cli.Context. | |||||
| ``` go | |||||
| ... | |||||
| app.Action = func(c *cli.Context) { | |||||
| println("Hello", c.Args()[0]) | |||||
| } | |||||
| ... | |||||
| ``` | |||||
| ### Flags | |||||
| Setting and querying flags is simple. | |||||
| ``` go | |||||
| ... | |||||
| app.Flags = []cli.Flag { | |||||
| cli.StringFlag{ | |||||
| Name: "lang", | |||||
| Value: "english", | |||||
| Usage: "language for the greeting", | |||||
| }, | |||||
| } | |||||
| app.Action = func(c *cli.Context) { | |||||
| name := "someone" | |||||
| if len(c.Args()) > 0 { | |||||
| name = c.Args()[0] | |||||
| } | |||||
| if c.String("lang") == "spanish" { | |||||
| println("Hola", name) | |||||
| } else { | |||||
| println("Hello", name) | |||||
| } | |||||
| } | |||||
| ... | |||||
| ``` | |||||
| #### Alternate Names | |||||
| You can set alternate (or short) names for flags by providing a comma-delimited list for the Name. e.g. | |||||
| ``` go | |||||
| app.Flags = []cli.Flag { | |||||
| cli.StringFlag{ | |||||
| Name: "lang, l", | |||||
| Value: "english", | |||||
| Usage: "language for the greeting", | |||||
| }, | |||||
| } | |||||
| ``` | |||||
| #### Values from the Environment | |||||
| You can also have the default value set from the environment via EnvVar. e.g. | |||||
| ``` go | |||||
| app.Flags = []cli.Flag { | |||||
| cli.StringFlag{ | |||||
| Name: "lang, l", | |||||
| Value: "english", | |||||
| Usage: "language for the greeting", | |||||
| EnvVar: "APP_LANG", | |||||
| }, | |||||
| } | |||||
| ``` | |||||
| That flag can then be set with `--lang spanish` or `-l spanish`. Note that giving two different forms of the same flag in the same command invocation is an error. | |||||
| ### Subcommands | |||||
| Subcommands can be defined for a more git-like command line app. | |||||
| ```go | |||||
| ... | |||||
| app.Commands = []cli.Command{ | |||||
| { | |||||
| Name: "add", | |||||
| ShortName: "a", | |||||
| Usage: "add a task to the list", | |||||
| Action: func(c *cli.Context) { | |||||
| println("added task: ", c.Args().First()) | |||||
| }, | |||||
| }, | |||||
| { | |||||
| Name: "complete", | |||||
| ShortName: "c", | |||||
| Usage: "complete a task on the list", | |||||
| Action: func(c *cli.Context) { | |||||
| println("completed task: ", c.Args().First()) | |||||
| }, | |||||
| }, | |||||
| { | |||||
| Name: "template", | |||||
| ShortName: "r", | |||||
| Usage: "options for task templates", | |||||
| Subcommands: []cli.Command{ | |||||
| { | |||||
| Name: "add", | |||||
| Usage: "add a new template", | |||||
| Action: func(c *cli.Context) { | |||||
| println("new task template: ", c.Args().First()) | |||||
| }, | |||||
| }, | |||||
| { | |||||
| Name: "remove", | |||||
| Usage: "remove an existing template", | |||||
| Action: func(c *cli.Context) { | |||||
| println("removed task template: ", c.Args().First()) | |||||
| }, | |||||
| }, | |||||
| }, | |||||
| }, | |||||
| } | |||||
| ... | |||||
| ``` | |||||
| ### Bash Completion | |||||
| You can enable completion commands by setting the EnableBashCompletion | |||||
| flag on the App object. By default, this setting will only auto-complete to | |||||
| show an app's subcommands, but you can write your own completion methods for | |||||
| the App or its subcommands. | |||||
| ```go | |||||
| ... | |||||
| var tasks = []string{"cook", "clean", "laundry", "eat", "sleep", "code"} | |||||
| app := cli.NewApp() | |||||
| app.EnableBashCompletion = true | |||||
| app.Commands = []cli.Command{ | |||||
| { | |||||
| Name: "complete", | |||||
| ShortName: "c", | |||||
| Usage: "complete a task on the list", | |||||
| Action: func(c *cli.Context) { | |||||
| println("completed task: ", c.Args().First()) | |||||
| }, | |||||
| BashComplete: func(c *cli.Context) { | |||||
| // This will complete if no args are passed | |||||
| if len(c.Args()) > 0 { | |||||
| return | |||||
| } | |||||
| for _, t := range tasks { | |||||
| println(t) | |||||
| } | |||||
| }, | |||||
| } | |||||
| } | |||||
| ... | |||||
| ``` | |||||
| #### To Enable | |||||
| Source the autocomplete/bash_autocomplete file in your .bashrc file while | |||||
| setting the PROG variable to the name of your program: | |||||
| `PROG=myprogram source /.../cli/autocomplete/bash_autocomplete` | |||||
| ## About | |||||
| cli.go is written by none other than the [Code Gangsta](http://codegangsta.io) | |||||
| @ -0,0 +1,248 @@ | |||||
| package cli | |||||
| import ( | |||||
| "fmt" | |||||
| "io/ioutil" | |||||
| "os" | |||||
| "time" | |||||
| ) | |||||
| // App is the main structure of a cli application. It is recomended that | |||||
| // and app be created with the cli.NewApp() function | |||||
| 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 | |||||
| // List of flags to parse | |||||
| Flags []Flag | |||||
| // Boolean to enable bash completion commands | |||||
| EnableBashCompletion bool | |||||
| // Boolean to hide built-in help command | |||||
| HideHelp bool | |||||
| // An action to execute when the bash-completion flag is set | |||||
| BashComplete func(context *Context) | |||||
| // An action to execute before any subcommands are run, but after the context is ready | |||||
| // If a non-nil error is returned, no subcommands are run | |||||
| Before func(context *Context) error | |||||
| // The action to execute when no subcommands are specified | |||||
| Action func(context *Context) | |||||
| // Execute this function if the proper command cannot be found | |||||
| CommandNotFound func(context *Context, command string) | |||||
| // 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", | |||||
| BashComplete: DefaultAppComplete, | |||||
| Action: helpCommand.Action, | |||||
| Compiled: compileTime(), | |||||
| Author: "Author", | |||||
| Email: "unknown@email", | |||||
| } | |||||
| } | |||||
| // Entry point to the cli app. Parses the arguments slice and routes to the proper flag/args combination | |||||
| func (a *App) Run(arguments []string) error { | |||||
| // append help to commands | |||||
| if a.Command(helpCommand.Name) == nil && !a.HideHelp { | |||||
| a.Commands = append(a.Commands, helpCommand) | |||||
| a.appendFlag(HelpFlag) | |||||
| } | |||||
| //append version/help flags | |||||
| if a.EnableBashCompletion { | |||||
| a.appendFlag(BashCompletionFlag) | |||||
| } | |||||
| a.appendFlag(VersionFlag) | |||||
| // parse flags | |||||
| set := flagSet(a.Name, a.Flags) | |||||
| set.SetOutput(ioutil.Discard) | |||||
| err := set.Parse(arguments[1:]) | |||||
| nerr := normalizeFlags(a.Flags, set) | |||||
| if nerr != nil { | |||||
| fmt.Println(nerr) | |||||
| context := NewContext(a, set, set) | |||||
| ShowAppHelp(context) | |||||
| fmt.Println("") | |||||
| return nerr | |||||
| } | |||||
| context := NewContext(a, set, set) | |||||
| if err != nil { | |||||
| fmt.Printf("Incorrect Usage.\n\n") | |||||
| ShowAppHelp(context) | |||||
| fmt.Println("") | |||||
| return err | |||||
| } | |||||
| if checkCompletions(context) { | |||||
| return nil | |||||
| } | |||||
| if checkHelp(context) { | |||||
| return nil | |||||
| } | |||||
| if checkVersion(context) { | |||||
| return nil | |||||
| } | |||||
| if a.Before != nil { | |||||
| err := a.Before(context) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| } | |||||
| args := context.Args() | |||||
| if args.Present() { | |||||
| name := args.First() | |||||
| c := a.Command(name) | |||||
| if c != nil { | |||||
| return c.Run(context) | |||||
| } | |||||
| } | |||||
| // Run default Action | |||||
| a.Action(context) | |||||
| return nil | |||||
| } | |||||
| // Another entry point to the cli app, takes care of passing arguments and error handling | |||||
| func (a *App) RunAndExitOnError() { | |||||
| if err := a.Run(os.Args); err != nil { | |||||
| os.Stderr.WriteString(fmt.Sprintln(err)) | |||||
| os.Exit(1) | |||||
| } | |||||
| } | |||||
| // Invokes the subcommand given the context, parses ctx.Args() to generate command-specific flags | |||||
| func (a *App) RunAsSubcommand(ctx *Context) error { | |||||
| // append help to commands | |||||
| if len(a.Commands) > 0 { | |||||
| if a.Command(helpCommand.Name) == nil && !a.HideHelp { | |||||
| a.Commands = append(a.Commands, helpCommand) | |||||
| a.appendFlag(HelpFlag) | |||||
| } | |||||
| } | |||||
| // append flags | |||||
| if a.EnableBashCompletion { | |||||
| a.appendFlag(BashCompletionFlag) | |||||
| } | |||||
| // parse flags | |||||
| set := flagSet(a.Name, a.Flags) | |||||
| set.SetOutput(ioutil.Discard) | |||||
| err := set.Parse(ctx.Args().Tail()) | |||||
| nerr := normalizeFlags(a.Flags, set) | |||||
| context := NewContext(a, set, ctx.globalSet) | |||||
| if nerr != nil { | |||||
| fmt.Println(nerr) | |||||
| if len(a.Commands) > 0 { | |||||
| ShowSubcommandHelp(context) | |||||
| } else { | |||||
| ShowCommandHelp(ctx, context.Args().First()) | |||||
| } | |||||
| fmt.Println("") | |||||
| return nerr | |||||
| } | |||||
| if err != nil { | |||||
| fmt.Printf("Incorrect Usage.\n\n") | |||||
| ShowSubcommandHelp(context) | |||||
| return err | |||||
| } | |||||
| if checkCompletions(context) { | |||||
| return nil | |||||
| } | |||||
| if len(a.Commands) > 0 { | |||||
| if checkSubcommandHelp(context) { | |||||
| return nil | |||||
| } | |||||
| } else { | |||||
| if checkCommandHelp(ctx, context.Args().First()) { | |||||
| return nil | |||||
| } | |||||
| } | |||||
| if a.Before != nil { | |||||
| err := a.Before(context) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| } | |||||
| args := context.Args() | |||||
| if args.Present() { | |||||
| name := args.First() | |||||
| c := a.Command(name) | |||||
| if c != nil { | |||||
| return c.Run(context) | |||||
| } | |||||
| } | |||||
| // Run default Action | |||||
| if len(a.Commands) > 0 { | |||||
| a.Action(context) | |||||
| } else { | |||||
| a.Action(ctx) | |||||
| } | |||||
| return nil | |||||
| } | |||||
| // Returns the named command on App. Returns nil if the command does not exist | |||||
| func (a *App) Command(name string) *Command { | |||||
| for _, c := range a.Commands { | |||||
| if c.HasName(name) { | |||||
| return &c | |||||
| } | |||||
| } | |||||
| return nil | |||||
| } | |||||
| func (a *App) hasFlag(flag Flag) bool { | |||||
| for _, f := range a.Flags { | |||||
| if flag == f { | |||||
| return true | |||||
| } | |||||
| } | |||||
| return false | |||||
| } | |||||
| func (a *App) appendFlag(flag Flag) { | |||||
| if !a.hasFlag(flag) { | |||||
| a.Flags = append(a.Flags, flag) | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,403 @@ | |||||
| package cli_test | |||||
| import ( | |||||
| "fmt" | |||||
| "os" | |||||
| "testing" | |||||
| "github.com/codegangsta/cli" | |||||
| ) | |||||
| func ExampleApp() { | |||||
| // set args for examples sake | |||||
| os.Args = []string{"greet", "--name", "Jeremy"} | |||||
| app := cli.NewApp() | |||||
| app.Name = "greet" | |||||
| app.Flags = []cli.Flag{ | |||||
| 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")) | |||||
| } | |||||
| app.Run(os.Args) | |||||
| // Output: | |||||
| // Hello Jeremy | |||||
| } | |||||
| func ExampleAppSubcommand() { | |||||
| // set args for examples sake | |||||
| os.Args = []string{"say", "hi", "english", "--name", "Jeremy"} | |||||
| app := cli.NewApp() | |||||
| app.Name = "say" | |||||
| app.Commands = []cli.Command{ | |||||
| { | |||||
| Name: "hello", | |||||
| ShortName: "hi", | |||||
| Usage: "use it to see a description", | |||||
| Description: "This is how we describe hello the function", | |||||
| Subcommands: []cli.Command{ | |||||
| { | |||||
| Name: "english", | |||||
| ShortName: "en", | |||||
| Usage: "sends a greeting in english", | |||||
| Description: "greets someone in english", | |||||
| Flags: []cli.Flag{ | |||||
| cli.StringFlag{ | |||||
| Name: "name", | |||||
| Value: "Bob", | |||||
| Usage: "Name of the person to greet", | |||||
| }, | |||||
| }, | |||||
| Action: func(c *cli.Context) { | |||||
| fmt.Println("Hello,", c.String("name")) | |||||
| }, | |||||
| }, | |||||
| }, | |||||
| }, | |||||
| } | |||||
| app.Run(os.Args) | |||||
| // Output: | |||||
| // Hello, Jeremy | |||||
| } | |||||
| func ExampleAppHelp() { | |||||
| // set args for examples sake | |||||
| os.Args = []string{"greet", "h", "describeit"} | |||||
| app := cli.NewApp() | |||||
| app.Name = "greet" | |||||
| app.Flags = []cli.Flag{ | |||||
| cli.StringFlag{Name: "name", Value: "bob", Usage: "a name to say"}, | |||||
| } | |||||
| app.Commands = []cli.Command{ | |||||
| { | |||||
| Name: "describeit", | |||||
| ShortName: "d", | |||||
| Usage: "use it to see a description", | |||||
| Description: "This is how we describe describeit the function", | |||||
| Action: func(c *cli.Context) { | |||||
| fmt.Printf("i like to describe things") | |||||
| }, | |||||
| }, | |||||
| } | |||||
| app.Run(os.Args) | |||||
| // Output: | |||||
| // NAME: | |||||
| // describeit - use it to see a description | |||||
| // | |||||
| // USAGE: | |||||
| // command describeit [arguments...] | |||||
| // | |||||
| // DESCRIPTION: | |||||
| // This is how we describe describeit the function | |||||
| } | |||||
| func ExampleAppBashComplete() { | |||||
| // set args for examples sake | |||||
| os.Args = []string{"greet", "--generate-bash-completion"} | |||||
| app := cli.NewApp() | |||||
| app.Name = "greet" | |||||
| app.EnableBashCompletion = true | |||||
| app.Commands = []cli.Command{ | |||||
| { | |||||
| Name: "describeit", | |||||
| ShortName: "d", | |||||
| Usage: "use it to see a description", | |||||
| Description: "This is how we describe describeit the function", | |||||
| Action: func(c *cli.Context) { | |||||
| fmt.Printf("i like to describe things") | |||||
| }, | |||||
| }, { | |||||
| Name: "next", | |||||
| Usage: "next example", | |||||
| Description: "more stuff to see when generating bash completion", | |||||
| Action: func(c *cli.Context) { | |||||
| fmt.Printf("the next example") | |||||
| }, | |||||
| }, | |||||
| } | |||||
| app.Run(os.Args) | |||||
| // Output: | |||||
| // describeit | |||||
| // d | |||||
| // next | |||||
| // help | |||||
| // h | |||||
| } | |||||
| func TestApp_Run(t *testing.T) { | |||||
| s := "" | |||||
| app := cli.NewApp() | |||||
| app.Action = func(c *cli.Context) { | |||||
| s = s + c.Args().First() | |||||
| } | |||||
| err := app.Run([]string{"command", "foo"}) | |||||
| expect(t, err, nil) | |||||
| err = app.Run([]string{"command", "bar"}) | |||||
| expect(t, err, nil) | |||||
| expect(t, s, "foobar") | |||||
| } | |||||
| var commandAppTests = []struct { | |||||
| name string | |||||
| expected bool | |||||
| }{ | |||||
| {"foobar", true}, | |||||
| {"batbaz", true}, | |||||
| {"b", true}, | |||||
| {"f", true}, | |||||
| {"bat", false}, | |||||
| {"nothing", false}, | |||||
| } | |||||
| func TestApp_Command(t *testing.T) { | |||||
| app := cli.NewApp() | |||||
| fooCommand := cli.Command{Name: "foobar", ShortName: "f"} | |||||
| batCommand := cli.Command{Name: "batbaz", ShortName: "b"} | |||||
| app.Commands = []cli.Command{ | |||||
| fooCommand, | |||||
| batCommand, | |||||
| } | |||||
| for _, test := range commandAppTests { | |||||
| expect(t, app.Command(test.name) != nil, test.expected) | |||||
| } | |||||
| } | |||||
| func TestApp_CommandWithArgBeforeFlags(t *testing.T) { | |||||
| var parsedOption, firstArg string | |||||
| app := cli.NewApp() | |||||
| command := cli.Command{ | |||||
| Name: "cmd", | |||||
| Flags: []cli.Flag{ | |||||
| cli.StringFlag{Name: "option", Value: "", Usage: "some option"}, | |||||
| }, | |||||
| Action: func(c *cli.Context) { | |||||
| parsedOption = c.String("option") | |||||
| firstArg = c.Args().First() | |||||
| }, | |||||
| } | |||||
| app.Commands = []cli.Command{command} | |||||
| app.Run([]string{"", "cmd", "my-arg", "--option", "my-option"}) | |||||
| expect(t, parsedOption, "my-option") | |||||
| expect(t, firstArg, "my-arg") | |||||
| } | |||||
| func TestApp_Float64Flag(t *testing.T) { | |||||
| var meters float64 | |||||
| app := cli.NewApp() | |||||
| app.Flags = []cli.Flag{ | |||||
| cli.Float64Flag{Name: "height", Value: 1.5, Usage: "Set the height, in meters"}, | |||||
| } | |||||
| app.Action = func(c *cli.Context) { | |||||
| meters = c.Float64("height") | |||||
| } | |||||
| app.Run([]string{"", "--height", "1.93"}) | |||||
| expect(t, meters, 1.93) | |||||
| } | |||||
| 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{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") | |||||
| parsedStringSlice = c.StringSlice("ip") | |||||
| parsedOption = c.String("option") | |||||
| firstArg = c.Args().First() | |||||
| }, | |||||
| } | |||||
| 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("%v does not match %v", parsedIntSlice, expectedIntSlice) | |||||
| } | |||||
| if !StrsEquals(parsedStringSlice, expectedStringSlice) { | |||||
| t.Errorf("%v does not match %v", parsedStringSlice, expectedStringSlice) | |||||
| } | |||||
| } | |||||
| func TestApp_BeforeFunc(t *testing.T) { | |||||
| beforeRun, subcommandRun := false, false | |||||
| beforeError := fmt.Errorf("fail") | |||||
| var err error | |||||
| app := cli.NewApp() | |||||
| app.Before = func(c *cli.Context) error { | |||||
| beforeRun = true | |||||
| s := c.String("opt") | |||||
| if s == "fail" { | |||||
| return beforeError | |||||
| } | |||||
| return nil | |||||
| } | |||||
| app.Commands = []cli.Command{ | |||||
| cli.Command{ | |||||
| Name: "sub", | |||||
| Action: func(c *cli.Context) { | |||||
| subcommandRun = true | |||||
| }, | |||||
| }, | |||||
| } | |||||
| app.Flags = []cli.Flag{ | |||||
| cli.StringFlag{Name: "opt"}, | |||||
| } | |||||
| // run with the Before() func succeeding | |||||
| err = app.Run([]string{"command", "--opt", "succeed", "sub"}) | |||||
| if err != nil { | |||||
| t.Fatalf("Run error: %s", err) | |||||
| } | |||||
| if beforeRun == false { | |||||
| t.Errorf("Before() not executed when expected") | |||||
| } | |||||
| if subcommandRun == false { | |||||
| t.Errorf("Subcommand not executed when expected") | |||||
| } | |||||
| // reset | |||||
| beforeRun, subcommandRun = false, false | |||||
| // run with the Before() func failing | |||||
| err = app.Run([]string{"command", "--opt", "fail", "sub"}) | |||||
| // should be the same error produced by the Before func | |||||
| if err != beforeError { | |||||
| t.Errorf("Run error expected, but not received") | |||||
| } | |||||
| if beforeRun == false { | |||||
| t.Errorf("Before() not executed when expected") | |||||
| } | |||||
| if subcommandRun == true { | |||||
| t.Errorf("Subcommand executed when NOT expected") | |||||
| } | |||||
| } | |||||
| func TestAppHelpPrinter(t *testing.T) { | |||||
| oldPrinter := cli.HelpPrinter | |||||
| defer func() { | |||||
| cli.HelpPrinter = oldPrinter | |||||
| }() | |||||
| var wasCalled = false | |||||
| cli.HelpPrinter = func(template string, data interface{}) { | |||||
| wasCalled = true | |||||
| } | |||||
| app := cli.NewApp() | |||||
| app.Run([]string{"-h"}) | |||||
| if wasCalled == false { | |||||
| t.Errorf("Help printer expected to be called, but was not") | |||||
| } | |||||
| } | |||||
| func TestAppCommandNotFound(t *testing.T) { | |||||
| beforeRun, subcommandRun := false, false | |||||
| app := cli.NewApp() | |||||
| app.CommandNotFound = func(c *cli.Context, command string) { | |||||
| beforeRun = true | |||||
| } | |||||
| app.Commands = []cli.Command{ | |||||
| cli.Command{ | |||||
| Name: "bar", | |||||
| Action: func(c *cli.Context) { | |||||
| subcommandRun = true | |||||
| }, | |||||
| }, | |||||
| } | |||||
| app.Run([]string{"command", "foo"}) | |||||
| expect(t, beforeRun, true) | |||||
| expect(t, subcommandRun, false) | |||||
| } | |||||
| func TestGlobalFlagsInSubcommands(t *testing.T) { | |||||
| subcommandRun := false | |||||
| app := cli.NewApp() | |||||
| app.Flags = []cli.Flag{ | |||||
| cli.BoolFlag{Name: "debug, d", Usage: "Enable debugging"}, | |||||
| } | |||||
| app.Commands = []cli.Command{ | |||||
| cli.Command{ | |||||
| Name: "foo", | |||||
| Subcommands: []cli.Command{ | |||||
| { | |||||
| Name: "bar", | |||||
| Action: func(c *cli.Context) { | |||||
| if c.GlobalBool("debug") { | |||||
| subcommandRun = true | |||||
| } | |||||
| }, | |||||
| }, | |||||
| }, | |||||
| }, | |||||
| } | |||||
| app.Run([]string{"command", "-d", "foo", "bar"}) | |||||
| expect(t, subcommandRun, true) | |||||
| } | |||||
| @ -0,0 +1,13 @@ | |||||
| #! /bin/bash | |||||
| _cli_bash_autocomplete() { | |||||
| local cur prev opts base | |||||
| COMPREPLY=() | |||||
| cur="${COMP_WORDS[COMP_CWORD]}" | |||||
| prev="${COMP_WORDS[COMP_CWORD-1]}" | |||||
| opts=$( ${COMP_WORDS[@]:0:COMP_CWORD} --generate-bash-completion ) | |||||
| COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) | |||||
| return 0 | |||||
| } | |||||
| complete -F _cli_bash_autocomplete $PROG | |||||
| @ -0,0 +1,19 @@ | |||||
| // Package cli provides a minimal framework for creating and organizing command line | |||||
| // Go applications. cli is designed to be easy to understand and write, the most simple | |||||
| // cli application can be written as follows: | |||||
| // func main() { | |||||
| // cli.NewApp().Run(os.Args) | |||||
| // } | |||||
| // | |||||
| // Of course this application does not do much, so let's make this an actual application: | |||||
| // func main() { | |||||
| // app := cli.NewApp() | |||||
| // app.Name = "greet" | |||||
| // app.Usage = "say a greeting" | |||||
| // app.Action = func(c *cli.Context) { | |||||
| // println("Greetings") | |||||
| // } | |||||
| // | |||||
| // app.Run(os.Args) | |||||
| // } | |||||
| package cli | |||||
| @ -0,0 +1,100 @@ | |||||
| package cli_test | |||||
| import ( | |||||
| "os" | |||||
| "github.com/codegangsta/cli" | |||||
| ) | |||||
| func Example() { | |||||
| app := cli.NewApp() | |||||
| app.Name = "todo" | |||||
| app.Usage = "task list on the command line" | |||||
| app.Commands = []cli.Command{ | |||||
| { | |||||
| Name: "add", | |||||
| ShortName: "a", | |||||
| Usage: "add a task to the list", | |||||
| Action: func(c *cli.Context) { | |||||
| println("added task: ", c.Args().First()) | |||||
| }, | |||||
| }, | |||||
| { | |||||
| Name: "complete", | |||||
| ShortName: "c", | |||||
| Usage: "complete a task on the list", | |||||
| Action: func(c *cli.Context) { | |||||
| println("completed task: ", c.Args().First()) | |||||
| }, | |||||
| }, | |||||
| } | |||||
| app.Run(os.Args) | |||||
| } | |||||
| func ExampleSubcommand() { | |||||
| app := cli.NewApp() | |||||
| app.Name = "say" | |||||
| app.Commands = []cli.Command{ | |||||
| { | |||||
| Name: "hello", | |||||
| ShortName: "hi", | |||||
| Usage: "use it to see a description", | |||||
| Description: "This is how we describe hello the function", | |||||
| Subcommands: []cli.Command{ | |||||
| { | |||||
| Name: "english", | |||||
| ShortName: "en", | |||||
| Usage: "sends a greeting in english", | |||||
| Description: "greets someone in english", | |||||
| Flags: []cli.Flag{ | |||||
| cli.StringFlag{ | |||||
| Name: "name", | |||||
| Value: "Bob", | |||||
| Usage: "Name of the person to greet", | |||||
| }, | |||||
| }, | |||||
| Action: func(c *cli.Context) { | |||||
| println("Hello, ", c.String("name")) | |||||
| }, | |||||
| }, { | |||||
| Name: "spanish", | |||||
| ShortName: "sp", | |||||
| Usage: "sends a greeting in spanish", | |||||
| Flags: []cli.Flag{ | |||||
| cli.StringFlag{ | |||||
| Name: "surname", | |||||
| Value: "Jones", | |||||
| Usage: "Surname of the person to greet", | |||||
| }, | |||||
| }, | |||||
| Action: func(c *cli.Context) { | |||||
| println("Hola, ", c.String("surname")) | |||||
| }, | |||||
| }, { | |||||
| Name: "french", | |||||
| ShortName: "fr", | |||||
| Usage: "sends a greeting in french", | |||||
| Flags: []cli.Flag{ | |||||
| cli.StringFlag{ | |||||
| Name: "nickname", | |||||
| Value: "Stevie", | |||||
| Usage: "Nickname of the person to greet", | |||||
| }, | |||||
| }, | |||||
| Action: func(c *cli.Context) { | |||||
| println("Bonjour, ", c.String("nickname")) | |||||
| }, | |||||
| }, | |||||
| }, | |||||
| }, { | |||||
| Name: "bye", | |||||
| Usage: "says goodbye", | |||||
| Action: func(c *cli.Context) { | |||||
| println("bye") | |||||
| }, | |||||
| }, | |||||
| } | |||||
| app.Run(os.Args) | |||||
| } | |||||
| @ -0,0 +1,141 @@ | |||||
| package cli | |||||
| import ( | |||||
| "fmt" | |||||
| "io/ioutil" | |||||
| "strings" | |||||
| ) | |||||
| // Command is a subcommand for a cli.App. | |||||
| type Command struct { | |||||
| // The name of the command | |||||
| Name string | |||||
| // short name of the command. Typically one character | |||||
| ShortName string | |||||
| // A short description of the usage of this command | |||||
| Usage string | |||||
| // A longer explanation of how the command works | |||||
| Description string | |||||
| // The function to call when checking for bash command completions | |||||
| BashComplete func(context *Context) | |||||
| // An action to execute before any sub-subcommands are run, but after the context is ready | |||||
| // If a non-nil error is returned, no sub-subcommands are run | |||||
| Before func(context *Context) error | |||||
| // The function to call when this command is invoked | |||||
| Action func(context *Context) | |||||
| // List of child commands | |||||
| Subcommands []Command | |||||
| // List of flags to parse | |||||
| Flags []Flag | |||||
| // Treat all flags as normal arguments if true | |||||
| SkipFlagParsing bool | |||||
| // Boolean to hide built-in help command | |||||
| HideHelp bool | |||||
| } | |||||
| // Invokes the command given the context, parses ctx.Args() to generate command-specific flags | |||||
| func (c Command) Run(ctx *Context) error { | |||||
| if len(c.Subcommands) > 0 || c.Before != nil { | |||||
| return c.startApp(ctx) | |||||
| } | |||||
| if !c.HideHelp { | |||||
| // append help to flags | |||||
| c.Flags = append( | |||||
| c.Flags, | |||||
| HelpFlag, | |||||
| ) | |||||
| } | |||||
| if ctx.App.EnableBashCompletion { | |||||
| c.Flags = append(c.Flags, BashCompletionFlag) | |||||
| } | |||||
| set := flagSet(c.Name, c.Flags) | |||||
| set.SetOutput(ioutil.Discard) | |||||
| firstFlagIndex := -1 | |||||
| for index, arg := range ctx.Args() { | |||||
| if strings.HasPrefix(arg, "-") { | |||||
| firstFlagIndex = index | |||||
| break | |||||
| } | |||||
| } | |||||
| var err error | |||||
| if firstFlagIndex > -1 && !c.SkipFlagParsing { | |||||
| args := ctx.Args() | |||||
| regularArgs := args[1:firstFlagIndex] | |||||
| flagArgs := args[firstFlagIndex:] | |||||
| err = set.Parse(append(flagArgs, regularArgs...)) | |||||
| } else { | |||||
| err = set.Parse(ctx.Args().Tail()) | |||||
| } | |||||
| if err != nil { | |||||
| fmt.Printf("Incorrect Usage.\n\n") | |||||
| ShowCommandHelp(ctx, c.Name) | |||||
| fmt.Println("") | |||||
| return err | |||||
| } | |||||
| nerr := normalizeFlags(c.Flags, set) | |||||
| if nerr != nil { | |||||
| fmt.Println(nerr) | |||||
| fmt.Println("") | |||||
| ShowCommandHelp(ctx, c.Name) | |||||
| fmt.Println("") | |||||
| return nerr | |||||
| } | |||||
| context := NewContext(ctx.App, set, ctx.globalSet) | |||||
| if checkCommandCompletions(context, c.Name) { | |||||
| return nil | |||||
| } | |||||
| if checkCommandHelp(context, c.Name) { | |||||
| return nil | |||||
| } | |||||
| context.Command = c | |||||
| c.Action(context) | |||||
| return nil | |||||
| } | |||||
| // Returns true if Command.Name or Command.ShortName matches given name | |||||
| func (c Command) HasName(name string) bool { | |||||
| return c.Name == name || c.ShortName == name | |||||
| } | |||||
| func (c Command) startApp(ctx *Context) error { | |||||
| app := NewApp() | |||||
| // set the name and usage | |||||
| app.Name = fmt.Sprintf("%s %s", ctx.App.Name, c.Name) | |||||
| if c.Description != "" { | |||||
| app.Usage = c.Description | |||||
| } else { | |||||
| app.Usage = c.Usage | |||||
| } | |||||
| // set the flags and commands | |||||
| app.Commands = c.Subcommands | |||||
| app.Flags = c.Flags | |||||
| app.HideHelp = c.HideHelp | |||||
| // bash completion | |||||
| app.EnableBashCompletion = ctx.App.EnableBashCompletion | |||||
| if c.BashComplete != nil { | |||||
| app.BashComplete = c.BashComplete | |||||
| } | |||||
| // set the actions | |||||
| app.Before = c.Before | |||||
| if c.Action != nil { | |||||
| app.Action = c.Action | |||||
| } else { | |||||
| app.Action = helpSubcommand.Action | |||||
| } | |||||
| return app.RunAsSubcommand(ctx) | |||||
| } | |||||
| @ -0,0 +1,48 @@ | |||||
| package cli_test | |||||
| import ( | |||||
| "flag" | |||||
| "github.com/codegangsta/cli" | |||||
| "testing" | |||||
| ) | |||||
| func TestCommandDoNotIgnoreFlags(t *testing.T) { | |||||
| app := cli.NewApp() | |||||
| set := flag.NewFlagSet("test", 0) | |||||
| test := []string{"blah", "blah", "-break"} | |||||
| set.Parse(test) | |||||
| c := cli.NewContext(app, set, set) | |||||
| command := cli.Command { | |||||
| Name: "test-cmd", | |||||
| ShortName: "tc", | |||||
| Usage: "this is for testing", | |||||
| Description: "testing", | |||||
| Action: func(_ *cli.Context) { }, | |||||
| } | |||||
| err := command.Run(c) | |||||
| expect(t, err.Error(), "flag provided but not defined: -break") | |||||
| } | |||||
| func TestCommandIgnoreFlags(t *testing.T) { | |||||
| app := cli.NewApp() | |||||
| set := flag.NewFlagSet("test", 0) | |||||
| test := []string{"blah", "blah"} | |||||
| set.Parse(test) | |||||
| c := cli.NewContext(app, set, set) | |||||
| command := cli.Command { | |||||
| Name: "test-cmd", | |||||
| ShortName: "tc", | |||||
| Usage: "this is for testing", | |||||
| Description: "testing", | |||||
| Action: func(_ *cli.Context) { }, | |||||
| SkipFlagParsing: true, | |||||
| } | |||||
| err := command.Run(c) | |||||
| expect(t, err, nil) | |||||
| } | |||||
| @ -0,0 +1,280 @@ | |||||
| package cli | |||||
| import ( | |||||
| "errors" | |||||
| "flag" | |||||
| "strconv" | |||||
| "strings" | |||||
| ) | |||||
| // 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 { | |||||
| App *App | |||||
| Command Command | |||||
| flagSet *flag.FlagSet | |||||
| globalSet *flag.FlagSet | |||||
| setFlags map[string]bool | |||||
| } | |||||
| // 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: app, flagSet: set, globalSet: globalSet} | |||||
| } | |||||
| // Looks up the value of a local int flag, returns 0 if no int flag exists | |||||
| func (c *Context) Int(name string) int { | |||||
| return lookupInt(name, c.flagSet) | |||||
| } | |||||
| // Looks up the value of a local float64 flag, returns 0 if no float64 flag exists | |||||
| func (c *Context) Float64(name string) float64 { | |||||
| return lookupFloat64(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 lookupBool(name, c.flagSet) | |||||
| } | |||||
| // Looks up the value of a local boolT flag, returns false if no bool flag exists | |||||
| func (c *Context) BoolT(name string) bool { | |||||
| return lookupBoolT(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 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 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 lookupIntSlice(name, c.flagSet) | |||||
| } | |||||
| // Looks up the value of a local generic flag, returns nil if no generic flag exists | |||||
| func (c *Context) Generic(name string) interface{} { | |||||
| return lookupGeneric(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 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 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 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 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 lookupIntSlice(name, c.globalSet) | |||||
| } | |||||
| // Looks up the value of a global generic flag, returns nil if no generic flag exists | |||||
| func (c *Context) GlobalGeneric(name string) interface{} { | |||||
| return lookupGeneric(name, c.globalSet) | |||||
| } | |||||
| // Determines if the flag was actually set exists | |||||
| func (c *Context) IsSet(name string) bool { | |||||
| if c.setFlags == nil { | |||||
| c.setFlags = make(map[string]bool) | |||||
| c.flagSet.Visit(func(f *flag.Flag) { | |||||
| c.setFlags[f.Name] = true | |||||
| }) | |||||
| } | |||||
| return c.setFlags[name] == true | |||||
| } | |||||
| type Args []string | |||||
| // Returns the command line arguments associated with the context. | |||||
| func (c *Context) Args() Args { | |||||
| args := Args(c.flagSet.Args()) | |||||
| return args | |||||
| } | |||||
| // 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 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 | |||||
| } | |||||
| // Swaps arguments at the given indexes | |||||
| func (a Args) Swap(from, to int) error { | |||||
| if from >= len(a) || to >= len(a) { | |||||
| return errors.New("index out of range") | |||||
| } | |||||
| a[from], a[to] = a[to], a[from] | |||||
| return nil | |||||
| } | |||||
| func lookupInt(name string, set *flag.FlagSet) int { | |||||
| f := set.Lookup(name) | |||||
| if f != nil { | |||||
| val, err := strconv.Atoi(f.Value.String()) | |||||
| if err != nil { | |||||
| return 0 | |||||
| } | |||||
| return val | |||||
| } | |||||
| return 0 | |||||
| } | |||||
| func lookupFloat64(name string, set *flag.FlagSet) float64 { | |||||
| f := set.Lookup(name) | |||||
| if f != nil { | |||||
| val, err := strconv.ParseFloat(f.Value.String(), 64) | |||||
| if err != nil { | |||||
| return 0 | |||||
| } | |||||
| return val | |||||
| } | |||||
| return 0 | |||||
| } | |||||
| func lookupString(name string, set *flag.FlagSet) string { | |||||
| f := set.Lookup(name) | |||||
| if f != nil { | |||||
| return f.Value.String() | |||||
| } | |||||
| return "" | |||||
| } | |||||
| func lookupStringSlice(name string, set *flag.FlagSet) []string { | |||||
| f := set.Lookup(name) | |||||
| if f != nil { | |||||
| return (f.Value.(*StringSlice)).Value() | |||||
| } | |||||
| return nil | |||||
| } | |||||
| func lookupIntSlice(name string, set *flag.FlagSet) []int { | |||||
| f := set.Lookup(name) | |||||
| if f != nil { | |||||
| return (f.Value.(*IntSlice)).Value() | |||||
| } | |||||
| return nil | |||||
| } | |||||
| func lookupGeneric(name string, set *flag.FlagSet) interface{} { | |||||
| f := set.Lookup(name) | |||||
| if f != nil { | |||||
| return f.Value | |||||
| } | |||||
| return nil | |||||
| } | |||||
| func lookupBool(name string, set *flag.FlagSet) bool { | |||||
| f := set.Lookup(name) | |||||
| if f != nil { | |||||
| val, err := strconv.ParseBool(f.Value.String()) | |||||
| if err != nil { | |||||
| return false | |||||
| } | |||||
| return val | |||||
| } | |||||
| return false | |||||
| } | |||||
| func lookupBoolT(name string, set *flag.FlagSet) bool { | |||||
| f := set.Lookup(name) | |||||
| if f != nil { | |||||
| val, err := strconv.ParseBool(f.Value.String()) | |||||
| if err != nil { | |||||
| return true | |||||
| } | |||||
| return val | |||||
| } | |||||
| return false | |||||
| } | |||||
| func copyFlag(name string, ff *flag.Flag, set *flag.FlagSet) { | |||||
| switch ff.Value.(type) { | |||||
| case *StringSlice: | |||||
| default: | |||||
| set.Set(name, ff.Value.String()) | |||||
| } | |||||
| } | |||||
| func normalizeFlags(flags []Flag, set *flag.FlagSet) error { | |||||
| visited := make(map[string]bool) | |||||
| set.Visit(func(f *flag.Flag) { | |||||
| visited[f.Name] = true | |||||
| }) | |||||
| for _, f := range flags { | |||||
| parts := strings.Split(f.getName(), ",") | |||||
| if len(parts) == 1 { | |||||
| continue | |||||
| } | |||||
| var ff *flag.Flag | |||||
| for _, name := range parts { | |||||
| name = strings.Trim(name, " ") | |||||
| if visited[name] { | |||||
| if ff != nil { | |||||
| return errors.New("Cannot use two forms of the same flag: " + name + " " + ff.Name) | |||||
| } | |||||
| ff = set.Lookup(name) | |||||
| } | |||||
| } | |||||
| if ff == nil { | |||||
| continue | |||||
| } | |||||
| for _, name := range parts { | |||||
| name = strings.Trim(name, " ") | |||||
| if !visited[name] { | |||||
| copyFlag(name, ff, set) | |||||
| } | |||||
| } | |||||
| } | |||||
| return nil | |||||
| } | |||||
| @ -0,0 +1,68 @@ | |||||
| package cli_test | |||||
| import ( | |||||
| "flag" | |||||
| "github.com/codegangsta/cli" | |||||
| "testing" | |||||
| ) | |||||
| func TestNewContext(t *testing.T) { | |||||
| set := flag.NewFlagSet("test", 0) | |||||
| set.Int("myflag", 12, "doc") | |||||
| globalSet := flag.NewFlagSet("test", 0) | |||||
| globalSet.Int("myflag", 42, "doc") | |||||
| command := cli.Command{Name: "mycommand"} | |||||
| c := cli.NewContext(nil, set, globalSet) | |||||
| c.Command = command | |||||
| expect(t, c.Int("myflag"), 12) | |||||
| expect(t, c.GlobalInt("myflag"), 42) | |||||
| expect(t, c.Command.Name, "mycommand") | |||||
| } | |||||
| func TestContext_Int(t *testing.T) { | |||||
| set := flag.NewFlagSet("test", 0) | |||||
| set.Int("myflag", 12, "doc") | |||||
| c := cli.NewContext(nil, set, set) | |||||
| expect(t, c.Int("myflag"), 12) | |||||
| } | |||||
| func TestContext_String(t *testing.T) { | |||||
| set := flag.NewFlagSet("test", 0) | |||||
| set.String("myflag", "hello world", "doc") | |||||
| c := cli.NewContext(nil, set, set) | |||||
| expect(t, c.String("myflag"), "hello world") | |||||
| } | |||||
| func TestContext_Bool(t *testing.T) { | |||||
| set := flag.NewFlagSet("test", 0) | |||||
| set.Bool("myflag", false, "doc") | |||||
| c := cli.NewContext(nil, set, set) | |||||
| expect(t, c.Bool("myflag"), false) | |||||
| } | |||||
| func TestContext_BoolT(t *testing.T) { | |||||
| set := flag.NewFlagSet("test", 0) | |||||
| set.Bool("myflag", true, "doc") | |||||
| c := cli.NewContext(nil, set, set) | |||||
| expect(t, c.BoolT("myflag"), true) | |||||
| } | |||||
| func TestContext_Args(t *testing.T) { | |||||
| set := flag.NewFlagSet("test", 0) | |||||
| set.Bool("myflag", false, "doc") | |||||
| c := cli.NewContext(nil, set, set) | |||||
| set.Parse([]string{"--myflag", "bat", "baz"}) | |||||
| expect(t, len(c.Args()), 2) | |||||
| expect(t, c.Bool("myflag"), true) | |||||
| } | |||||
| func TestContext_IsSet(t *testing.T) { | |||||
| set := flag.NewFlagSet("test", 0) | |||||
| set.Bool("myflag", false, "doc") | |||||
| set.String("otherflag", "hello world", "doc") | |||||
| c := cli.NewContext(nil, set, set) | |||||
| set.Parse([]string{"--myflag", "bat", "baz"}) | |||||
| expect(t, c.IsSet("myflag"), true) | |||||
| expect(t, c.IsSet("otherflag"), false) | |||||
| expect(t, c.IsSet("bogusflag"), false) | |||||
| } | |||||
| @ -0,0 +1,379 @@ | |||||
| package cli | |||||
| import ( | |||||
| "flag" | |||||
| "fmt" | |||||
| "os" | |||||
| "strconv" | |||||
| "strings" | |||||
| ) | |||||
| // This flag enables bash-completion for all commands and subcommands | |||||
| var BashCompletionFlag = BoolFlag{ | |||||
| Name: "generate-bash-completion", | |||||
| } | |||||
| // This flag prints the version for the application | |||||
| var VersionFlag = BoolFlag{ | |||||
| Name: "version, v", | |||||
| Usage: "print the version", | |||||
| } | |||||
| // This flag prints the help for all commands and subcommands | |||||
| var HelpFlag = BoolFlag{ | |||||
| Name: "help, h", | |||||
| Usage: "show help", | |||||
| } | |||||
| // Flag is a common interface related to parsing flags in cli. | |||||
| // For more advanced flag parsing techniques, it is recomended that | |||||
| // this interface be implemented. | |||||
| type Flag interface { | |||||
| fmt.Stringer | |||||
| // Apply Flag settings to the given flag set | |||||
| Apply(*flag.FlagSet) | |||||
| getName() string | |||||
| } | |||||
| func flagSet(name string, flags []Flag) *flag.FlagSet { | |||||
| set := flag.NewFlagSet(name, flag.ContinueOnError) | |||||
| for _, f := range flags { | |||||
| f.Apply(set) | |||||
| } | |||||
| return set | |||||
| } | |||||
| func eachName(longName string, fn func(string)) { | |||||
| parts := strings.Split(longName, ",") | |||||
| for _, name := range parts { | |||||
| name = strings.Trim(name, " ") | |||||
| fn(name) | |||||
| } | |||||
| } | |||||
| // Generic is a generic parseable type identified by a specific flag | |||||
| type Generic interface { | |||||
| Set(value string) error | |||||
| String() string | |||||
| } | |||||
| // GenericFlag is the flag type for types implementing Generic | |||||
| type GenericFlag struct { | |||||
| Name string | |||||
| Value Generic | |||||
| Usage string | |||||
| EnvVar string | |||||
| } | |||||
| func (f GenericFlag) String() string { | |||||
| return withEnvHint(f.EnvVar, fmt.Sprintf("%s%s %v\t`%v` %s", prefixFor(f.Name), f.Name, f.Value, "-"+f.Name+" option -"+f.Name+" option", f.Usage)) | |||||
| } | |||||
| func (f GenericFlag) Apply(set *flag.FlagSet) { | |||||
| val := f.Value | |||||
| if f.EnvVar != "" { | |||||
| if envVal := os.Getenv(f.EnvVar); envVal != "" { | |||||
| val.Set(envVal) | |||||
| } | |||||
| } | |||||
| eachName(f.Name, func(name string) { | |||||
| set.Var(f.Value, name, f.Usage) | |||||
| }) | |||||
| } | |||||
| func (f GenericFlag) getName() string { | |||||
| return f.Name | |||||
| } | |||||
| 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 | |||||
| EnvVar string | |||||
| } | |||||
| func (f StringSliceFlag) String() string { | |||||
| firstName := strings.Trim(strings.Split(f.Name, ",")[0], " ") | |||||
| pref := prefixFor(firstName) | |||||
| return withEnvHint(f.EnvVar, fmt.Sprintf("%s '%v'\t%v", prefixedNames(f.Name), pref+firstName+" option "+pref+firstName+" option", f.Usage)) | |||||
| } | |||||
| func (f StringSliceFlag) Apply(set *flag.FlagSet) { | |||||
| if f.EnvVar != "" { | |||||
| if envVal := os.Getenv(f.EnvVar); envVal != "" { | |||||
| newVal := &StringSlice{} | |||||
| for _, s := range strings.Split(envVal, ",") { | |||||
| newVal.Set(s) | |||||
| } | |||||
| f.Value = newVal | |||||
| } | |||||
| } | |||||
| eachName(f.Name, func(name string) { | |||||
| set.Var(f.Value, name, f.Usage) | |||||
| }) | |||||
| } | |||||
| func (f StringSliceFlag) getName() string { | |||||
| return f.Name | |||||
| } | |||||
| 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 | |||||
| EnvVar string | |||||
| } | |||||
| func (f IntSliceFlag) String() string { | |||||
| firstName := strings.Trim(strings.Split(f.Name, ",")[0], " ") | |||||
| pref := prefixFor(firstName) | |||||
| return withEnvHint(f.EnvVar, fmt.Sprintf("%s '%v'\t%v", prefixedNames(f.Name), pref+firstName+" option "+pref+firstName+" option", f.Usage)) | |||||
| } | |||||
| func (f IntSliceFlag) Apply(set *flag.FlagSet) { | |||||
| if f.EnvVar != "" { | |||||
| if envVal := os.Getenv(f.EnvVar); envVal != "" { | |||||
| newVal := &IntSlice{} | |||||
| for _, s := range strings.Split(envVal, ",") { | |||||
| err := newVal.Set(s) | |||||
| if err != nil { | |||||
| fmt.Fprintf(os.Stderr, err.Error()) | |||||
| } | |||||
| } | |||||
| f.Value = newVal | |||||
| } | |||||
| } | |||||
| eachName(f.Name, func(name string) { | |||||
| set.Var(f.Value, name, f.Usage) | |||||
| }) | |||||
| } | |||||
| func (f IntSliceFlag) getName() string { | |||||
| return f.Name | |||||
| } | |||||
| type BoolFlag struct { | |||||
| Name string | |||||
| Usage string | |||||
| EnvVar string | |||||
| } | |||||
| func (f BoolFlag) String() string { | |||||
| return withEnvHint(f.EnvVar, fmt.Sprintf("%s\t%v", prefixedNames(f.Name), f.Usage)) | |||||
| } | |||||
| func (f BoolFlag) Apply(set *flag.FlagSet) { | |||||
| val := false | |||||
| if f.EnvVar != "" { | |||||
| if envVal := os.Getenv(f.EnvVar); envVal != "" { | |||||
| envValBool, err := strconv.ParseBool(envVal) | |||||
| if err == nil { | |||||
| val = envValBool | |||||
| } | |||||
| } | |||||
| } | |||||
| eachName(f.Name, func(name string) { | |||||
| set.Bool(name, val, f.Usage) | |||||
| }) | |||||
| } | |||||
| func (f BoolFlag) getName() string { | |||||
| return f.Name | |||||
| } | |||||
| type BoolTFlag struct { | |||||
| Name string | |||||
| Usage string | |||||
| EnvVar string | |||||
| } | |||||
| func (f BoolTFlag) String() string { | |||||
| return withEnvHint(f.EnvVar, fmt.Sprintf("%s\t%v", prefixedNames(f.Name), f.Usage)) | |||||
| } | |||||
| func (f BoolTFlag) Apply(set *flag.FlagSet) { | |||||
| val := true | |||||
| if f.EnvVar != "" { | |||||
| if envVal := os.Getenv(f.EnvVar); envVal != "" { | |||||
| envValBool, err := strconv.ParseBool(envVal) | |||||
| if err == nil { | |||||
| val = envValBool | |||||
| } | |||||
| } | |||||
| } | |||||
| eachName(f.Name, func(name string) { | |||||
| set.Bool(name, val, f.Usage) | |||||
| }) | |||||
| } | |||||
| func (f BoolTFlag) getName() string { | |||||
| return f.Name | |||||
| } | |||||
| type StringFlag struct { | |||||
| Name string | |||||
| Value string | |||||
| Usage string | |||||
| EnvVar string | |||||
| } | |||||
| func (f StringFlag) String() string { | |||||
| var fmtString string | |||||
| fmtString = "%s %v\t%v" | |||||
| if len(f.Value) > 0 { | |||||
| fmtString = "%s '%v'\t%v" | |||||
| } else { | |||||
| fmtString = "%s %v\t%v" | |||||
| } | |||||
| return withEnvHint(f.EnvVar, fmt.Sprintf(fmtString, prefixedNames(f.Name), f.Value, f.Usage)) | |||||
| } | |||||
| func (f StringFlag) Apply(set *flag.FlagSet) { | |||||
| if f.EnvVar != "" { | |||||
| if envVal := os.Getenv(f.EnvVar); envVal != "" { | |||||
| f.Value = envVal | |||||
| } | |||||
| } | |||||
| eachName(f.Name, func(name string) { | |||||
| set.String(name, f.Value, f.Usage) | |||||
| }) | |||||
| } | |||||
| func (f StringFlag) getName() string { | |||||
| return f.Name | |||||
| } | |||||
| type IntFlag struct { | |||||
| Name string | |||||
| Value int | |||||
| Usage string | |||||
| EnvVar string | |||||
| } | |||||
| func (f IntFlag) String() string { | |||||
| return withEnvHint(f.EnvVar, fmt.Sprintf("%s '%v'\t%v", prefixedNames(f.Name), f.Value, f.Usage)) | |||||
| } | |||||
| func (f IntFlag) Apply(set *flag.FlagSet) { | |||||
| if f.EnvVar != "" { | |||||
| if envVal := os.Getenv(f.EnvVar); envVal != "" { | |||||
| envValInt, err := strconv.ParseUint(envVal, 10, 64) | |||||
| if err == nil { | |||||
| f.Value = int(envValInt) | |||||
| } | |||||
| } | |||||
| } | |||||
| eachName(f.Name, func(name string) { | |||||
| set.Int(name, f.Value, f.Usage) | |||||
| }) | |||||
| } | |||||
| func (f IntFlag) getName() string { | |||||
| return f.Name | |||||
| } | |||||
| type Float64Flag struct { | |||||
| Name string | |||||
| Value float64 | |||||
| Usage string | |||||
| EnvVar string | |||||
| } | |||||
| func (f Float64Flag) String() string { | |||||
| return withEnvHint(f.EnvVar, fmt.Sprintf("%s '%v'\t%v", prefixedNames(f.Name), f.Value, f.Usage)) | |||||
| } | |||||
| func (f Float64Flag) Apply(set *flag.FlagSet) { | |||||
| if f.EnvVar != "" { | |||||
| if envVal := os.Getenv(f.EnvVar); envVal != "" { | |||||
| envValFloat, err := strconv.ParseFloat(envVal, 10) | |||||
| if err == nil { | |||||
| f.Value = float64(envValFloat) | |||||
| } | |||||
| } | |||||
| } | |||||
| eachName(f.Name, func(name string) { | |||||
| set.Float64(name, f.Value, f.Usage) | |||||
| }) | |||||
| } | |||||
| func (f Float64Flag) getName() string { | |||||
| return f.Name | |||||
| } | |||||
| func prefixFor(name string) (prefix string) { | |||||
| if len(name) == 1 { | |||||
| prefix = "-" | |||||
| } else { | |||||
| prefix = "--" | |||||
| } | |||||
| return | |||||
| } | |||||
| func prefixedNames(fullName string) (prefixed string) { | |||||
| parts := strings.Split(fullName, ",") | |||||
| for i, name := range parts { | |||||
| name = strings.Trim(name, " ") | |||||
| prefixed += prefixFor(name) + name | |||||
| if i < len(parts)-1 { | |||||
| prefixed += ", " | |||||
| } | |||||
| } | |||||
| return | |||||
| } | |||||
| func withEnvHint(envVar, str string) string { | |||||
| envText := "" | |||||
| if envVar != "" { | |||||
| envText = fmt.Sprintf(" [$%s]", envVar) | |||||
| } | |||||
| return str + envText | |||||
| } | |||||
| @ -0,0 +1,554 @@ | |||||
| package cli_test | |||||
| import ( | |||||
| "github.com/codegangsta/cli" | |||||
| "fmt" | |||||
| "os" | |||||
| "reflect" | |||||
| "strings" | |||||
| "testing" | |||||
| ) | |||||
| var boolFlagTests = []struct { | |||||
| name string | |||||
| expected string | |||||
| }{ | |||||
| {"help", "--help\t"}, | |||||
| {"h", "-h\t"}, | |||||
| } | |||||
| func TestBoolFlagHelpOutput(t *testing.T) { | |||||
| for _, test := range boolFlagTests { | |||||
| flag := cli.BoolFlag{Name: test.name} | |||||
| output := flag.String() | |||||
| if output != test.expected { | |||||
| t.Errorf("%s does not match %s", output, test.expected) | |||||
| } | |||||
| } | |||||
| } | |||||
| var stringFlagTests = []struct { | |||||
| name string | |||||
| value string | |||||
| expected string | |||||
| }{ | |||||
| {"help", "", "--help \t"}, | |||||
| {"h", "", "-h \t"}, | |||||
| {"h", "", "-h \t"}, | |||||
| {"test", "Something", "--test 'Something'\t"}, | |||||
| } | |||||
| func TestStringFlagHelpOutput(t *testing.T) { | |||||
| for _, test := range stringFlagTests { | |||||
| flag := cli.StringFlag{Name: test.name, Value: test.value} | |||||
| output := flag.String() | |||||
| if output != test.expected { | |||||
| t.Errorf("%s does not match %s", output, test.expected) | |||||
| } | |||||
| } | |||||
| } | |||||
| func TestStringFlagWithEnvVarHelpOutput(t *testing.T) { | |||||
| os.Setenv("APP_FOO", "derp") | |||||
| for _, test := range stringFlagTests { | |||||
| flag := cli.StringFlag{Name: test.name, Value: test.value, EnvVar: "APP_FOO"} | |||||
| output := flag.String() | |||||
| if !strings.HasSuffix(output, " [$APP_FOO]") { | |||||
| t.Errorf("%s does not end with [$APP_FOO]", output) | |||||
| } | |||||
| } | |||||
| } | |||||
| var stringSliceFlagTests = []struct { | |||||
| name string | |||||
| value *cli.StringSlice | |||||
| expected string | |||||
| }{ | |||||
| {"help", func() *cli.StringSlice { | |||||
| s := &cli.StringSlice{} | |||||
| s.Set("") | |||||
| return s | |||||
| }(), "--help '--help option --help option'\t"}, | |||||
| {"h", func() *cli.StringSlice { | |||||
| s := &cli.StringSlice{} | |||||
| s.Set("") | |||||
| return s | |||||
| }(), "-h '-h option -h option'\t"}, | |||||
| {"h", func() *cli.StringSlice { | |||||
| s := &cli.StringSlice{} | |||||
| s.Set("") | |||||
| return s | |||||
| }(), "-h '-h option -h option'\t"}, | |||||
| {"test", func() *cli.StringSlice { | |||||
| s := &cli.StringSlice{} | |||||
| s.Set("Something") | |||||
| return s | |||||
| }(), "--test '--test option --test option'\t"}, | |||||
| } | |||||
| func TestStringSliceFlagHelpOutput(t *testing.T) { | |||||
| for _, test := range stringSliceFlagTests { | |||||
| flag := cli.StringSliceFlag{Name: test.name, Value: test.value} | |||||
| output := flag.String() | |||||
| if output != test.expected { | |||||
| t.Errorf("%q does not match %q", output, test.expected) | |||||
| } | |||||
| } | |||||
| } | |||||
| func TestStringSliceFlagWithEnvVarHelpOutput(t *testing.T) { | |||||
| os.Setenv("APP_QWWX", "11,4") | |||||
| for _, test := range stringSliceFlagTests { | |||||
| flag := cli.StringSliceFlag{Name: test.name, Value: test.value, EnvVar: "APP_QWWX"} | |||||
| output := flag.String() | |||||
| if !strings.HasSuffix(output, " [$APP_QWWX]") { | |||||
| t.Errorf("%q does not end with [$APP_QWWX]", output) | |||||
| } | |||||
| } | |||||
| } | |||||
| var intFlagTests = []struct { | |||||
| name string | |||||
| expected string | |||||
| }{ | |||||
| {"help", "--help '0'\t"}, | |||||
| {"h", "-h '0'\t"}, | |||||
| } | |||||
| func TestIntFlagHelpOutput(t *testing.T) { | |||||
| for _, test := range intFlagTests { | |||||
| flag := cli.IntFlag{Name: test.name} | |||||
| output := flag.String() | |||||
| if output != test.expected { | |||||
| t.Errorf("%s does not match %s", output, test.expected) | |||||
| } | |||||
| } | |||||
| } | |||||
| func TestIntFlagWithEnvVarHelpOutput(t *testing.T) { | |||||
| os.Setenv("APP_BAR", "2") | |||||
| for _, test := range intFlagTests { | |||||
| flag := cli.IntFlag{Name: test.name, EnvVar: "APP_BAR"} | |||||
| output := flag.String() | |||||
| if !strings.HasSuffix(output, " [$APP_BAR]") { | |||||
| t.Errorf("%s does not end with [$APP_BAR]", output) | |||||
| } | |||||
| } | |||||
| } | |||||
| var intSliceFlagTests = []struct { | |||||
| name string | |||||
| value *cli.IntSlice | |||||
| expected string | |||||
| }{ | |||||
| {"help", &cli.IntSlice{}, "--help '--help option --help option'\t"}, | |||||
| {"h", &cli.IntSlice{}, "-h '-h option -h option'\t"}, | |||||
| {"h", &cli.IntSlice{}, "-h '-h option -h option'\t"}, | |||||
| {"test", func() *cli.IntSlice { | |||||
| i := &cli.IntSlice{} | |||||
| i.Set("9") | |||||
| return i | |||||
| }(), "--test '--test option --test option'\t"}, | |||||
| } | |||||
| func TestIntSliceFlagHelpOutput(t *testing.T) { | |||||
| for _, test := range intSliceFlagTests { | |||||
| flag := cli.IntSliceFlag{Name: test.name, Value: test.value} | |||||
| output := flag.String() | |||||
| if output != test.expected { | |||||
| t.Errorf("%q does not match %q", output, test.expected) | |||||
| } | |||||
| } | |||||
| } | |||||
| func TestIntSliceFlagWithEnvVarHelpOutput(t *testing.T) { | |||||
| os.Setenv("APP_SMURF", "42,3") | |||||
| for _, test := range intSliceFlagTests { | |||||
| flag := cli.IntSliceFlag{Name: test.name, Value: test.value, EnvVar: "APP_SMURF"} | |||||
| output := flag.String() | |||||
| if !strings.HasSuffix(output, " [$APP_SMURF]") { | |||||
| t.Errorf("%q does not end with [$APP_SMURF]", output) | |||||
| } | |||||
| } | |||||
| } | |||||
| var float64FlagTests = []struct { | |||||
| name string | |||||
| expected string | |||||
| }{ | |||||
| {"help", "--help '0'\t"}, | |||||
| {"h", "-h '0'\t"}, | |||||
| } | |||||
| func TestFloat64FlagHelpOutput(t *testing.T) { | |||||
| for _, test := range float64FlagTests { | |||||
| flag := cli.Float64Flag{Name: test.name} | |||||
| output := flag.String() | |||||
| if output != test.expected { | |||||
| t.Errorf("%s does not match %s", output, test.expected) | |||||
| } | |||||
| } | |||||
| } | |||||
| func TestFloat64FlagWithEnvVarHelpOutput(t *testing.T) { | |||||
| os.Setenv("APP_BAZ", "99.4") | |||||
| for _, test := range float64FlagTests { | |||||
| flag := cli.Float64Flag{Name: test.name, EnvVar: "APP_BAZ"} | |||||
| output := flag.String() | |||||
| if !strings.HasSuffix(output, " [$APP_BAZ]") { | |||||
| t.Errorf("%s does not end with [$APP_BAZ]", output) | |||||
| } | |||||
| } | |||||
| } | |||||
| var genericFlagTests = []struct { | |||||
| name string | |||||
| value cli.Generic | |||||
| expected string | |||||
| }{ | |||||
| {"help", &Parser{}, "--help <nil>\t`-help option -help option` "}, | |||||
| {"h", &Parser{}, "-h <nil>\t`-h option -h option` "}, | |||||
| {"test", &Parser{}, "--test <nil>\t`-test option -test option` "}, | |||||
| } | |||||
| func TestGenericFlagHelpOutput(t *testing.T) { | |||||
| for _, test := range genericFlagTests { | |||||
| flag := cli.GenericFlag{Name: test.name} | |||||
| output := flag.String() | |||||
| if output != test.expected { | |||||
| t.Errorf("%q does not match %q", output, test.expected) | |||||
| } | |||||
| } | |||||
| } | |||||
| func TestGenericFlagWithEnvVarHelpOutput(t *testing.T) { | |||||
| os.Setenv("APP_ZAP", "3") | |||||
| for _, test := range genericFlagTests { | |||||
| flag := cli.GenericFlag{Name: test.name, EnvVar: "APP_ZAP"} | |||||
| output := flag.String() | |||||
| if !strings.HasSuffix(output, " [$APP_ZAP]") { | |||||
| t.Errorf("%s does not end with [$APP_ZAP]", output) | |||||
| } | |||||
| } | |||||
| } | |||||
| func TestParseMultiString(t *testing.T) { | |||||
| (&cli.App{ | |||||
| Flags: []cli.Flag{ | |||||
| cli.StringFlag{Name: "serve, s"}, | |||||
| }, | |||||
| Action: func(ctx *cli.Context) { | |||||
| if ctx.String("serve") != "10" { | |||||
| t.Errorf("main name not set") | |||||
| } | |||||
| if ctx.String("s") != "10" { | |||||
| t.Errorf("short name not set") | |||||
| } | |||||
| }, | |||||
| }).Run([]string{"run", "-s", "10"}) | |||||
| } | |||||
| func TestParseMultiStringFromEnv(t *testing.T) { | |||||
| os.Setenv("APP_COUNT", "20") | |||||
| (&cli.App{ | |||||
| Flags: []cli.Flag{ | |||||
| cli.StringFlag{Name: "count, c", EnvVar: "APP_COUNT"}, | |||||
| }, | |||||
| Action: func(ctx *cli.Context) { | |||||
| if ctx.String("count") != "20" { | |||||
| t.Errorf("main name not set") | |||||
| } | |||||
| if ctx.String("c") != "20" { | |||||
| t.Errorf("short name not set") | |||||
| } | |||||
| }, | |||||
| }).Run([]string{"run"}) | |||||
| } | |||||
| func TestParseMultiStringSlice(t *testing.T) { | |||||
| (&cli.App{ | |||||
| Flags: []cli.Flag{ | |||||
| cli.StringSliceFlag{Name: "serve, s", Value: &cli.StringSlice{}}, | |||||
| }, | |||||
| Action: func(ctx *cli.Context) { | |||||
| if !reflect.DeepEqual(ctx.StringSlice("serve"), []string{"10", "20"}) { | |||||
| t.Errorf("main name not set") | |||||
| } | |||||
| if !reflect.DeepEqual(ctx.StringSlice("s"), []string{"10", "20"}) { | |||||
| t.Errorf("short name not set") | |||||
| } | |||||
| }, | |||||
| }).Run([]string{"run", "-s", "10", "-s", "20"}) | |||||
| } | |||||
| func TestParseMultiStringSliceFromEnv(t *testing.T) { | |||||
| os.Setenv("APP_INTERVALS", "20,30,40") | |||||
| (&cli.App{ | |||||
| Flags: []cli.Flag{ | |||||
| cli.StringSliceFlag{Name: "intervals, i", Value: &cli.StringSlice{}, EnvVar: "APP_INTERVALS"}, | |||||
| }, | |||||
| Action: func(ctx *cli.Context) { | |||||
| if !reflect.DeepEqual(ctx.StringSlice("intervals"), []string{"20", "30", "40"}) { | |||||
| t.Errorf("main name not set from env") | |||||
| } | |||||
| if !reflect.DeepEqual(ctx.StringSlice("i"), []string{"20", "30", "40"}) { | |||||
| t.Errorf("short name not set from env") | |||||
| } | |||||
| }, | |||||
| }).Run([]string{"run"}) | |||||
| } | |||||
| func TestParseMultiInt(t *testing.T) { | |||||
| a := cli.App{ | |||||
| Flags: []cli.Flag{ | |||||
| cli.IntFlag{Name: "serve, s"}, | |||||
| }, | |||||
| Action: func(ctx *cli.Context) { | |||||
| if ctx.Int("serve") != 10 { | |||||
| t.Errorf("main name not set") | |||||
| } | |||||
| if ctx.Int("s") != 10 { | |||||
| t.Errorf("short name not set") | |||||
| } | |||||
| }, | |||||
| } | |||||
| a.Run([]string{"run", "-s", "10"}) | |||||
| } | |||||
| func TestParseMultiIntFromEnv(t *testing.T) { | |||||
| os.Setenv("APP_TIMEOUT_SECONDS", "10") | |||||
| a := cli.App{ | |||||
| Flags: []cli.Flag{ | |||||
| cli.IntFlag{Name: "timeout, t", EnvVar: "APP_TIMEOUT_SECONDS"}, | |||||
| }, | |||||
| Action: func(ctx *cli.Context) { | |||||
| if ctx.Int("timeout") != 10 { | |||||
| t.Errorf("main name not set") | |||||
| } | |||||
| if ctx.Int("t") != 10 { | |||||
| t.Errorf("short name not set") | |||||
| } | |||||
| }, | |||||
| } | |||||
| a.Run([]string{"run"}) | |||||
| } | |||||
| func TestParseMultiIntSlice(t *testing.T) { | |||||
| (&cli.App{ | |||||
| Flags: []cli.Flag{ | |||||
| cli.IntSliceFlag{Name: "serve, s", Value: &cli.IntSlice{}}, | |||||
| }, | |||||
| Action: func(ctx *cli.Context) { | |||||
| if !reflect.DeepEqual(ctx.IntSlice("serve"), []int{10, 20}) { | |||||
| t.Errorf("main name not set") | |||||
| } | |||||
| if !reflect.DeepEqual(ctx.IntSlice("s"), []int{10, 20}) { | |||||
| t.Errorf("short name not set") | |||||
| } | |||||
| }, | |||||
| }).Run([]string{"run", "-s", "10", "-s", "20"}) | |||||
| } | |||||
| func TestParseMultiIntSliceFromEnv(t *testing.T) { | |||||
| os.Setenv("APP_INTERVALS", "20,30,40") | |||||
| (&cli.App{ | |||||
| Flags: []cli.Flag{ | |||||
| cli.IntSliceFlag{Name: "intervals, i", Value: &cli.IntSlice{}, EnvVar: "APP_INTERVALS"}, | |||||
| }, | |||||
| Action: func(ctx *cli.Context) { | |||||
| if !reflect.DeepEqual(ctx.IntSlice("intervals"), []int{20, 30, 40}) { | |||||
| t.Errorf("main name not set from env") | |||||
| } | |||||
| if !reflect.DeepEqual(ctx.IntSlice("i"), []int{20, 30, 40}) { | |||||
| t.Errorf("short name not set from env") | |||||
| } | |||||
| }, | |||||
| }).Run([]string{"run"}) | |||||
| } | |||||
| func TestParseMultiFloat64(t *testing.T) { | |||||
| a := cli.App{ | |||||
| Flags: []cli.Flag{ | |||||
| cli.Float64Flag{Name: "serve, s"}, | |||||
| }, | |||||
| Action: func(ctx *cli.Context) { | |||||
| if ctx.Float64("serve") != 10.2 { | |||||
| t.Errorf("main name not set") | |||||
| } | |||||
| if ctx.Float64("s") != 10.2 { | |||||
| t.Errorf("short name not set") | |||||
| } | |||||
| }, | |||||
| } | |||||
| a.Run([]string{"run", "-s", "10.2"}) | |||||
| } | |||||
| func TestParseMultiFloat64FromEnv(t *testing.T) { | |||||
| os.Setenv("APP_TIMEOUT_SECONDS", "15.5") | |||||
| a := cli.App{ | |||||
| Flags: []cli.Flag{ | |||||
| cli.Float64Flag{Name: "timeout, t", EnvVar: "APP_TIMEOUT_SECONDS"}, | |||||
| }, | |||||
| Action: func(ctx *cli.Context) { | |||||
| if ctx.Float64("timeout") != 15.5 { | |||||
| t.Errorf("main name not set") | |||||
| } | |||||
| if ctx.Float64("t") != 15.5 { | |||||
| t.Errorf("short name not set") | |||||
| } | |||||
| }, | |||||
| } | |||||
| a.Run([]string{"run"}) | |||||
| } | |||||
| func TestParseMultiBool(t *testing.T) { | |||||
| a := cli.App{ | |||||
| Flags: []cli.Flag{ | |||||
| cli.BoolFlag{Name: "serve, s"}, | |||||
| }, | |||||
| Action: func(ctx *cli.Context) { | |||||
| if ctx.Bool("serve") != true { | |||||
| t.Errorf("main name not set") | |||||
| } | |||||
| if ctx.Bool("s") != true { | |||||
| t.Errorf("short name not set") | |||||
| } | |||||
| }, | |||||
| } | |||||
| a.Run([]string{"run", "--serve"}) | |||||
| } | |||||
| func TestParseMultiBoolFromEnv(t *testing.T) { | |||||
| os.Setenv("APP_DEBUG", "1") | |||||
| a := cli.App{ | |||||
| Flags: []cli.Flag{ | |||||
| cli.BoolFlag{Name: "debug, d", EnvVar: "APP_DEBUG"}, | |||||
| }, | |||||
| Action: func(ctx *cli.Context) { | |||||
| if ctx.Bool("debug") != true { | |||||
| t.Errorf("main name not set from env") | |||||
| } | |||||
| if ctx.Bool("d") != true { | |||||
| t.Errorf("short name not set from env") | |||||
| } | |||||
| }, | |||||
| } | |||||
| a.Run([]string{"run"}) | |||||
| } | |||||
| func TestParseMultiBoolT(t *testing.T) { | |||||
| a := cli.App{ | |||||
| Flags: []cli.Flag{ | |||||
| cli.BoolTFlag{Name: "serve, s"}, | |||||
| }, | |||||
| Action: func(ctx *cli.Context) { | |||||
| if ctx.BoolT("serve") != true { | |||||
| t.Errorf("main name not set") | |||||
| } | |||||
| if ctx.BoolT("s") != true { | |||||
| t.Errorf("short name not set") | |||||
| } | |||||
| }, | |||||
| } | |||||
| a.Run([]string{"run", "--serve"}) | |||||
| } | |||||
| func TestParseMultiBoolTFromEnv(t *testing.T) { | |||||
| os.Setenv("APP_DEBUG", "0") | |||||
| a := cli.App{ | |||||
| Flags: []cli.Flag{ | |||||
| cli.BoolTFlag{Name: "debug, d", EnvVar: "APP_DEBUG"}, | |||||
| }, | |||||
| Action: func(ctx *cli.Context) { | |||||
| if ctx.BoolT("debug") != false { | |||||
| t.Errorf("main name not set from env") | |||||
| } | |||||
| if ctx.BoolT("d") != false { | |||||
| t.Errorf("short name not set from env") | |||||
| } | |||||
| }, | |||||
| } | |||||
| a.Run([]string{"run"}) | |||||
| } | |||||
| type Parser [2]string | |||||
| func (p *Parser) Set(value string) error { | |||||
| parts := strings.Split(value, ",") | |||||
| if len(parts) != 2 { | |||||
| return fmt.Errorf("invalid format") | |||||
| } | |||||
| (*p)[0] = parts[0] | |||||
| (*p)[1] = parts[1] | |||||
| return nil | |||||
| } | |||||
| func (p *Parser) String() string { | |||||
| return fmt.Sprintf("%s,%s", p[0], p[1]) | |||||
| } | |||||
| func TestParseGeneric(t *testing.T) { | |||||
| a := cli.App{ | |||||
| Flags: []cli.Flag{ | |||||
| cli.GenericFlag{Name: "serve, s", Value: &Parser{}}, | |||||
| }, | |||||
| Action: func(ctx *cli.Context) { | |||||
| if !reflect.DeepEqual(ctx.Generic("serve"), &Parser{"10", "20"}) { | |||||
| t.Errorf("main name not set") | |||||
| } | |||||
| if !reflect.DeepEqual(ctx.Generic("s"), &Parser{"10", "20"}) { | |||||
| t.Errorf("short name not set") | |||||
| } | |||||
| }, | |||||
| } | |||||
| a.Run([]string{"run", "-s", "10,20"}) | |||||
| } | |||||
| func TestParseGenericFromEnv(t *testing.T) { | |||||
| os.Setenv("APP_SERVE", "20,30") | |||||
| a := cli.App{ | |||||
| Flags: []cli.Flag{ | |||||
| cli.GenericFlag{Name: "serve, s", Value: &Parser{}, EnvVar: "APP_SERVE"}, | |||||
| }, | |||||
| Action: func(ctx *cli.Context) { | |||||
| if !reflect.DeepEqual(ctx.Generic("serve"), &Parser{"20", "30"}) { | |||||
| t.Errorf("main name not set from env") | |||||
| } | |||||
| if !reflect.DeepEqual(ctx.Generic("s"), &Parser{"20", "30"}) { | |||||
| t.Errorf("short name not set from env") | |||||
| } | |||||
| }, | |||||
| } | |||||
| a.Run([]string{"run"}) | |||||
| } | |||||
| @ -0,0 +1,213 @@ | |||||
| package cli | |||||
| import ( | |||||
| "fmt" | |||||
| "os" | |||||
| "text/tabwriter" | |||||
| "text/template" | |||||
| ) | |||||
| // The text template for the Default help topic. | |||||
| // cli.go uses text/template to render templates. You can | |||||
| // render custom help text by setting this variable. | |||||
| var AppHelpTemplate = `NAME: | |||||
| {{.Name}} - {{.Usage}} | |||||
| USAGE: | |||||
| {{.Name}} {{ if .Flags }}[global options] {{ end }}command{{ if .Flags }} [command options]{{ end }} [arguments...] | |||||
| VERSION: | |||||
| {{.Version}} | |||||
| COMMANDS: | |||||
| {{range .Commands}}{{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}} | |||||
| {{end}}{{ if .Flags }} | |||||
| GLOBAL OPTIONS: | |||||
| {{range .Flags}}{{.}} | |||||
| {{end}}{{ end }} | |||||
| ` | |||||
| // The text template for the command help topic. | |||||
| // cli.go uses text/template to render templates. You can | |||||
| // render custom help text by setting this variable. | |||||
| var CommandHelpTemplate = `NAME: | |||||
| {{.Name}} - {{.Usage}} | |||||
| USAGE: | |||||
| command {{.Name}}{{ if .Flags }} [command options]{{ end }} [arguments...] | |||||
| DESCRIPTION: | |||||
| {{.Description}}{{ if .Flags }} | |||||
| OPTIONS: | |||||
| {{range .Flags}}{{.}} | |||||
| {{end}}{{ end }} | |||||
| ` | |||||
| // The text template for the subcommand help topic. | |||||
| // cli.go uses text/template to render templates. You can | |||||
| // render custom help text by setting this variable. | |||||
| var SubcommandHelpTemplate = `NAME: | |||||
| {{.Name}} - {{.Usage}} | |||||
| USAGE: | |||||
| {{.Name}} command{{ if .Flags }} [command options]{{ end }} [arguments...] | |||||
| COMMANDS: | |||||
| {{range .Commands}}{{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}} | |||||
| {{end}}{{ if .Flags }} | |||||
| OPTIONS: | |||||
| {{range .Flags}}{{.}} | |||||
| {{end}}{{ end }} | |||||
| ` | |||||
| var helpCommand = Command{ | |||||
| Name: "help", | |||||
| ShortName: "h", | |||||
| Usage: "Shows a list of commands or help for one command", | |||||
| Action: func(c *Context) { | |||||
| args := c.Args() | |||||
| if args.Present() { | |||||
| ShowCommandHelp(c, args.First()) | |||||
| } else { | |||||
| ShowAppHelp(c) | |||||
| } | |||||
| }, | |||||
| } | |||||
| var helpSubcommand = Command{ | |||||
| Name: "help", | |||||
| ShortName: "h", | |||||
| Usage: "Shows a list of commands or help for one command", | |||||
| Action: func(c *Context) { | |||||
| args := c.Args() | |||||
| if args.Present() { | |||||
| ShowCommandHelp(c, args.First()) | |||||
| } else { | |||||
| ShowSubcommandHelp(c) | |||||
| } | |||||
| }, | |||||
| } | |||||
| // Prints help for the App | |||||
| var HelpPrinter = printHelp | |||||
| func ShowAppHelp(c *Context) { | |||||
| HelpPrinter(AppHelpTemplate, c.App) | |||||
| } | |||||
| // Prints the list of subcommands as the default app completion method | |||||
| func DefaultAppComplete(c *Context) { | |||||
| for _, command := range c.App.Commands { | |||||
| fmt.Println(command.Name) | |||||
| if command.ShortName != "" { | |||||
| fmt.Println(command.ShortName) | |||||
| } | |||||
| } | |||||
| } | |||||
| // Prints help for the given command | |||||
| func ShowCommandHelp(c *Context, command string) { | |||||
| for _, c := range c.App.Commands { | |||||
| if c.HasName(command) { | |||||
| HelpPrinter(CommandHelpTemplate, c) | |||||
| return | |||||
| } | |||||
| } | |||||
| if c.App.CommandNotFound != nil { | |||||
| c.App.CommandNotFound(c, command) | |||||
| } else { | |||||
| fmt.Printf("No help topic for '%v'\n", command) | |||||
| } | |||||
| } | |||||
| // Prints help for the given subcommand | |||||
| func ShowSubcommandHelp(c *Context) { | |||||
| HelpPrinter(SubcommandHelpTemplate, c.App) | |||||
| } | |||||
| // Prints the version number of the App | |||||
| func ShowVersion(c *Context) { | |||||
| fmt.Printf("%v version %v\n", c.App.Name, c.App.Version) | |||||
| } | |||||
| // Prints the lists of commands within a given context | |||||
| func ShowCompletions(c *Context) { | |||||
| a := c.App | |||||
| if a != nil && a.BashComplete != nil { | |||||
| a.BashComplete(c) | |||||
| } | |||||
| } | |||||
| // Prints the custom completions for a given command | |||||
| func ShowCommandCompletions(ctx *Context, command string) { | |||||
| c := ctx.App.Command(command) | |||||
| if c != nil && c.BashComplete != nil { | |||||
| c.BashComplete(ctx) | |||||
| } | |||||
| } | |||||
| func printHelp(templ string, data interface{}) { | |||||
| w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0) | |||||
| t := template.Must(template.New("help").Parse(templ)) | |||||
| err := t.Execute(w, data) | |||||
| if err != nil { | |||||
| panic(err) | |||||
| } | |||||
| w.Flush() | |||||
| } | |||||
| func checkVersion(c *Context) bool { | |||||
| if c.GlobalBool("version") { | |||||
| ShowVersion(c) | |||||
| return true | |||||
| } | |||||
| return false | |||||
| } | |||||
| func checkHelp(c *Context) bool { | |||||
| if c.GlobalBool("h") || c.GlobalBool("help") { | |||||
| ShowAppHelp(c) | |||||
| return true | |||||
| } | |||||
| return false | |||||
| } | |||||
| func checkCommandHelp(c *Context, name string) bool { | |||||
| if c.Bool("h") || c.Bool("help") { | |||||
| ShowCommandHelp(c, name) | |||||
| return true | |||||
| } | |||||
| return false | |||||
| } | |||||
| func checkSubcommandHelp(c *Context) bool { | |||||
| if c.GlobalBool("h") || c.GlobalBool("help") { | |||||
| ShowSubcommandHelp(c) | |||||
| return true | |||||
| } | |||||
| return false | |||||
| } | |||||
| func checkCompletions(c *Context) bool { | |||||
| if c.GlobalBool(BashCompletionFlag.Name) && c.App.EnableBashCompletion { | |||||
| ShowCompletions(c) | |||||
| return true | |||||
| } | |||||
| return false | |||||
| } | |||||
| func checkCommandCompletions(c *Context, name string) bool { | |||||
| if c.Bool(BashCompletionFlag.Name) && c.App.EnableBashCompletion { | |||||
| ShowCommandCompletions(c, name) | |||||
| return true | |||||
| } | |||||
| return false | |||||
| } | |||||
| @ -0,0 +1,19 @@ | |||||
| package cli_test | |||||
| import ( | |||||
| "reflect" | |||||
| "testing" | |||||
| ) | |||||
| /* 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)) | |||||
| } | |||||
| } | |||||
| 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)) | |||||
| } | |||||
| } | |||||