├── .gitignore ├── Godeps ├── _workspace │ ├── .gitignore │ └── src │ │ └── github.com │ │ ├── wsxiaoys │ │ └── terminal │ │ │ ├── .gitignore │ │ │ ├── example │ │ │ └── hello.go │ │ │ ├── README.md │ │ │ ├── LICENSE │ │ │ ├── terminal.go │ │ │ └── color │ │ │ └── color.go │ │ └── codegangsta │ │ └── cli │ │ ├── .travis.yml │ │ ├── autocomplete │ │ ├── zsh_autocomplete │ │ └── bash_autocomplete │ │ ├── helpers_test.go │ │ ├── cli.go │ │ ├── LICENSE │ │ ├── command_test.go │ │ ├── cli_test.go │ │ ├── context_test.go │ │ ├── command.go │ │ ├── help.go │ │ ├── app.go │ │ ├── README.md │ │ ├── context.go │ │ ├── flag.go │ │ ├── app_test.go │ │ └── flag_test.go ├── Readme └── Godeps.json ├── changelog.md ├── make.sh ├── README.md ├── anybar.go └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | builds/* 2 | -------------------------------------------------------------------------------- /Godeps/_workspace/.gitignore: -------------------------------------------------------------------------------- 1 | /pkg 2 | /bin 3 | -------------------------------------------------------------------------------- /changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ##0.0.1 4 | 5 | * Initial release 6 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/wsxiaoys/terminal/.gitignore: -------------------------------------------------------------------------------- 1 | example/example 2 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/codegangsta/cli/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go: 1.1 3 | 4 | script: 5 | - go vet ./... 6 | - go test -v ./... 7 | -------------------------------------------------------------------------------- /Godeps/Readme: -------------------------------------------------------------------------------- 1 | This directory tree is generated automatically by godep. 2 | 3 | Please do not edit. 4 | 5 | See https://github.com/tools/godep for more information. 6 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/codegangsta/cli/autocomplete/zsh_autocomplete: -------------------------------------------------------------------------------- 1 | autoload -U compinit && compinit 2 | autoload -U bashcompinit && bashcompinit 3 | 4 | script_dir=$(dirname $0) 5 | source ${script_dir}/bash_autocomplete 6 | -------------------------------------------------------------------------------- /Godeps/Godeps.json: -------------------------------------------------------------------------------- 1 | { 2 | "ImportPath": "github.com/johntdyer/anybar-go", 3 | "GoVersion": "go1.4", 4 | "Packages": [ 5 | "." 6 | ], 7 | "Deps": [ 8 | { 9 | "ImportPath": "github.com/codegangsta/cli", 10 | "Comment": "1.2.0-50-ga14c5b4", 11 | "Rev": "a14c5b47c7efa4ff80cc42e1079a34b4756f2311" 12 | }, 13 | { 14 | "ImportPath": "github.com/wsxiaoys/terminal", 15 | "Rev": "9dcaf1d63119a8ac00eef82270eaef08b6aa2328" 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/codegangsta/cli/autocomplete/bash_autocomplete: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | _cli_bash_autocomplete() { 4 | local cur prev opts base 5 | COMPREPLY=() 6 | cur="${COMP_WORDS[COMP_CWORD]}" 7 | prev="${COMP_WORDS[COMP_CWORD-1]}" 8 | opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} --generate-bash-completion ) 9 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 10 | return 0 11 | } 12 | 13 | complete -F _cli_bash_autocomplete $PROG -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/wsxiaoys/terminal/example/hello.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/johntdyer/anybar-go/Godeps/_workspace/src/github.com/wsxiaoys/terminal" 5 | "github.com/johntdyer/anybar-go/Godeps/_workspace/src/github.com/wsxiaoys/terminal/color" 6 | ) 7 | 8 | func main() { 9 | terminal.Stdout.Color("y"). 10 | Print("Hello world").Nl(). 11 | Reset(). 12 | Colorf("@{kW}Hello world\n") 13 | 14 | color.Print("@rHello world") 15 | } 16 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/codegangsta/cli/helpers_test.go: -------------------------------------------------------------------------------- 1 | package cli_test 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | /* Test Helpers */ 9 | func expect(t *testing.T, a interface{}, b interface{}) { 10 | if a != b { 11 | t.Errorf("Expected %v (type %v) - Got %v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a)) 12 | } 13 | } 14 | 15 | func refute(t *testing.T, a interface{}, b interface{}) { 16 | if a == b { 17 | t.Errorf("Did not expect %v (type %v) - Got %v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a)) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /make.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | CGO_ENABLED=0 3 | APPNAME=anybar-go 4 | 5 | # Install dependencies 6 | go get github.com/tools/godep 7 | 8 | mkdir -p builds 9 | 10 | 11 | # Build for OSX 12 | godep go build -o builds/$APPNAME.osx 13 | if [ $? -eq 0 ]; then 14 | echo "Success Build artifact - builds/$APPNAME.osx" 15 | else 16 | echo "Build error" 17 | exit $? 18 | fi 19 | 20 | # Build for Linux 21 | GOOS=linux GOARCH=amd64 CGO_ENABLED=0 godep go build -o builds/$APPNAME.linux 22 | if [ $? -eq 0 ]; then 23 | echo "Success Build artifact - builds/$APPNAME.linux" 24 | else 25 | echo "Build error" 26 | exit $? 27 | fi 28 | 29 | chmod +x builds/$APPNAME.linux 30 | chmod +x builds/$APPNAME.osx 31 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/codegangsta/cli/cli.go: -------------------------------------------------------------------------------- 1 | // Package cli provides a minimal framework for creating and organizing command line 2 | // Go applications. cli is designed to be easy to understand and write, the most simple 3 | // cli application can be written as follows: 4 | // func main() { 5 | // cli.NewApp().Run(os.Args) 6 | // } 7 | // 8 | // Of course this application does not do much, so let's make this an actual application: 9 | // func main() { 10 | // app := cli.NewApp() 11 | // app.Name = "greet" 12 | // app.Usage = "say a greeting" 13 | // app.Action = func(c *cli.Context) { 14 | // println("Greetings") 15 | // } 16 | // 17 | // app.Run(os.Args) 18 | // } 19 | package cli 20 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/wsxiaoys/terminal/README.md: -------------------------------------------------------------------------------- 1 | ## Terminal ## 2 | Terminal is a simple golang package that provides basic terminal handling. 3 | 4 | Terminal wraps color/format functions provided by [ANSI escape code](http://en.wikipedia.org/wiki/ANSI_escape_code) 5 | 6 | ## Usage ## 7 | ```go 8 | package main 9 | 10 | import ( 11 | "github.com/wsxiaoys/terminal" 12 | "github.com/wsxiaoys/terminal/color" 13 | ) 14 | 15 | func main() { 16 | terminal.Stdout.Color("y"). 17 | Print("Hello world").Nl(). 18 | Reset(). 19 | Colorf("@{kW}Hello world\n") 20 | 21 | color.Print("@rHello world") 22 | } 23 | ``` 24 | Check the [godoc result](https://godoc.org/github.com/wsxiaoys/terminal) for more details. 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # anybar-go 2 | 3 | Small go app to send commands to [Anybar](https://github.com/tonsky/AnyBar). 4 | 5 | 6 | ## Building 7 | 8 | * Build artifact `./make.sh` 9 | * Copy to bin `cp builds/anybar-go.osx ~/bin/anybar-go` 10 | * Profit 11 | 12 | 13 | ## Development 14 | go get github.com/tools/godep 15 | godep restore 16 | godep run anybar-go 17 | 18 | 19 | ## Use 20 | 21 | ```shell 22 | NAME: 23 | anybar-go - Anybar CLI 24 | 25 | USAGE: 26 | anybar-go [global options] command [command options] [arguments...] 27 | 28 | VERSION: 29 | 0.0.1 30 | 31 | AUTHOR: 32 | John Dyer - 33 | 34 | COMMANDS: 35 | help, h Shows a list of commands or help for one command 36 | 37 | GLOBAL OPTIONS: 38 | --port, -p '1738' Port to connect to anybar [$ANYBAR_PORT] 39 | --address, -a 'localhost' Address to send message. 40 | --help, -h show help 41 | --version, -v print the version 42 | 43 | ``` 44 | 45 | 46 | ### Sending message 47 | 48 | #### Defaults 49 | 50 | ./anybar-go.osx green 51 | 52 | #### Setting port 53 | 54 | ./anybar-go.osx green -p 2000 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/codegangsta/cli/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2013 Jeremy Saenz 2 | All Rights Reserved. 3 | 4 | MIT LICENSE 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | this software and associated documentation files (the "Software"), to deal in 8 | the Software without restriction, including without limitation the rights to 9 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | the Software, and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 20 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/codegangsta/cli/command_test.go: -------------------------------------------------------------------------------- 1 | package cli_test 2 | 3 | import ( 4 | "flag" 5 | "testing" 6 | 7 | "github.com/johntdyer/anybar-go/Godeps/_workspace/src/github.com/codegangsta/cli" 8 | ) 9 | 10 | func TestCommandDoNotIgnoreFlags(t *testing.T) { 11 | app := cli.NewApp() 12 | set := flag.NewFlagSet("test", 0) 13 | test := []string{"blah", "blah", "-break"} 14 | set.Parse(test) 15 | 16 | c := cli.NewContext(app, set, set) 17 | 18 | command := cli.Command{ 19 | Name: "test-cmd", 20 | ShortName: "tc", 21 | Usage: "this is for testing", 22 | Description: "testing", 23 | Action: func(_ *cli.Context) {}, 24 | } 25 | err := command.Run(c) 26 | 27 | expect(t, err.Error(), "flag provided but not defined: -break") 28 | } 29 | 30 | func TestCommandIgnoreFlags(t *testing.T) { 31 | app := cli.NewApp() 32 | set := flag.NewFlagSet("test", 0) 33 | test := []string{"blah", "blah"} 34 | set.Parse(test) 35 | 36 | c := cli.NewContext(app, set, set) 37 | 38 | command := cli.Command{ 39 | Name: "test-cmd", 40 | ShortName: "tc", 41 | Usage: "this is for testing", 42 | Description: "testing", 43 | Action: func(_ *cli.Context) {}, 44 | SkipFlagParsing: true, 45 | } 46 | err := command.Run(c) 47 | 48 | expect(t, err, nil) 49 | } 50 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/wsxiaoys/terminal/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Meng Zhang. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /anybar.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/johntdyer/anybar-go/Godeps/_workspace/src/github.com/codegangsta/cli" 6 | "github.com/johntdyer/anybar-go/Godeps/_workspace/src/github.com/wsxiaoys/terminal" 7 | "log" 8 | "net" 9 | "os" 10 | ) 11 | 12 | func main() { 13 | 14 | app := cli.NewApp() 15 | 16 | app.Version = "0.0.1" 17 | app.Name = "anybar-go" 18 | app.Usage = "Anybar CLI" 19 | app.Author = "John Dyer" 20 | app.Email = "johntdyer@gmail.com" 21 | 22 | app.Flags = []cli.Flag{ 23 | 24 | cli.StringFlag{ 25 | Name: "port, p", 26 | Value: "1738", 27 | Usage: "Port to connect to anybar", 28 | EnvVar: "ANYBAR_PORT", 29 | }, 30 | 31 | cli.StringFlag{Name: "address, a", 32 | Value: "localhost", 33 | Usage: "Address to send message.", 34 | }, 35 | } 36 | 37 | app.Action = func(c *cli.Context) { 38 | msg := "" 39 | if len(c.Args()) > 0 { 40 | msg = c.Args()[0] 41 | addr := fmt.Sprintf("%s:%s", c.String("address"), c.String("port")) 42 | sendPacket(msg, addr) 43 | } else { 44 | 45 | terminal.Stdout.Color("r").Print("Message is required").Nl().Reset() 46 | 47 | } 48 | 49 | } 50 | 51 | app.Run(os.Args) 52 | 53 | } 54 | 55 | func sendPacket(msg string, addr string) { 56 | laddr, err := net.ResolveUDPAddr("udp", ":0") 57 | if err != nil { 58 | log.Fatal(err) 59 | } 60 | maddr, err := net.ResolveUDPAddr("udp4", addr) 61 | if err != nil { 62 | log.Fatal(err) 63 | } 64 | c, err := net.ListenUDP("udp4", laddr) 65 | if err != nil { 66 | log.Fatal(err) 67 | } 68 | defer c.Close() 69 | 70 | _, err = c.WriteToUDP([]byte(msg), maddr) 71 | if err != nil { 72 | log.Fatal(err) 73 | } 74 | 75 | terminal.Stdout.Color("b").Print(fmt.Sprintf("Sent '%s' successfully", msg)).Nl().Reset() 76 | 77 | } 78 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/wsxiaoys/terminal/terminal.go: -------------------------------------------------------------------------------- 1 | package terminal 2 | 3 | import ( 4 | "fmt" 5 | "github.com/johntdyer/anybar-go/Godeps/_workspace/src/github.com/wsxiaoys/terminal/color" 6 | "io" 7 | "log" 8 | "os" 9 | ) 10 | 11 | type TerminalWriter struct { 12 | io.Writer 13 | } 14 | 15 | var ( 16 | Stdout = &TerminalWriter{os.Stdout} 17 | Stderr = &TerminalWriter{os.Stderr} 18 | ) 19 | 20 | func (w *TerminalWriter) checkOutput(s string) { 21 | if _, err := io.WriteString(w, s); err != nil { 22 | log.Fatal("Write to %v failed.", w) 23 | } 24 | } 25 | 26 | func (w *TerminalWriter) Color(syntax string) *TerminalWriter { 27 | escapeCode := color.Colorize(syntax) 28 | w.checkOutput(escapeCode) 29 | return w 30 | } 31 | 32 | func (w *TerminalWriter) Reset() *TerminalWriter { 33 | w.checkOutput(color.ResetCode) 34 | return w 35 | } 36 | 37 | func (w *TerminalWriter) Print(a ...interface{}) *TerminalWriter { 38 | fmt.Fprint(w, a...) 39 | return w 40 | } 41 | 42 | func (w *TerminalWriter) Nl(a ...interface{}) *TerminalWriter { 43 | length := 1 44 | if len(a) > 0 { 45 | length = a[0].(int) 46 | } 47 | for i := 0; i < length; i++ { 48 | w.checkOutput("\n") 49 | } 50 | return w 51 | } 52 | 53 | func (w *TerminalWriter) Colorf(format string, a ...interface{}) *TerminalWriter { 54 | w.checkOutput(color.Sprintf(format, a...)) 55 | return w 56 | } 57 | 58 | func (w *TerminalWriter) Clear() *TerminalWriter { 59 | w.checkOutput("\033[2J") 60 | return w 61 | } 62 | 63 | func (w *TerminalWriter) ClearLine() *TerminalWriter { 64 | w.checkOutput("\033[2K") 65 | return w 66 | } 67 | 68 | func (w *TerminalWriter) Move(x, y int) *TerminalWriter { 69 | w.checkOutput(fmt.Sprintf("\033[%d;%dH", x, y)) 70 | return w 71 | } 72 | 73 | func (w *TerminalWriter) Up(x int) *TerminalWriter { 74 | w.checkOutput(fmt.Sprintf("\033[%dA", x)) 75 | return w 76 | } 77 | 78 | func (w *TerminalWriter) Down(x int) *TerminalWriter { 79 | w.checkOutput(fmt.Sprintf("\033[%dB", x)) 80 | return w 81 | } 82 | 83 | func (w *TerminalWriter) Right(x int) *TerminalWriter { 84 | w.checkOutput(fmt.Sprintf("\033[%dC", x)) 85 | return w 86 | } 87 | 88 | func (w *TerminalWriter) Left(x int) *TerminalWriter { 89 | w.checkOutput(fmt.Sprintf("\033[%dD", x)) 90 | return w 91 | } 92 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/codegangsta/cli/cli_test.go: -------------------------------------------------------------------------------- 1 | package cli_test 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/johntdyer/anybar-go/Godeps/_workspace/src/github.com/codegangsta/cli" 7 | ) 8 | 9 | func Example() { 10 | app := cli.NewApp() 11 | app.Name = "todo" 12 | app.Usage = "task list on the command line" 13 | app.Commands = []cli.Command{ 14 | { 15 | Name: "add", 16 | ShortName: "a", 17 | Usage: "add a task to the list", 18 | Action: func(c *cli.Context) { 19 | println("added task: ", c.Args().First()) 20 | }, 21 | }, 22 | { 23 | Name: "complete", 24 | ShortName: "c", 25 | Usage: "complete a task on the list", 26 | Action: func(c *cli.Context) { 27 | println("completed task: ", c.Args().First()) 28 | }, 29 | }, 30 | } 31 | 32 | app.Run(os.Args) 33 | } 34 | 35 | func ExampleSubcommand() { 36 | app := cli.NewApp() 37 | app.Name = "say" 38 | app.Commands = []cli.Command{ 39 | { 40 | Name: "hello", 41 | ShortName: "hi", 42 | Usage: "use it to see a description", 43 | Description: "This is how we describe hello the function", 44 | Subcommands: []cli.Command{ 45 | { 46 | Name: "english", 47 | ShortName: "en", 48 | Usage: "sends a greeting in english", 49 | Description: "greets someone in english", 50 | Flags: []cli.Flag{ 51 | cli.StringFlag{ 52 | Name: "name", 53 | Value: "Bob", 54 | Usage: "Name of the person to greet", 55 | }, 56 | }, 57 | Action: func(c *cli.Context) { 58 | println("Hello, ", c.String("name")) 59 | }, 60 | }, { 61 | Name: "spanish", 62 | ShortName: "sp", 63 | Usage: "sends a greeting in spanish", 64 | Flags: []cli.Flag{ 65 | cli.StringFlag{ 66 | Name: "surname", 67 | Value: "Jones", 68 | Usage: "Surname of the person to greet", 69 | }, 70 | }, 71 | Action: func(c *cli.Context) { 72 | println("Hola, ", c.String("surname")) 73 | }, 74 | }, { 75 | Name: "french", 76 | ShortName: "fr", 77 | Usage: "sends a greeting in french", 78 | Flags: []cli.Flag{ 79 | cli.StringFlag{ 80 | Name: "nickname", 81 | Value: "Stevie", 82 | Usage: "Nickname of the person to greet", 83 | }, 84 | }, 85 | Action: func(c *cli.Context) { 86 | println("Bonjour, ", c.String("nickname")) 87 | }, 88 | }, 89 | }, 90 | }, { 91 | Name: "bye", 92 | Usage: "says goodbye", 93 | Action: func(c *cli.Context) { 94 | println("bye") 95 | }, 96 | }, 97 | } 98 | 99 | app.Run(os.Args) 100 | } 101 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/codegangsta/cli/context_test.go: -------------------------------------------------------------------------------- 1 | package cli_test 2 | 3 | import ( 4 | "flag" 5 | "testing" 6 | "time" 7 | 8 | "github.com/johntdyer/anybar-go/Godeps/_workspace/src/github.com/codegangsta/cli" 9 | ) 10 | 11 | func TestNewContext(t *testing.T) { 12 | set := flag.NewFlagSet("test", 0) 13 | set.Int("myflag", 12, "doc") 14 | globalSet := flag.NewFlagSet("test", 0) 15 | globalSet.Int("myflag", 42, "doc") 16 | command := cli.Command{Name: "mycommand"} 17 | c := cli.NewContext(nil, set, globalSet) 18 | c.Command = command 19 | expect(t, c.Int("myflag"), 12) 20 | expect(t, c.GlobalInt("myflag"), 42) 21 | expect(t, c.Command.Name, "mycommand") 22 | } 23 | 24 | func TestContext_Int(t *testing.T) { 25 | set := flag.NewFlagSet("test", 0) 26 | set.Int("myflag", 12, "doc") 27 | c := cli.NewContext(nil, set, set) 28 | expect(t, c.Int("myflag"), 12) 29 | } 30 | 31 | func TestContext_Duration(t *testing.T) { 32 | set := flag.NewFlagSet("test", 0) 33 | set.Duration("myflag", time.Duration(12*time.Second), "doc") 34 | c := cli.NewContext(nil, set, set) 35 | expect(t, c.Duration("myflag"), time.Duration(12*time.Second)) 36 | } 37 | 38 | func TestContext_String(t *testing.T) { 39 | set := flag.NewFlagSet("test", 0) 40 | set.String("myflag", "hello world", "doc") 41 | c := cli.NewContext(nil, set, set) 42 | expect(t, c.String("myflag"), "hello world") 43 | } 44 | 45 | func TestContext_Bool(t *testing.T) { 46 | set := flag.NewFlagSet("test", 0) 47 | set.Bool("myflag", false, "doc") 48 | c := cli.NewContext(nil, set, set) 49 | expect(t, c.Bool("myflag"), false) 50 | } 51 | 52 | func TestContext_BoolT(t *testing.T) { 53 | set := flag.NewFlagSet("test", 0) 54 | set.Bool("myflag", true, "doc") 55 | c := cli.NewContext(nil, set, set) 56 | expect(t, c.BoolT("myflag"), true) 57 | } 58 | 59 | func TestContext_Args(t *testing.T) { 60 | set := flag.NewFlagSet("test", 0) 61 | set.Bool("myflag", false, "doc") 62 | c := cli.NewContext(nil, set, set) 63 | set.Parse([]string{"--myflag", "bat", "baz"}) 64 | expect(t, len(c.Args()), 2) 65 | expect(t, c.Bool("myflag"), true) 66 | } 67 | 68 | func TestContext_IsSet(t *testing.T) { 69 | set := flag.NewFlagSet("test", 0) 70 | set.Bool("myflag", false, "doc") 71 | set.String("otherflag", "hello world", "doc") 72 | globalSet := flag.NewFlagSet("test", 0) 73 | globalSet.Bool("myflagGlobal", true, "doc") 74 | c := cli.NewContext(nil, set, globalSet) 75 | set.Parse([]string{"--myflag", "bat", "baz"}) 76 | globalSet.Parse([]string{"--myflagGlobal", "bat", "baz"}) 77 | expect(t, c.IsSet("myflag"), true) 78 | expect(t, c.IsSet("otherflag"), false) 79 | expect(t, c.IsSet("bogusflag"), false) 80 | expect(t, c.IsSet("myflagGlobal"), false) 81 | } 82 | 83 | func TestContext_GlobalIsSet(t *testing.T) { 84 | set := flag.NewFlagSet("test", 0) 85 | set.Bool("myflag", false, "doc") 86 | set.String("otherflag", "hello world", "doc") 87 | globalSet := flag.NewFlagSet("test", 0) 88 | globalSet.Bool("myflagGlobal", true, "doc") 89 | globalSet.Bool("myflagGlobalUnset", true, "doc") 90 | c := cli.NewContext(nil, set, globalSet) 91 | set.Parse([]string{"--myflag", "bat", "baz"}) 92 | globalSet.Parse([]string{"--myflagGlobal", "bat", "baz"}) 93 | expect(t, c.GlobalIsSet("myflag"), false) 94 | expect(t, c.GlobalIsSet("otherflag"), false) 95 | expect(t, c.GlobalIsSet("bogusflag"), false) 96 | expect(t, c.GlobalIsSet("myflagGlobal"), true) 97 | expect(t, c.GlobalIsSet("myflagGlobalUnset"), false) 98 | expect(t, c.GlobalIsSet("bogusGlobal"), false) 99 | } 100 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/codegangsta/cli/command.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "strings" 7 | ) 8 | 9 | // Command is a subcommand for a cli.App. 10 | type Command struct { 11 | // The name of the command 12 | Name string 13 | // short name of the command. Typically one character 14 | ShortName string 15 | // A short description of the usage of this command 16 | Usage string 17 | // A longer explanation of how the command works 18 | Description string 19 | // The function to call when checking for bash command completions 20 | BashComplete func(context *Context) 21 | // An action to execute before any sub-subcommands are run, but after the context is ready 22 | // If a non-nil error is returned, no sub-subcommands are run 23 | Before func(context *Context) error 24 | // The function to call when this command is invoked 25 | Action func(context *Context) 26 | // List of child commands 27 | Subcommands []Command 28 | // List of flags to parse 29 | Flags []Flag 30 | // Treat all flags as normal arguments if true 31 | SkipFlagParsing bool 32 | // Boolean to hide built-in help command 33 | HideHelp bool 34 | } 35 | 36 | // Invokes the command given the context, parses ctx.Args() to generate command-specific flags 37 | func (c Command) Run(ctx *Context) error { 38 | 39 | if len(c.Subcommands) > 0 || c.Before != nil { 40 | return c.startApp(ctx) 41 | } 42 | 43 | if !c.HideHelp { 44 | // append help to flags 45 | c.Flags = append( 46 | c.Flags, 47 | HelpFlag, 48 | ) 49 | } 50 | 51 | if ctx.App.EnableBashCompletion { 52 | c.Flags = append(c.Flags, BashCompletionFlag) 53 | } 54 | 55 | set := flagSet(c.Name, c.Flags) 56 | set.SetOutput(ioutil.Discard) 57 | 58 | firstFlagIndex := -1 59 | for index, arg := range ctx.Args() { 60 | if strings.HasPrefix(arg, "-") { 61 | firstFlagIndex = index 62 | break 63 | } 64 | } 65 | 66 | var err error 67 | if firstFlagIndex > -1 && !c.SkipFlagParsing { 68 | args := ctx.Args() 69 | regularArgs := args[1:firstFlagIndex] 70 | flagArgs := args[firstFlagIndex:] 71 | err = set.Parse(append(flagArgs, regularArgs...)) 72 | } else { 73 | err = set.Parse(ctx.Args().Tail()) 74 | } 75 | 76 | if err != nil { 77 | fmt.Fprint(ctx.App.Writer, "Incorrect Usage.\n\n") 78 | ShowCommandHelp(ctx, c.Name) 79 | fmt.Fprintln(ctx.App.Writer) 80 | return err 81 | } 82 | 83 | nerr := normalizeFlags(c.Flags, set) 84 | if nerr != nil { 85 | fmt.Fprintln(ctx.App.Writer, nerr) 86 | fmt.Fprintln(ctx.App.Writer) 87 | ShowCommandHelp(ctx, c.Name) 88 | fmt.Fprintln(ctx.App.Writer) 89 | return nerr 90 | } 91 | context := NewContext(ctx.App, set, ctx.globalSet) 92 | 93 | if checkCommandCompletions(context, c.Name) { 94 | return nil 95 | } 96 | 97 | if checkCommandHelp(context, c.Name) { 98 | return nil 99 | } 100 | context.Command = c 101 | c.Action(context) 102 | return nil 103 | } 104 | 105 | // Returns true if Command.Name or Command.ShortName matches given name 106 | func (c Command) HasName(name string) bool { 107 | return c.Name == name || c.ShortName == name 108 | } 109 | 110 | func (c Command) startApp(ctx *Context) error { 111 | app := NewApp() 112 | 113 | // set the name and usage 114 | app.Name = fmt.Sprintf("%s %s", ctx.App.Name, c.Name) 115 | if c.Description != "" { 116 | app.Usage = c.Description 117 | } else { 118 | app.Usage = c.Usage 119 | } 120 | 121 | // set CommandNotFound 122 | app.CommandNotFound = ctx.App.CommandNotFound 123 | 124 | // set the flags and commands 125 | app.Commands = c.Subcommands 126 | app.Flags = c.Flags 127 | app.HideHelp = c.HideHelp 128 | 129 | // bash completion 130 | app.EnableBashCompletion = ctx.App.EnableBashCompletion 131 | if c.BashComplete != nil { 132 | app.BashComplete = c.BashComplete 133 | } 134 | 135 | // set the actions 136 | app.Before = c.Before 137 | if c.Action != nil { 138 | app.Action = c.Action 139 | } else { 140 | app.Action = helpSubcommand.Action 141 | } 142 | 143 | return app.RunAsSubcommand(ctx) 144 | } 145 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/codegangsta/cli/help.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import "fmt" 4 | 5 | // The text template for the Default help topic. 6 | // cli.go uses text/template to render templates. You can 7 | // render custom help text by setting this variable. 8 | var AppHelpTemplate = `NAME: 9 | {{.Name}} - {{.Usage}} 10 | 11 | USAGE: 12 | {{.Name}} {{if .Flags}}[global options] {{end}}command{{if .Flags}} [command options]{{end}} [arguments...] 13 | 14 | VERSION: 15 | {{.Version}}{{if or .Author .Email}} 16 | 17 | AUTHOR:{{if .Author}} 18 | {{.Author}}{{if .Email}} - <{{.Email}}>{{end}}{{else}} 19 | {{.Email}}{{end}}{{end}} 20 | 21 | COMMANDS: 22 | {{range .Commands}}{{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}} 23 | {{end}}{{if .Flags}} 24 | GLOBAL OPTIONS: 25 | {{range .Flags}}{{.}} 26 | {{end}}{{end}} 27 | ` 28 | 29 | // The text template for the command help topic. 30 | // cli.go uses text/template to render templates. You can 31 | // render custom help text by setting this variable. 32 | var CommandHelpTemplate = `NAME: 33 | {{.Name}} - {{.Usage}} 34 | 35 | USAGE: 36 | command {{.Name}}{{if .Flags}} [command options]{{end}} [arguments...]{{if .Description}} 37 | 38 | DESCRIPTION: 39 | {{.Description}}{{end}}{{if .Flags}} 40 | 41 | OPTIONS: 42 | {{range .Flags}}{{.}} 43 | {{end}}{{ end }} 44 | ` 45 | 46 | // The text template for the subcommand help topic. 47 | // cli.go uses text/template to render templates. You can 48 | // render custom help text by setting this variable. 49 | var SubcommandHelpTemplate = `NAME: 50 | {{.Name}} - {{.Usage}} 51 | 52 | USAGE: 53 | {{.Name}} command{{if .Flags}} [command options]{{end}} [arguments...] 54 | 55 | COMMANDS: 56 | {{range .Commands}}{{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}} 57 | {{end}}{{if .Flags}} 58 | OPTIONS: 59 | {{range .Flags}}{{.}} 60 | {{end}}{{end}} 61 | ` 62 | 63 | var helpCommand = Command{ 64 | Name: "help", 65 | ShortName: "h", 66 | Usage: "Shows a list of commands or help for one command", 67 | Action: func(c *Context) { 68 | args := c.Args() 69 | if args.Present() { 70 | ShowCommandHelp(c, args.First()) 71 | } else { 72 | ShowAppHelp(c) 73 | } 74 | }, 75 | } 76 | 77 | var helpSubcommand = Command{ 78 | Name: "help", 79 | ShortName: "h", 80 | Usage: "Shows a list of commands or help for one command", 81 | Action: func(c *Context) { 82 | args := c.Args() 83 | if args.Present() { 84 | ShowCommandHelp(c, args.First()) 85 | } else { 86 | ShowSubcommandHelp(c) 87 | } 88 | }, 89 | } 90 | 91 | // Prints help for the App 92 | type helpPrinter func(templ string, data interface{}) 93 | 94 | var HelpPrinter helpPrinter = nil 95 | 96 | // Prints version for the App 97 | var VersionPrinter = printVersion 98 | 99 | func ShowAppHelp(c *Context) { 100 | HelpPrinter(AppHelpTemplate, c.App) 101 | } 102 | 103 | // Prints the list of subcommands as the default app completion method 104 | func DefaultAppComplete(c *Context) { 105 | for _, command := range c.App.Commands { 106 | fmt.Fprintln(c.App.Writer, command.Name) 107 | if command.ShortName != "" { 108 | fmt.Fprintln(c.App.Writer, command.ShortName) 109 | } 110 | } 111 | } 112 | 113 | // Prints help for the given command 114 | func ShowCommandHelp(c *Context, command string) { 115 | for _, c := range c.App.Commands { 116 | if c.HasName(command) { 117 | HelpPrinter(CommandHelpTemplate, c) 118 | return 119 | } 120 | } 121 | 122 | if c.App.CommandNotFound != nil { 123 | c.App.CommandNotFound(c, command) 124 | } else { 125 | fmt.Fprintf(c.App.Writer, "No help topic for '%v'\n", command) 126 | } 127 | } 128 | 129 | // Prints help for the given subcommand 130 | func ShowSubcommandHelp(c *Context) { 131 | ShowCommandHelp(c, c.Command.Name) 132 | } 133 | 134 | // Prints the version number of the App 135 | func ShowVersion(c *Context) { 136 | VersionPrinter(c) 137 | } 138 | 139 | func printVersion(c *Context) { 140 | fmt.Fprintf(c.App.Writer, "%v version %v\n", c.App.Name, c.App.Version) 141 | } 142 | 143 | // Prints the lists of commands within a given context 144 | func ShowCompletions(c *Context) { 145 | a := c.App 146 | if a != nil && a.BashComplete != nil { 147 | a.BashComplete(c) 148 | } 149 | } 150 | 151 | // Prints the custom completions for a given command 152 | func ShowCommandCompletions(ctx *Context, command string) { 153 | c := ctx.App.Command(command) 154 | if c != nil && c.BashComplete != nil { 155 | c.BashComplete(ctx) 156 | } 157 | } 158 | 159 | func checkVersion(c *Context) bool { 160 | if c.GlobalBool("version") { 161 | ShowVersion(c) 162 | return true 163 | } 164 | 165 | return false 166 | } 167 | 168 | func checkHelp(c *Context) bool { 169 | if c.GlobalBool("h") || c.GlobalBool("help") { 170 | ShowAppHelp(c) 171 | return true 172 | } 173 | 174 | return false 175 | } 176 | 177 | func checkCommandHelp(c *Context, name string) bool { 178 | if c.Bool("h") || c.Bool("help") { 179 | ShowCommandHelp(c, name) 180 | return true 181 | } 182 | 183 | return false 184 | } 185 | 186 | func checkSubcommandHelp(c *Context) bool { 187 | if c.GlobalBool("h") || c.GlobalBool("help") { 188 | ShowSubcommandHelp(c) 189 | return true 190 | } 191 | 192 | return false 193 | } 194 | 195 | func checkCompletions(c *Context) bool { 196 | if (c.GlobalBool(BashCompletionFlag.Name) || c.Bool(BashCompletionFlag.Name)) && c.App.EnableBashCompletion { 197 | ShowCompletions(c) 198 | return true 199 | } 200 | 201 | return false 202 | } 203 | 204 | func checkCommandCompletions(c *Context, name string) bool { 205 | if c.Bool(BashCompletionFlag.Name) && c.App.EnableBashCompletion { 206 | ShowCommandCompletions(c, name) 207 | return true 208 | } 209 | 210 | return false 211 | } 212 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/wsxiaoys/terminal/color/color.go: -------------------------------------------------------------------------------- 1 | // The colors package provide a simple way to bring colorful charcaters to terminal interface. 2 | // 3 | // This example will output the text with a Blue foreground and a Black background 4 | // color.Println("@{bK}Example Text") 5 | // 6 | // This one will output the text with a red foreground 7 | // color.Println("@rExample Text") 8 | // 9 | // This one will escape the @ 10 | // color.Println("@@") 11 | // 12 | // Full color syntax code 13 | // @{rgbcmykwRGBCMYKW} foreground/background color 14 | // r/R: Red 15 | // g/G: Green 16 | // b/B: Blue 17 | // c/C: Cyan 18 | // m/M: Magenta 19 | // y/Y: Yellow 20 | // k/K: Black 21 | // w/W: White 22 | // @{|} Reset format style 23 | // @{!./_} Bold / Dim / Italic / Underline 24 | // @{^&} Blink / Fast blink 25 | // @{?} Reverse the foreground and background color 26 | // @{-} Hide the text 27 | // Note some of the functions are not widely supported, like "Fast blink" and "Italic". 28 | package color 29 | 30 | import ( 31 | "bytes" 32 | "errors" 33 | "fmt" 34 | "io" 35 | "log" 36 | ) 37 | 38 | const ( 39 | EscapeChar = '@' // Escape character for color syntax 40 | ResetCode = "\033[0m" // Short for reset to default style 41 | ) 42 | 43 | // Mapping from character to concrete escape code. 44 | var codeMap = map[int]int{ 45 | '|': 0, 46 | '!': 1, 47 | '.': 2, 48 | '/': 3, 49 | '_': 4, 50 | '^': 5, 51 | '&': 6, 52 | '?': 7, 53 | '-': 8, 54 | 55 | 'k': 30, 56 | 'r': 31, 57 | 'g': 32, 58 | 'y': 33, 59 | 'b': 34, 60 | 'm': 35, 61 | 'c': 36, 62 | 'w': 37, 63 | 'd': 39, 64 | 65 | 'K': 40, 66 | 'R': 41, 67 | 'G': 42, 68 | 'Y': 43, 69 | 'B': 44, 70 | 'M': 45, 71 | 'C': 46, 72 | 'W': 47, 73 | 'D': 49, 74 | } 75 | 76 | // Compile color syntax string like "rG" to escape code. 77 | func Colorize(x string) string { 78 | attr := 0 79 | fg := 39 80 | bg := 49 81 | 82 | for _, key := range x { 83 | c, ok := codeMap[int(key)] 84 | switch { 85 | case !ok: 86 | log.Printf("Wrong color syntax: %c", key) 87 | case 0 <= c && c <= 8: 88 | attr = c 89 | case 30 <= c && c <= 37: 90 | fg = c 91 | case 40 <= c && c <= 47: 92 | bg = c 93 | } 94 | } 95 | return fmt.Sprintf("\033[%d;%d;%dm", attr, fg, bg) 96 | } 97 | 98 | // Handle state after meeting one '@' 99 | func compileColorSyntax(input, output *bytes.Buffer) { 100 | i, _, err := input.ReadRune() 101 | if err != nil { 102 | // EOF got 103 | log.Print("Parse failed on color syntax") 104 | return 105 | } 106 | 107 | switch i { 108 | default: 109 | output.WriteString(Colorize(string(i))) 110 | case '{': 111 | color := bytes.NewBufferString("") 112 | for { 113 | i, _, err := input.ReadRune() 114 | if err != nil { 115 | log.Print("Parse failed on color syntax") 116 | break 117 | } 118 | if i == '}' { 119 | break 120 | } 121 | color.WriteRune(i) 122 | } 123 | output.WriteString(Colorize(color.String())) 124 | case EscapeChar: 125 | output.WriteRune(EscapeChar) 126 | } 127 | } 128 | 129 | // Compile the string and replace color syntax with concrete escape code. 130 | func compile(x string) string { 131 | if x == "" { 132 | return "" 133 | } 134 | 135 | input := bytes.NewBufferString(x) 136 | output := bytes.NewBufferString("") 137 | 138 | for { 139 | i, _, err := input.ReadRune() 140 | if err != nil { 141 | break 142 | } 143 | switch i { 144 | default: 145 | output.WriteRune(i) 146 | case EscapeChar: 147 | compileColorSyntax(input, output) 148 | } 149 | } 150 | return output.String() 151 | } 152 | 153 | // Compile multiple values, only do compiling on string type. 154 | func compileValues(a *[]interface{}) { 155 | for i, x := range *a { 156 | if str, ok := x.(string); ok { 157 | (*a)[i] = compile(str) 158 | } 159 | } 160 | } 161 | 162 | // Similar to fmt.Print, will reset the color at the end. 163 | func Print(a ...interface{}) (int, error) { 164 | a = append(a, ResetCode) 165 | compileValues(&a) 166 | return fmt.Print(a...) 167 | } 168 | 169 | // Similar to fmt.Println, will reset the color at the end. 170 | func Println(a ...interface{}) (int, error) { 171 | a = append(a, ResetCode) 172 | compileValues(&a) 173 | return fmt.Println(a...) 174 | } 175 | 176 | // Similar to fmt.Printf, will reset the color at the end. 177 | func Printf(format string, a ...interface{}) (int, error) { 178 | format += ResetCode 179 | format = compile(format) 180 | return fmt.Printf(format, a...) 181 | } 182 | 183 | // Similar to fmt.Fprint, will reset the color at the end. 184 | func Fprint(w io.Writer, a ...interface{}) (int, error) { 185 | a = append(a, ResetCode) 186 | compileValues(&a) 187 | return fmt.Fprint(w, a...) 188 | } 189 | 190 | // Similar to fmt.Fprintln, will reset the color at the end. 191 | func Fprintln(w io.Writer, a ...interface{}) (int, error) { 192 | a = append(a, ResetCode) 193 | compileValues(&a) 194 | return fmt.Fprintln(w, a...) 195 | } 196 | 197 | // Similar to fmt.Fprintf, will reset the color at the end. 198 | func Fprintf(w io.Writer, format string, a ...interface{}) (int, error) { 199 | format += ResetCode 200 | format = compile(format) 201 | return fmt.Fprintf(w, format, a...) 202 | } 203 | 204 | // Similar to fmt.Sprint, will reset the color at the end. 205 | func Sprint(a ...interface{}) string { 206 | a = append(a, ResetCode) 207 | compileValues(&a) 208 | return fmt.Sprint(a...) 209 | } 210 | 211 | // Similar to fmt.Sprintf, will reset the color at the end. 212 | func Sprintf(format string, a ...interface{}) string { 213 | format += ResetCode 214 | format = compile(format) 215 | return fmt.Sprintf(format, a...) 216 | } 217 | 218 | // Similar to fmt.Errorf, will reset the color at the end. 219 | func Errorf(format string, a ...interface{}) error { 220 | return errors.New(Sprintf(format, a...)) 221 | } 222 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/codegangsta/cli/app.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "io/ioutil" 7 | "os" 8 | "text/tabwriter" 9 | "text/template" 10 | "time" 11 | ) 12 | 13 | // App is the main structure of a cli application. It is recomended that 14 | // and app be created with the cli.NewApp() function 15 | type App struct { 16 | // The name of the program. Defaults to os.Args[0] 17 | Name string 18 | // Description of the program. 19 | Usage string 20 | // Version of the program 21 | Version string 22 | // List of commands to execute 23 | Commands []Command 24 | // List of flags to parse 25 | Flags []Flag 26 | // Boolean to enable bash completion commands 27 | EnableBashCompletion bool 28 | // Boolean to hide built-in help command 29 | HideHelp bool 30 | // Boolean to hide built-in version flag 31 | HideVersion bool 32 | // An action to execute when the bash-completion flag is set 33 | BashComplete func(context *Context) 34 | // An action to execute before any subcommands are run, but after the context is ready 35 | // If a non-nil error is returned, no subcommands are run 36 | Before func(context *Context) error 37 | // The action to execute when no subcommands are specified 38 | Action func(context *Context) 39 | // Execute this function if the proper command cannot be found 40 | CommandNotFound func(context *Context, command string) 41 | // Compilation date 42 | Compiled time.Time 43 | // Author 44 | Author string 45 | // Author e-mail 46 | Email string 47 | // Writer writer to write output to 48 | Writer io.Writer 49 | } 50 | 51 | // Tries to find out when this binary was compiled. 52 | // Returns the current time if it fails to find it. 53 | func compileTime() time.Time { 54 | info, err := os.Stat(os.Args[0]) 55 | if err != nil { 56 | return time.Now() 57 | } 58 | return info.ModTime() 59 | } 60 | 61 | // Creates a new cli Application with some reasonable defaults for Name, Usage, Version and Action. 62 | func NewApp() *App { 63 | return &App{ 64 | Name: os.Args[0], 65 | Usage: "A new cli application", 66 | Version: "0.0.0", 67 | BashComplete: DefaultAppComplete, 68 | Action: helpCommand.Action, 69 | Compiled: compileTime(), 70 | Author: "Author", 71 | Email: "unknown@email", 72 | Writer: os.Stdout, 73 | } 74 | } 75 | 76 | // Entry point to the cli app. Parses the arguments slice and routes to the proper flag/args combination 77 | func (a *App) Run(arguments []string) error { 78 | if HelpPrinter == nil { 79 | defer func() { 80 | HelpPrinter = nil 81 | }() 82 | 83 | HelpPrinter = func(templ string, data interface{}) { 84 | w := tabwriter.NewWriter(a.Writer, 0, 8, 1, '\t', 0) 85 | t := template.Must(template.New("help").Parse(templ)) 86 | err := t.Execute(w, data) 87 | if err != nil { 88 | panic(err) 89 | } 90 | w.Flush() 91 | } 92 | } 93 | 94 | // append help to commands 95 | if a.Command(helpCommand.Name) == nil && !a.HideHelp { 96 | a.Commands = append(a.Commands, helpCommand) 97 | a.appendFlag(HelpFlag) 98 | } 99 | 100 | //append version/help flags 101 | if a.EnableBashCompletion { 102 | a.appendFlag(BashCompletionFlag) 103 | } 104 | 105 | if !a.HideVersion { 106 | a.appendFlag(VersionFlag) 107 | } 108 | 109 | // parse flags 110 | set := flagSet(a.Name, a.Flags) 111 | set.SetOutput(ioutil.Discard) 112 | err := set.Parse(arguments[1:]) 113 | nerr := normalizeFlags(a.Flags, set) 114 | if nerr != nil { 115 | fmt.Fprintln(a.Writer, nerr) 116 | context := NewContext(a, set, set) 117 | ShowAppHelp(context) 118 | fmt.Fprintln(a.Writer) 119 | return nerr 120 | } 121 | context := NewContext(a, set, set) 122 | 123 | if err != nil { 124 | fmt.Fprintf(a.Writer, "Incorrect Usage.\n\n") 125 | ShowAppHelp(context) 126 | fmt.Fprintln(a.Writer) 127 | return err 128 | } 129 | 130 | if checkCompletions(context) { 131 | return nil 132 | } 133 | 134 | if checkHelp(context) { 135 | return nil 136 | } 137 | 138 | if checkVersion(context) { 139 | return nil 140 | } 141 | 142 | if a.Before != nil { 143 | err := a.Before(context) 144 | if err != nil { 145 | return err 146 | } 147 | } 148 | 149 | args := context.Args() 150 | if args.Present() { 151 | name := args.First() 152 | c := a.Command(name) 153 | if c != nil { 154 | return c.Run(context) 155 | } 156 | } 157 | 158 | // Run default Action 159 | a.Action(context) 160 | return nil 161 | } 162 | 163 | // Another entry point to the cli app, takes care of passing arguments and error handling 164 | func (a *App) RunAndExitOnError() { 165 | if err := a.Run(os.Args); err != nil { 166 | fmt.Fprintln(os.Stderr, err) 167 | os.Exit(1) 168 | } 169 | } 170 | 171 | // Invokes the subcommand given the context, parses ctx.Args() to generate command-specific flags 172 | func (a *App) RunAsSubcommand(ctx *Context) error { 173 | // append help to commands 174 | if len(a.Commands) > 0 { 175 | if a.Command(helpCommand.Name) == nil && !a.HideHelp { 176 | a.Commands = append(a.Commands, helpCommand) 177 | a.appendFlag(HelpFlag) 178 | } 179 | } 180 | 181 | // append flags 182 | if a.EnableBashCompletion { 183 | a.appendFlag(BashCompletionFlag) 184 | } 185 | 186 | // parse flags 187 | set := flagSet(a.Name, a.Flags) 188 | set.SetOutput(ioutil.Discard) 189 | err := set.Parse(ctx.Args().Tail()) 190 | nerr := normalizeFlags(a.Flags, set) 191 | context := NewContext(a, set, ctx.globalSet) 192 | 193 | if nerr != nil { 194 | fmt.Fprintln(a.Writer, nerr) 195 | if len(a.Commands) > 0 { 196 | ShowSubcommandHelp(context) 197 | } else { 198 | ShowCommandHelp(ctx, context.Args().First()) 199 | } 200 | fmt.Fprintln(a.Writer) 201 | return nerr 202 | } 203 | 204 | if err != nil { 205 | fmt.Fprintf(a.Writer, "Incorrect Usage.\n\n") 206 | ShowSubcommandHelp(context) 207 | return err 208 | } 209 | 210 | if checkCompletions(context) { 211 | return nil 212 | } 213 | 214 | if len(a.Commands) > 0 { 215 | if checkSubcommandHelp(context) { 216 | return nil 217 | } 218 | } else { 219 | if checkCommandHelp(ctx, context.Args().First()) { 220 | return nil 221 | } 222 | } 223 | 224 | if a.Before != nil { 225 | err := a.Before(context) 226 | if err != nil { 227 | return err 228 | } 229 | } 230 | 231 | args := context.Args() 232 | if args.Present() { 233 | name := args.First() 234 | c := a.Command(name) 235 | if c != nil { 236 | return c.Run(context) 237 | } 238 | } 239 | 240 | // Run default Action 241 | if len(a.Commands) > 0 { 242 | a.Action(context) 243 | } else { 244 | a.Action(ctx) 245 | } 246 | 247 | return nil 248 | } 249 | 250 | // Returns the named command on App. Returns nil if the command does not exist 251 | func (a *App) Command(name string) *Command { 252 | for _, c := range a.Commands { 253 | if c.HasName(name) { 254 | return &c 255 | } 256 | } 257 | 258 | return nil 259 | } 260 | 261 | func (a *App) hasFlag(flag Flag) bool { 262 | for _, f := range a.Flags { 263 | if flag == f { 264 | return true 265 | } 266 | } 267 | 268 | return false 269 | } 270 | 271 | func (a *App) appendFlag(flag Flag) { 272 | if !a.hasFlag(flag) { 273 | a.Flags = append(a.Flags, flag) 274 | } 275 | } 276 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/codegangsta/cli/README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/codegangsta/cli.png?branch=master)](https://travis-ci.org/codegangsta/cli) 2 | 3 | # cli.go 4 | 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. 5 | 6 | You can view the API docs here: 7 | http://godoc.org/github.com/codegangsta/cli 8 | 9 | ## Overview 10 | 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. 11 | 12 | **This is where cli.go comes into play.** cli.go makes command line programming fun, organized, and expressive! 13 | 14 | ## Installation 15 | Make sure you have a working Go environment (go 1.1 is *required*). [See the install instructions](http://golang.org/doc/install.html). 16 | 17 | To install `cli.go`, simply run: 18 | ``` 19 | $ go get github.com/codegangsta/cli 20 | ``` 21 | 22 | Make sure your `PATH` includes to the `$GOPATH/bin` directory so your commands can be easily used: 23 | ``` 24 | export PATH=$PATH:$GOPATH/bin 25 | ``` 26 | 27 | ## Getting Started 28 | 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()`. 29 | 30 | ``` go 31 | package main 32 | 33 | import ( 34 | "os" 35 | "github.com/codegangsta/cli" 36 | ) 37 | 38 | func main() { 39 | cli.NewApp().Run(os.Args) 40 | } 41 | ``` 42 | 43 | This app will run and show help text, but is not very useful. Let's give an action to execute and some help documentation: 44 | 45 | ``` go 46 | package main 47 | 48 | import ( 49 | "os" 50 | "github.com/codegangsta/cli" 51 | ) 52 | 53 | func main() { 54 | app := cli.NewApp() 55 | app.Name = "boom" 56 | app.Usage = "make an explosive entrance" 57 | app.Action = func(c *cli.Context) { 58 | println("boom! I say!") 59 | } 60 | 61 | app.Run(os.Args) 62 | } 63 | ``` 64 | 65 | Running this already gives you a ton of functionality, plus support for things like subcommands and flags, which are covered below. 66 | 67 | ## Example 68 | 69 | 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! 70 | 71 | Start by creating a directory named `greet`, and within it, add a file, `greet.go` with the following code in it: 72 | 73 | ``` go 74 | package main 75 | 76 | import ( 77 | "os" 78 | "github.com/codegangsta/cli" 79 | ) 80 | 81 | func main() { 82 | app := cli.NewApp() 83 | app.Name = "greet" 84 | app.Usage = "fight the loneliness!" 85 | app.Action = func(c *cli.Context) { 86 | println("Hello friend!") 87 | } 88 | 89 | app.Run(os.Args) 90 | } 91 | ``` 92 | 93 | Install our command to the `$GOPATH/bin` directory: 94 | 95 | ``` 96 | $ go install 97 | ``` 98 | 99 | Finally run our new command: 100 | 101 | ``` 102 | $ greet 103 | Hello friend! 104 | ``` 105 | 106 | cli.go also generates some bitchass help text: 107 | ``` 108 | $ greet help 109 | NAME: 110 | greet - fight the loneliness! 111 | 112 | USAGE: 113 | greet [global options] command [command options] [arguments...] 114 | 115 | VERSION: 116 | 0.0.0 117 | 118 | COMMANDS: 119 | help, h Shows a list of commands or help for one command 120 | 121 | GLOBAL OPTIONS 122 | --version Shows version information 123 | ``` 124 | 125 | ### Arguments 126 | You can lookup arguments by calling the `Args` function on `cli.Context`. 127 | 128 | ``` go 129 | ... 130 | app.Action = func(c *cli.Context) { 131 | println("Hello", c.Args()[0]) 132 | } 133 | ... 134 | ``` 135 | 136 | ### Flags 137 | Setting and querying flags is simple. 138 | ``` go 139 | ... 140 | app.Flags = []cli.Flag { 141 | cli.StringFlag{ 142 | Name: "lang", 143 | Value: "english", 144 | Usage: "language for the greeting", 145 | }, 146 | } 147 | app.Action = func(c *cli.Context) { 148 | name := "someone" 149 | if len(c.Args()) > 0 { 150 | name = c.Args()[0] 151 | } 152 | if c.String("lang") == "spanish" { 153 | println("Hola", name) 154 | } else { 155 | println("Hello", name) 156 | } 157 | } 158 | ... 159 | ``` 160 | 161 | #### Alternate Names 162 | 163 | You can set alternate (or short) names for flags by providing a comma-delimited list for the `Name`. e.g. 164 | 165 | ``` go 166 | app.Flags = []cli.Flag { 167 | cli.StringFlag{ 168 | Name: "lang, l", 169 | Value: "english", 170 | Usage: "language for the greeting", 171 | }, 172 | } 173 | ``` 174 | 175 | 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. 176 | 177 | #### Values from the Environment 178 | 179 | You can also have the default value set from the environment via `EnvVar`. e.g. 180 | 181 | ``` go 182 | app.Flags = []cli.Flag { 183 | cli.StringFlag{ 184 | Name: "lang, l", 185 | Value: "english", 186 | Usage: "language for the greeting", 187 | EnvVar: "APP_LANG", 188 | }, 189 | } 190 | ``` 191 | 192 | The `EnvVar` may also be given as a comma-delimited "cascade", where the first environment variable that resolves is used as the default. 193 | 194 | ``` go 195 | app.Flags = []cli.Flag { 196 | cli.StringFlag{ 197 | Name: "lang, l", 198 | Value: "english", 199 | Usage: "language for the greeting", 200 | EnvVar: "LEGACY_COMPAT_LANG,APP_LANG,LANG", 201 | }, 202 | } 203 | ``` 204 | 205 | ### Subcommands 206 | 207 | Subcommands can be defined for a more git-like command line app. 208 | ```go 209 | ... 210 | app.Commands = []cli.Command{ 211 | { 212 | Name: "add", 213 | ShortName: "a", 214 | Usage: "add a task to the list", 215 | Action: func(c *cli.Context) { 216 | println("added task: ", c.Args().First()) 217 | }, 218 | }, 219 | { 220 | Name: "complete", 221 | ShortName: "c", 222 | Usage: "complete a task on the list", 223 | Action: func(c *cli.Context) { 224 | println("completed task: ", c.Args().First()) 225 | }, 226 | }, 227 | { 228 | Name: "template", 229 | ShortName: "r", 230 | Usage: "options for task templates", 231 | Subcommands: []cli.Command{ 232 | { 233 | Name: "add", 234 | Usage: "add a new template", 235 | Action: func(c *cli.Context) { 236 | println("new task template: ", c.Args().First()) 237 | }, 238 | }, 239 | { 240 | Name: "remove", 241 | Usage: "remove an existing template", 242 | Action: func(c *cli.Context) { 243 | println("removed task template: ", c.Args().First()) 244 | }, 245 | }, 246 | }, 247 | }, 248 | } 249 | ... 250 | ``` 251 | 252 | ### Bash Completion 253 | 254 | You can enable completion commands by setting the `EnableBashCompletion` 255 | flag on the `App` object. By default, this setting will only auto-complete to 256 | show an app's subcommands, but you can write your own completion methods for 257 | the App or its subcommands. 258 | ```go 259 | ... 260 | var tasks = []string{"cook", "clean", "laundry", "eat", "sleep", "code"} 261 | app := cli.NewApp() 262 | app.EnableBashCompletion = true 263 | app.Commands = []cli.Command{ 264 | { 265 | Name: "complete", 266 | ShortName: "c", 267 | Usage: "complete a task on the list", 268 | Action: func(c *cli.Context) { 269 | println("completed task: ", c.Args().First()) 270 | }, 271 | BashComplete: func(c *cli.Context) { 272 | // This will complete if no args are passed 273 | if len(c.Args()) > 0 { 274 | return 275 | } 276 | for _, t := range tasks { 277 | fmt.Println(t) 278 | } 279 | }, 280 | } 281 | } 282 | ... 283 | ``` 284 | 285 | #### To Enable 286 | 287 | Source the `autocomplete/bash_autocomplete` file in your `.bashrc` file while 288 | setting the `PROG` variable to the name of your program: 289 | 290 | `PROG=myprogram source /.../cli/autocomplete/bash_autocomplete` 291 | 292 | 293 | ## Contribution Guidelines 294 | Feel free to put up a pull request to fix a bug or maybe add a feature. I will give it a code review and make sure that it does not break backwards compatibility. If I or any other collaborators agree that it is in line with the vision of the project, we will work with you to get the code into a mergeable state and merge it into the master branch. 295 | 296 | If you are have contributed something significant to the project, I will most likely add you as a collaborator. As a collaborator you are given the ability to merge others pull requests. It is very important that new code does not break existing code, so be careful about what code you do choose to merge. If you have any questions feel free to link @codegangsta to the issue in question and we can review it together. 297 | 298 | If you feel like you have contributed to the project but have not yet been added as a collaborator, I probably forgot to add you. Hit @codegangsta up over email and we will get it figured out. 299 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/codegangsta/cli/context.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "errors" 5 | "flag" 6 | "strconv" 7 | "strings" 8 | "time" 9 | ) 10 | 11 | // Context is a type that is passed through to 12 | // each Handler action in a cli application. Context 13 | // can be used to retrieve context-specific Args and 14 | // parsed command-line options. 15 | type Context struct { 16 | App *App 17 | Command Command 18 | flagSet *flag.FlagSet 19 | globalSet *flag.FlagSet 20 | setFlags map[string]bool 21 | globalSetFlags map[string]bool 22 | } 23 | 24 | // Creates a new context. For use in when invoking an App or Command action. 25 | func NewContext(app *App, set *flag.FlagSet, globalSet *flag.FlagSet) *Context { 26 | return &Context{App: app, flagSet: set, globalSet: globalSet} 27 | } 28 | 29 | // Looks up the value of a local int flag, returns 0 if no int flag exists 30 | func (c *Context) Int(name string) int { 31 | return lookupInt(name, c.flagSet) 32 | } 33 | 34 | // Looks up the value of a local time.Duration flag, returns 0 if no time.Duration flag exists 35 | func (c *Context) Duration(name string) time.Duration { 36 | return lookupDuration(name, c.flagSet) 37 | } 38 | 39 | // Looks up the value of a local float64 flag, returns 0 if no float64 flag exists 40 | func (c *Context) Float64(name string) float64 { 41 | return lookupFloat64(name, c.flagSet) 42 | } 43 | 44 | // Looks up the value of a local bool flag, returns false if no bool flag exists 45 | func (c *Context) Bool(name string) bool { 46 | return lookupBool(name, c.flagSet) 47 | } 48 | 49 | // Looks up the value of a local boolT flag, returns false if no bool flag exists 50 | func (c *Context) BoolT(name string) bool { 51 | return lookupBoolT(name, c.flagSet) 52 | } 53 | 54 | // Looks up the value of a local string flag, returns "" if no string flag exists 55 | func (c *Context) String(name string) string { 56 | return lookupString(name, c.flagSet) 57 | } 58 | 59 | // Looks up the value of a local string slice flag, returns nil if no string slice flag exists 60 | func (c *Context) StringSlice(name string) []string { 61 | return lookupStringSlice(name, c.flagSet) 62 | } 63 | 64 | // Looks up the value of a local int slice flag, returns nil if no int slice flag exists 65 | func (c *Context) IntSlice(name string) []int { 66 | return lookupIntSlice(name, c.flagSet) 67 | } 68 | 69 | // Looks up the value of a local generic flag, returns nil if no generic flag exists 70 | func (c *Context) Generic(name string) interface{} { 71 | return lookupGeneric(name, c.flagSet) 72 | } 73 | 74 | // Looks up the value of a global int flag, returns 0 if no int flag exists 75 | func (c *Context) GlobalInt(name string) int { 76 | return lookupInt(name, c.globalSet) 77 | } 78 | 79 | // Looks up the value of a global time.Duration flag, returns 0 if no time.Duration flag exists 80 | func (c *Context) GlobalDuration(name string) time.Duration { 81 | return lookupDuration(name, c.globalSet) 82 | } 83 | 84 | // Looks up the value of a global bool flag, returns false if no bool flag exists 85 | func (c *Context) GlobalBool(name string) bool { 86 | return lookupBool(name, c.globalSet) 87 | } 88 | 89 | // Looks up the value of a global string flag, returns "" if no string flag exists 90 | func (c *Context) GlobalString(name string) string { 91 | return lookupString(name, c.globalSet) 92 | } 93 | 94 | // Looks up the value of a global string slice flag, returns nil if no string slice flag exists 95 | func (c *Context) GlobalStringSlice(name string) []string { 96 | return lookupStringSlice(name, c.globalSet) 97 | } 98 | 99 | // Looks up the value of a global int slice flag, returns nil if no int slice flag exists 100 | func (c *Context) GlobalIntSlice(name string) []int { 101 | return lookupIntSlice(name, c.globalSet) 102 | } 103 | 104 | // Looks up the value of a global generic flag, returns nil if no generic flag exists 105 | func (c *Context) GlobalGeneric(name string) interface{} { 106 | return lookupGeneric(name, c.globalSet) 107 | } 108 | 109 | // Determines if the flag was actually set 110 | func (c *Context) IsSet(name string) bool { 111 | if c.setFlags == nil { 112 | c.setFlags = make(map[string]bool) 113 | c.flagSet.Visit(func(f *flag.Flag) { 114 | c.setFlags[f.Name] = true 115 | }) 116 | } 117 | return c.setFlags[name] == true 118 | } 119 | 120 | // Determines if the global flag was actually set 121 | func (c *Context) GlobalIsSet(name string) bool { 122 | if c.globalSetFlags == nil { 123 | c.globalSetFlags = make(map[string]bool) 124 | c.globalSet.Visit(func(f *flag.Flag) { 125 | c.globalSetFlags[f.Name] = true 126 | }) 127 | } 128 | return c.globalSetFlags[name] == true 129 | } 130 | 131 | // Returns a slice of flag names used in this context. 132 | func (c *Context) FlagNames() (names []string) { 133 | for _, flag := range c.Command.Flags { 134 | name := strings.Split(flag.getName(), ",")[0] 135 | if name == "help" { 136 | continue 137 | } 138 | names = append(names, name) 139 | } 140 | return 141 | } 142 | 143 | // Returns a slice of global flag names used by the app. 144 | func (c *Context) GlobalFlagNames() (names []string) { 145 | for _, flag := range c.App.Flags { 146 | name := strings.Split(flag.getName(), ",")[0] 147 | if name == "help" || name == "version" { 148 | continue 149 | } 150 | names = append(names, name) 151 | } 152 | return 153 | } 154 | 155 | type Args []string 156 | 157 | // Returns the command line arguments associated with the context. 158 | func (c *Context) Args() Args { 159 | args := Args(c.flagSet.Args()) 160 | return args 161 | } 162 | 163 | // Returns the nth argument, or else a blank string 164 | func (a Args) Get(n int) string { 165 | if len(a) > n { 166 | return a[n] 167 | } 168 | return "" 169 | } 170 | 171 | // Returns the first argument, or else a blank string 172 | func (a Args) First() string { 173 | return a.Get(0) 174 | } 175 | 176 | // Return the rest of the arguments (not the first one) 177 | // or else an empty string slice 178 | func (a Args) Tail() []string { 179 | if len(a) >= 2 { 180 | return []string(a)[1:] 181 | } 182 | return []string{} 183 | } 184 | 185 | // Checks if there are any arguments present 186 | func (a Args) Present() bool { 187 | return len(a) != 0 188 | } 189 | 190 | // Swaps arguments at the given indexes 191 | func (a Args) Swap(from, to int) error { 192 | if from >= len(a) || to >= len(a) { 193 | return errors.New("index out of range") 194 | } 195 | a[from], a[to] = a[to], a[from] 196 | return nil 197 | } 198 | 199 | func lookupInt(name string, set *flag.FlagSet) int { 200 | f := set.Lookup(name) 201 | if f != nil { 202 | val, err := strconv.Atoi(f.Value.String()) 203 | if err != nil { 204 | return 0 205 | } 206 | return val 207 | } 208 | 209 | return 0 210 | } 211 | 212 | func lookupDuration(name string, set *flag.FlagSet) time.Duration { 213 | f := set.Lookup(name) 214 | if f != nil { 215 | val, err := time.ParseDuration(f.Value.String()) 216 | if err == nil { 217 | return val 218 | } 219 | } 220 | 221 | return 0 222 | } 223 | 224 | func lookupFloat64(name string, set *flag.FlagSet) float64 { 225 | f := set.Lookup(name) 226 | if f != nil { 227 | val, err := strconv.ParseFloat(f.Value.String(), 64) 228 | if err != nil { 229 | return 0 230 | } 231 | return val 232 | } 233 | 234 | return 0 235 | } 236 | 237 | func lookupString(name string, set *flag.FlagSet) string { 238 | f := set.Lookup(name) 239 | if f != nil { 240 | return f.Value.String() 241 | } 242 | 243 | return "" 244 | } 245 | 246 | func lookupStringSlice(name string, set *flag.FlagSet) []string { 247 | f := set.Lookup(name) 248 | if f != nil { 249 | return (f.Value.(*StringSlice)).Value() 250 | 251 | } 252 | 253 | return nil 254 | } 255 | 256 | func lookupIntSlice(name string, set *flag.FlagSet) []int { 257 | f := set.Lookup(name) 258 | if f != nil { 259 | return (f.Value.(*IntSlice)).Value() 260 | 261 | } 262 | 263 | return nil 264 | } 265 | 266 | func lookupGeneric(name string, set *flag.FlagSet) interface{} { 267 | f := set.Lookup(name) 268 | if f != nil { 269 | return f.Value 270 | } 271 | return nil 272 | } 273 | 274 | func lookupBool(name string, set *flag.FlagSet) bool { 275 | f := set.Lookup(name) 276 | if f != nil { 277 | val, err := strconv.ParseBool(f.Value.String()) 278 | if err != nil { 279 | return false 280 | } 281 | return val 282 | } 283 | 284 | return false 285 | } 286 | 287 | func lookupBoolT(name string, set *flag.FlagSet) bool { 288 | f := set.Lookup(name) 289 | if f != nil { 290 | val, err := strconv.ParseBool(f.Value.String()) 291 | if err != nil { 292 | return true 293 | } 294 | return val 295 | } 296 | 297 | return false 298 | } 299 | 300 | func copyFlag(name string, ff *flag.Flag, set *flag.FlagSet) { 301 | switch ff.Value.(type) { 302 | case *StringSlice: 303 | default: 304 | set.Set(name, ff.Value.String()) 305 | } 306 | } 307 | 308 | func normalizeFlags(flags []Flag, set *flag.FlagSet) error { 309 | visited := make(map[string]bool) 310 | set.Visit(func(f *flag.Flag) { 311 | visited[f.Name] = true 312 | }) 313 | for _, f := range flags { 314 | parts := strings.Split(f.getName(), ",") 315 | if len(parts) == 1 { 316 | continue 317 | } 318 | var ff *flag.Flag 319 | for _, name := range parts { 320 | name = strings.Trim(name, " ") 321 | if visited[name] { 322 | if ff != nil { 323 | return errors.New("Cannot use two forms of the same flag: " + name + " " + ff.Name) 324 | } 325 | ff = set.Lookup(name) 326 | } 327 | } 328 | if ff == nil { 329 | continue 330 | } 331 | for _, name := range parts { 332 | name = strings.Trim(name, " ") 333 | if !visited[name] { 334 | copyFlag(name, ff, set) 335 | } 336 | } 337 | } 338 | return nil 339 | } 340 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/codegangsta/cli/flag.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "os" 7 | "strconv" 8 | "strings" 9 | "time" 10 | ) 11 | 12 | // This flag enables bash-completion for all commands and subcommands 13 | var BashCompletionFlag = BoolFlag{ 14 | Name: "generate-bash-completion", 15 | } 16 | 17 | // This flag prints the version for the application 18 | var VersionFlag = BoolFlag{ 19 | Name: "version, v", 20 | Usage: "print the version", 21 | } 22 | 23 | // This flag prints the help for all commands and subcommands 24 | var HelpFlag = BoolFlag{ 25 | Name: "help, h", 26 | Usage: "show help", 27 | } 28 | 29 | // Flag is a common interface related to parsing flags in cli. 30 | // For more advanced flag parsing techniques, it is recomended that 31 | // this interface be implemented. 32 | type Flag interface { 33 | fmt.Stringer 34 | // Apply Flag settings to the given flag set 35 | Apply(*flag.FlagSet) 36 | getName() string 37 | } 38 | 39 | func flagSet(name string, flags []Flag) *flag.FlagSet { 40 | set := flag.NewFlagSet(name, flag.ContinueOnError) 41 | 42 | for _, f := range flags { 43 | f.Apply(set) 44 | } 45 | return set 46 | } 47 | 48 | func eachName(longName string, fn func(string)) { 49 | parts := strings.Split(longName, ",") 50 | for _, name := range parts { 51 | name = strings.Trim(name, " ") 52 | fn(name) 53 | } 54 | } 55 | 56 | // Generic is a generic parseable type identified by a specific flag 57 | type Generic interface { 58 | Set(value string) error 59 | String() string 60 | } 61 | 62 | // GenericFlag is the flag type for types implementing Generic 63 | type GenericFlag struct { 64 | Name string 65 | Value Generic 66 | Usage string 67 | EnvVar string 68 | } 69 | 70 | func (f GenericFlag) String() string { 71 | 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)) 72 | } 73 | 74 | func (f GenericFlag) Apply(set *flag.FlagSet) { 75 | val := f.Value 76 | if f.EnvVar != "" { 77 | for _, envVar := range strings.Split(f.EnvVar, ",") { 78 | envVar = strings.TrimSpace(envVar) 79 | if envVal := os.Getenv(envVar); envVal != "" { 80 | val.Set(envVal) 81 | break 82 | } 83 | } 84 | } 85 | 86 | eachName(f.Name, func(name string) { 87 | set.Var(f.Value, name, f.Usage) 88 | }) 89 | } 90 | 91 | func (f GenericFlag) getName() string { 92 | return f.Name 93 | } 94 | 95 | type StringSlice []string 96 | 97 | func (f *StringSlice) Set(value string) error { 98 | *f = append(*f, value) 99 | return nil 100 | } 101 | 102 | func (f *StringSlice) String() string { 103 | return fmt.Sprintf("%s", *f) 104 | } 105 | 106 | func (f *StringSlice) Value() []string { 107 | return *f 108 | } 109 | 110 | type StringSliceFlag struct { 111 | Name string 112 | Value *StringSlice 113 | Usage string 114 | EnvVar string 115 | } 116 | 117 | func (f StringSliceFlag) String() string { 118 | firstName := strings.Trim(strings.Split(f.Name, ",")[0], " ") 119 | pref := prefixFor(firstName) 120 | return withEnvHint(f.EnvVar, fmt.Sprintf("%s '%v'\t%v", prefixedNames(f.Name), pref+firstName+" option "+pref+firstName+" option", f.Usage)) 121 | } 122 | 123 | func (f StringSliceFlag) Apply(set *flag.FlagSet) { 124 | if f.EnvVar != "" { 125 | for _, envVar := range strings.Split(f.EnvVar, ",") { 126 | envVar = strings.TrimSpace(envVar) 127 | if envVal := os.Getenv(envVar); envVal != "" { 128 | newVal := &StringSlice{} 129 | for _, s := range strings.Split(envVal, ",") { 130 | s = strings.TrimSpace(s) 131 | newVal.Set(s) 132 | } 133 | f.Value = newVal 134 | break 135 | } 136 | } 137 | } 138 | 139 | eachName(f.Name, func(name string) { 140 | set.Var(f.Value, name, f.Usage) 141 | }) 142 | } 143 | 144 | func (f StringSliceFlag) getName() string { 145 | return f.Name 146 | } 147 | 148 | type IntSlice []int 149 | 150 | func (f *IntSlice) Set(value string) error { 151 | 152 | tmp, err := strconv.Atoi(value) 153 | if err != nil { 154 | return err 155 | } else { 156 | *f = append(*f, tmp) 157 | } 158 | return nil 159 | } 160 | 161 | func (f *IntSlice) String() string { 162 | return fmt.Sprintf("%d", *f) 163 | } 164 | 165 | func (f *IntSlice) Value() []int { 166 | return *f 167 | } 168 | 169 | type IntSliceFlag struct { 170 | Name string 171 | Value *IntSlice 172 | Usage string 173 | EnvVar string 174 | } 175 | 176 | func (f IntSliceFlag) String() string { 177 | firstName := strings.Trim(strings.Split(f.Name, ",")[0], " ") 178 | pref := prefixFor(firstName) 179 | return withEnvHint(f.EnvVar, fmt.Sprintf("%s '%v'\t%v", prefixedNames(f.Name), pref+firstName+" option "+pref+firstName+" option", f.Usage)) 180 | } 181 | 182 | func (f IntSliceFlag) Apply(set *flag.FlagSet) { 183 | if f.EnvVar != "" { 184 | for _, envVar := range strings.Split(f.EnvVar, ",") { 185 | envVar = strings.TrimSpace(envVar) 186 | if envVal := os.Getenv(envVar); envVal != "" { 187 | newVal := &IntSlice{} 188 | for _, s := range strings.Split(envVal, ",") { 189 | s = strings.TrimSpace(s) 190 | err := newVal.Set(s) 191 | if err != nil { 192 | fmt.Fprintf(os.Stderr, err.Error()) 193 | } 194 | } 195 | f.Value = newVal 196 | break 197 | } 198 | } 199 | } 200 | 201 | eachName(f.Name, func(name string) { 202 | set.Var(f.Value, name, f.Usage) 203 | }) 204 | } 205 | 206 | func (f IntSliceFlag) getName() string { 207 | return f.Name 208 | } 209 | 210 | type BoolFlag struct { 211 | Name string 212 | Usage string 213 | EnvVar string 214 | } 215 | 216 | func (f BoolFlag) String() string { 217 | return withEnvHint(f.EnvVar, fmt.Sprintf("%s\t%v", prefixedNames(f.Name), f.Usage)) 218 | } 219 | 220 | func (f BoolFlag) Apply(set *flag.FlagSet) { 221 | val := false 222 | if f.EnvVar != "" { 223 | for _, envVar := range strings.Split(f.EnvVar, ",") { 224 | envVar = strings.TrimSpace(envVar) 225 | if envVal := os.Getenv(envVar); envVal != "" { 226 | envValBool, err := strconv.ParseBool(envVal) 227 | if err == nil { 228 | val = envValBool 229 | } 230 | break 231 | } 232 | } 233 | } 234 | 235 | eachName(f.Name, func(name string) { 236 | set.Bool(name, val, f.Usage) 237 | }) 238 | } 239 | 240 | func (f BoolFlag) getName() string { 241 | return f.Name 242 | } 243 | 244 | type BoolTFlag struct { 245 | Name string 246 | Usage string 247 | EnvVar string 248 | } 249 | 250 | func (f BoolTFlag) String() string { 251 | return withEnvHint(f.EnvVar, fmt.Sprintf("%s\t%v", prefixedNames(f.Name), f.Usage)) 252 | } 253 | 254 | func (f BoolTFlag) Apply(set *flag.FlagSet) { 255 | val := true 256 | if f.EnvVar != "" { 257 | for _, envVar := range strings.Split(f.EnvVar, ",") { 258 | envVar = strings.TrimSpace(envVar) 259 | if envVal := os.Getenv(envVar); envVal != "" { 260 | envValBool, err := strconv.ParseBool(envVal) 261 | if err == nil { 262 | val = envValBool 263 | break 264 | } 265 | } 266 | } 267 | } 268 | 269 | eachName(f.Name, func(name string) { 270 | set.Bool(name, val, f.Usage) 271 | }) 272 | } 273 | 274 | func (f BoolTFlag) getName() string { 275 | return f.Name 276 | } 277 | 278 | type StringFlag struct { 279 | Name string 280 | Value string 281 | Usage string 282 | EnvVar string 283 | } 284 | 285 | func (f StringFlag) String() string { 286 | var fmtString string 287 | fmtString = "%s %v\t%v" 288 | 289 | if len(f.Value) > 0 { 290 | fmtString = "%s '%v'\t%v" 291 | } else { 292 | fmtString = "%s %v\t%v" 293 | } 294 | 295 | return withEnvHint(f.EnvVar, fmt.Sprintf(fmtString, prefixedNames(f.Name), f.Value, f.Usage)) 296 | } 297 | 298 | func (f StringFlag) Apply(set *flag.FlagSet) { 299 | if f.EnvVar != "" { 300 | for _, envVar := range strings.Split(f.EnvVar, ",") { 301 | envVar = strings.TrimSpace(envVar) 302 | if envVal := os.Getenv(envVar); envVal != "" { 303 | f.Value = envVal 304 | break 305 | } 306 | } 307 | } 308 | 309 | eachName(f.Name, func(name string) { 310 | set.String(name, f.Value, f.Usage) 311 | }) 312 | } 313 | 314 | func (f StringFlag) getName() string { 315 | return f.Name 316 | } 317 | 318 | type IntFlag struct { 319 | Name string 320 | Value int 321 | Usage string 322 | EnvVar string 323 | } 324 | 325 | func (f IntFlag) String() string { 326 | return withEnvHint(f.EnvVar, fmt.Sprintf("%s '%v'\t%v", prefixedNames(f.Name), f.Value, f.Usage)) 327 | } 328 | 329 | func (f IntFlag) Apply(set *flag.FlagSet) { 330 | if f.EnvVar != "" { 331 | for _, envVar := range strings.Split(f.EnvVar, ",") { 332 | envVar = strings.TrimSpace(envVar) 333 | if envVal := os.Getenv(envVar); envVal != "" { 334 | envValInt, err := strconv.ParseUint(envVal, 10, 64) 335 | if err == nil { 336 | f.Value = int(envValInt) 337 | break 338 | } 339 | } 340 | } 341 | } 342 | 343 | eachName(f.Name, func(name string) { 344 | set.Int(name, f.Value, f.Usage) 345 | }) 346 | } 347 | 348 | func (f IntFlag) getName() string { 349 | return f.Name 350 | } 351 | 352 | type DurationFlag struct { 353 | Name string 354 | Value time.Duration 355 | Usage string 356 | EnvVar string 357 | } 358 | 359 | func (f DurationFlag) String() string { 360 | return withEnvHint(f.EnvVar, fmt.Sprintf("%s '%v'\t%v", prefixedNames(f.Name), f.Value, f.Usage)) 361 | } 362 | 363 | func (f DurationFlag) Apply(set *flag.FlagSet) { 364 | if f.EnvVar != "" { 365 | for _, envVar := range strings.Split(f.EnvVar, ",") { 366 | envVar = strings.TrimSpace(envVar) 367 | if envVal := os.Getenv(envVar); envVal != "" { 368 | envValDuration, err := time.ParseDuration(envVal) 369 | if err == nil { 370 | f.Value = envValDuration 371 | break 372 | } 373 | } 374 | } 375 | } 376 | 377 | eachName(f.Name, func(name string) { 378 | set.Duration(name, f.Value, f.Usage) 379 | }) 380 | } 381 | 382 | func (f DurationFlag) getName() string { 383 | return f.Name 384 | } 385 | 386 | type Float64Flag struct { 387 | Name string 388 | Value float64 389 | Usage string 390 | EnvVar string 391 | } 392 | 393 | func (f Float64Flag) String() string { 394 | return withEnvHint(f.EnvVar, fmt.Sprintf("%s '%v'\t%v", prefixedNames(f.Name), f.Value, f.Usage)) 395 | } 396 | 397 | func (f Float64Flag) Apply(set *flag.FlagSet) { 398 | if f.EnvVar != "" { 399 | for _, envVar := range strings.Split(f.EnvVar, ",") { 400 | envVar = strings.TrimSpace(envVar) 401 | if envVal := os.Getenv(envVar); envVal != "" { 402 | envValFloat, err := strconv.ParseFloat(envVal, 10) 403 | if err == nil { 404 | f.Value = float64(envValFloat) 405 | } 406 | } 407 | } 408 | } 409 | 410 | eachName(f.Name, func(name string) { 411 | set.Float64(name, f.Value, f.Usage) 412 | }) 413 | } 414 | 415 | func (f Float64Flag) getName() string { 416 | return f.Name 417 | } 418 | 419 | func prefixFor(name string) (prefix string) { 420 | if len(name) == 1 { 421 | prefix = "-" 422 | } else { 423 | prefix = "--" 424 | } 425 | 426 | return 427 | } 428 | 429 | func prefixedNames(fullName string) (prefixed string) { 430 | parts := strings.Split(fullName, ",") 431 | for i, name := range parts { 432 | name = strings.Trim(name, " ") 433 | prefixed += prefixFor(name) + name 434 | if i < len(parts)-1 { 435 | prefixed += ", " 436 | } 437 | } 438 | return 439 | } 440 | 441 | func withEnvHint(envVar, str string) string { 442 | envText := "" 443 | if envVar != "" { 444 | envText = fmt.Sprintf(" [$%s]", strings.Join(strings.Split(envVar, ","), ", $")) 445 | } 446 | return str + envText 447 | } 448 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/codegangsta/cli/app_test.go: -------------------------------------------------------------------------------- 1 | package cli_test 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "testing" 7 | 8 | "github.com/johntdyer/anybar-go/Godeps/_workspace/src/github.com/codegangsta/cli" 9 | ) 10 | 11 | func ExampleApp() { 12 | // set args for examples sake 13 | os.Args = []string{"greet", "--name", "Jeremy"} 14 | 15 | app := cli.NewApp() 16 | app.Name = "greet" 17 | app.Flags = []cli.Flag{ 18 | cli.StringFlag{Name: "name", Value: "bob", Usage: "a name to say"}, 19 | } 20 | app.Action = func(c *cli.Context) { 21 | fmt.Printf("Hello %v\n", c.String("name")) 22 | } 23 | app.Run(os.Args) 24 | // Output: 25 | // Hello Jeremy 26 | } 27 | 28 | func ExampleAppSubcommand() { 29 | // set args for examples sake 30 | os.Args = []string{"say", "hi", "english", "--name", "Jeremy"} 31 | app := cli.NewApp() 32 | app.Name = "say" 33 | app.Commands = []cli.Command{ 34 | { 35 | Name: "hello", 36 | ShortName: "hi", 37 | Usage: "use it to see a description", 38 | Description: "This is how we describe hello the function", 39 | Subcommands: []cli.Command{ 40 | { 41 | Name: "english", 42 | ShortName: "en", 43 | Usage: "sends a greeting in english", 44 | Description: "greets someone in english", 45 | Flags: []cli.Flag{ 46 | cli.StringFlag{ 47 | Name: "name", 48 | Value: "Bob", 49 | Usage: "Name of the person to greet", 50 | }, 51 | }, 52 | Action: func(c *cli.Context) { 53 | fmt.Println("Hello,", c.String("name")) 54 | }, 55 | }, 56 | }, 57 | }, 58 | } 59 | 60 | app.Run(os.Args) 61 | // Output: 62 | // Hello, Jeremy 63 | } 64 | 65 | func ExampleAppHelp() { 66 | // set args for examples sake 67 | os.Args = []string{"greet", "h", "describeit"} 68 | 69 | app := cli.NewApp() 70 | app.Name = "greet" 71 | app.Flags = []cli.Flag{ 72 | cli.StringFlag{Name: "name", Value: "bob", Usage: "a name to say"}, 73 | } 74 | app.Commands = []cli.Command{ 75 | { 76 | Name: "describeit", 77 | ShortName: "d", 78 | Usage: "use it to see a description", 79 | Description: "This is how we describe describeit the function", 80 | Action: func(c *cli.Context) { 81 | fmt.Printf("i like to describe things") 82 | }, 83 | }, 84 | } 85 | app.Run(os.Args) 86 | // Output: 87 | // NAME: 88 | // describeit - use it to see a description 89 | // 90 | // USAGE: 91 | // command describeit [arguments...] 92 | // 93 | // DESCRIPTION: 94 | // This is how we describe describeit the function 95 | } 96 | 97 | func ExampleAppBashComplete() { 98 | // set args for examples sake 99 | os.Args = []string{"greet", "--generate-bash-completion"} 100 | 101 | app := cli.NewApp() 102 | app.Name = "greet" 103 | app.EnableBashCompletion = true 104 | app.Commands = []cli.Command{ 105 | { 106 | Name: "describeit", 107 | ShortName: "d", 108 | Usage: "use it to see a description", 109 | Description: "This is how we describe describeit the function", 110 | Action: func(c *cli.Context) { 111 | fmt.Printf("i like to describe things") 112 | }, 113 | }, { 114 | Name: "next", 115 | Usage: "next example", 116 | Description: "more stuff to see when generating bash completion", 117 | Action: func(c *cli.Context) { 118 | fmt.Printf("the next example") 119 | }, 120 | }, 121 | } 122 | 123 | app.Run(os.Args) 124 | // Output: 125 | // describeit 126 | // d 127 | // next 128 | // help 129 | // h 130 | } 131 | 132 | func TestApp_Run(t *testing.T) { 133 | s := "" 134 | 135 | app := cli.NewApp() 136 | app.Action = func(c *cli.Context) { 137 | s = s + c.Args().First() 138 | } 139 | 140 | err := app.Run([]string{"command", "foo"}) 141 | expect(t, err, nil) 142 | err = app.Run([]string{"command", "bar"}) 143 | expect(t, err, nil) 144 | expect(t, s, "foobar") 145 | } 146 | 147 | var commandAppTests = []struct { 148 | name string 149 | expected bool 150 | }{ 151 | {"foobar", true}, 152 | {"batbaz", true}, 153 | {"b", true}, 154 | {"f", true}, 155 | {"bat", false}, 156 | {"nothing", false}, 157 | } 158 | 159 | func TestApp_Command(t *testing.T) { 160 | app := cli.NewApp() 161 | fooCommand := cli.Command{Name: "foobar", ShortName: "f"} 162 | batCommand := cli.Command{Name: "batbaz", ShortName: "b"} 163 | app.Commands = []cli.Command{ 164 | fooCommand, 165 | batCommand, 166 | } 167 | 168 | for _, test := range commandAppTests { 169 | expect(t, app.Command(test.name) != nil, test.expected) 170 | } 171 | } 172 | 173 | func TestApp_CommandWithArgBeforeFlags(t *testing.T) { 174 | var parsedOption, firstArg string 175 | 176 | app := cli.NewApp() 177 | command := cli.Command{ 178 | Name: "cmd", 179 | Flags: []cli.Flag{ 180 | cli.StringFlag{Name: "option", Value: "", Usage: "some option"}, 181 | }, 182 | Action: func(c *cli.Context) { 183 | parsedOption = c.String("option") 184 | firstArg = c.Args().First() 185 | }, 186 | } 187 | app.Commands = []cli.Command{command} 188 | 189 | app.Run([]string{"", "cmd", "my-arg", "--option", "my-option"}) 190 | 191 | expect(t, parsedOption, "my-option") 192 | expect(t, firstArg, "my-arg") 193 | } 194 | 195 | func TestApp_Float64Flag(t *testing.T) { 196 | var meters float64 197 | 198 | app := cli.NewApp() 199 | app.Flags = []cli.Flag{ 200 | cli.Float64Flag{Name: "height", Value: 1.5, Usage: "Set the height, in meters"}, 201 | } 202 | app.Action = func(c *cli.Context) { 203 | meters = c.Float64("height") 204 | } 205 | 206 | app.Run([]string{"", "--height", "1.93"}) 207 | expect(t, meters, 1.93) 208 | } 209 | 210 | func TestApp_ParseSliceFlags(t *testing.T) { 211 | var parsedOption, firstArg string 212 | var parsedIntSlice []int 213 | var parsedStringSlice []string 214 | 215 | app := cli.NewApp() 216 | command := cli.Command{ 217 | Name: "cmd", 218 | Flags: []cli.Flag{ 219 | cli.IntSliceFlag{Name: "p", Value: &cli.IntSlice{}, Usage: "set one or more ip addr"}, 220 | cli.StringSliceFlag{Name: "ip", Value: &cli.StringSlice{}, Usage: "set one or more ports to open"}, 221 | }, 222 | Action: func(c *cli.Context) { 223 | parsedIntSlice = c.IntSlice("p") 224 | parsedStringSlice = c.StringSlice("ip") 225 | parsedOption = c.String("option") 226 | firstArg = c.Args().First() 227 | }, 228 | } 229 | app.Commands = []cli.Command{command} 230 | 231 | app.Run([]string{"", "cmd", "my-arg", "-p", "22", "-p", "80", "-ip", "8.8.8.8", "-ip", "8.8.4.4"}) 232 | 233 | IntsEquals := func(a, b []int) bool { 234 | if len(a) != len(b) { 235 | return false 236 | } 237 | for i, v := range a { 238 | if v != b[i] { 239 | return false 240 | } 241 | } 242 | return true 243 | } 244 | 245 | StrsEquals := func(a, b []string) bool { 246 | if len(a) != len(b) { 247 | return false 248 | } 249 | for i, v := range a { 250 | if v != b[i] { 251 | return false 252 | } 253 | } 254 | return true 255 | } 256 | var expectedIntSlice = []int{22, 80} 257 | var expectedStringSlice = []string{"8.8.8.8", "8.8.4.4"} 258 | 259 | if !IntsEquals(parsedIntSlice, expectedIntSlice) { 260 | t.Errorf("%v does not match %v", parsedIntSlice, expectedIntSlice) 261 | } 262 | 263 | if !StrsEquals(parsedStringSlice, expectedStringSlice) { 264 | t.Errorf("%v does not match %v", parsedStringSlice, expectedStringSlice) 265 | } 266 | } 267 | 268 | func TestApp_DefaultStdout(t *testing.T) { 269 | app := cli.NewApp() 270 | 271 | if app.Writer != os.Stdout { 272 | t.Error("Default output writer not set.") 273 | } 274 | } 275 | 276 | type mockWriter struct { 277 | written []byte 278 | } 279 | 280 | func (fw *mockWriter) Write(p []byte) (n int, err error) { 281 | if fw.written == nil { 282 | fw.written = p 283 | } else { 284 | fw.written = append(fw.written, p...) 285 | } 286 | 287 | return len(p), nil 288 | } 289 | 290 | func (fw *mockWriter) GetWritten() (b []byte) { 291 | return fw.written 292 | } 293 | 294 | func TestApp_SetStdout(t *testing.T) { 295 | w := &mockWriter{} 296 | 297 | app := cli.NewApp() 298 | app.Name = "test" 299 | app.Writer = w 300 | 301 | err := app.Run([]string{"help"}) 302 | 303 | if err != nil { 304 | t.Fatalf("Run error: %s", err) 305 | } 306 | 307 | if len(w.written) == 0 { 308 | t.Error("App did not write output to desired writer.") 309 | } 310 | } 311 | 312 | func TestApp_BeforeFunc(t *testing.T) { 313 | beforeRun, subcommandRun := false, false 314 | beforeError := fmt.Errorf("fail") 315 | var err error 316 | 317 | app := cli.NewApp() 318 | 319 | app.Before = func(c *cli.Context) error { 320 | beforeRun = true 321 | s := c.String("opt") 322 | if s == "fail" { 323 | return beforeError 324 | } 325 | 326 | return nil 327 | } 328 | 329 | app.Commands = []cli.Command{ 330 | cli.Command{ 331 | Name: "sub", 332 | Action: func(c *cli.Context) { 333 | subcommandRun = true 334 | }, 335 | }, 336 | } 337 | 338 | app.Flags = []cli.Flag{ 339 | cli.StringFlag{Name: "opt"}, 340 | } 341 | 342 | // run with the Before() func succeeding 343 | err = app.Run([]string{"command", "--opt", "succeed", "sub"}) 344 | 345 | if err != nil { 346 | t.Fatalf("Run error: %s", err) 347 | } 348 | 349 | if beforeRun == false { 350 | t.Errorf("Before() not executed when expected") 351 | } 352 | 353 | if subcommandRun == false { 354 | t.Errorf("Subcommand not executed when expected") 355 | } 356 | 357 | // reset 358 | beforeRun, subcommandRun = false, false 359 | 360 | // run with the Before() func failing 361 | err = app.Run([]string{"command", "--opt", "fail", "sub"}) 362 | 363 | // should be the same error produced by the Before func 364 | if err != beforeError { 365 | t.Errorf("Run error expected, but not received") 366 | } 367 | 368 | if beforeRun == false { 369 | t.Errorf("Before() not executed when expected") 370 | } 371 | 372 | if subcommandRun == true { 373 | t.Errorf("Subcommand executed when NOT expected") 374 | } 375 | 376 | } 377 | 378 | func TestAppHelpPrinter(t *testing.T) { 379 | oldPrinter := cli.HelpPrinter 380 | defer func() { 381 | cli.HelpPrinter = oldPrinter 382 | }() 383 | 384 | var wasCalled = false 385 | cli.HelpPrinter = func(template string, data interface{}) { 386 | wasCalled = true 387 | } 388 | 389 | app := cli.NewApp() 390 | app.Run([]string{"-h"}) 391 | 392 | if wasCalled == false { 393 | t.Errorf("Help printer expected to be called, but was not") 394 | } 395 | } 396 | 397 | func TestAppVersionPrinter(t *testing.T) { 398 | oldPrinter := cli.VersionPrinter 399 | defer func() { 400 | cli.VersionPrinter = oldPrinter 401 | }() 402 | 403 | var wasCalled = false 404 | cli.VersionPrinter = func(c *cli.Context) { 405 | wasCalled = true 406 | } 407 | 408 | app := cli.NewApp() 409 | ctx := cli.NewContext(app, nil, nil) 410 | cli.ShowVersion(ctx) 411 | 412 | if wasCalled == false { 413 | t.Errorf("Version printer expected to be called, but was not") 414 | } 415 | } 416 | 417 | func TestAppCommandNotFound(t *testing.T) { 418 | beforeRun, subcommandRun := false, false 419 | app := cli.NewApp() 420 | 421 | app.CommandNotFound = func(c *cli.Context, command string) { 422 | beforeRun = true 423 | } 424 | 425 | app.Commands = []cli.Command{ 426 | cli.Command{ 427 | Name: "bar", 428 | Action: func(c *cli.Context) { 429 | subcommandRun = true 430 | }, 431 | }, 432 | } 433 | 434 | app.Run([]string{"command", "foo"}) 435 | 436 | expect(t, beforeRun, true) 437 | expect(t, subcommandRun, false) 438 | } 439 | 440 | func TestGlobalFlagsInSubcommands(t *testing.T) { 441 | subcommandRun := false 442 | app := cli.NewApp() 443 | 444 | app.Flags = []cli.Flag{ 445 | cli.BoolFlag{Name: "debug, d", Usage: "Enable debugging"}, 446 | } 447 | 448 | app.Commands = []cli.Command{ 449 | cli.Command{ 450 | Name: "foo", 451 | Subcommands: []cli.Command{ 452 | { 453 | Name: "bar", 454 | Action: func(c *cli.Context) { 455 | if c.GlobalBool("debug") { 456 | subcommandRun = true 457 | } 458 | }, 459 | }, 460 | }, 461 | }, 462 | } 463 | 464 | app.Run([]string{"command", "-d", "foo", "bar"}) 465 | 466 | expect(t, subcommandRun, true) 467 | } 468 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | {description} 294 | Copyright (C) {year} {fullname} 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | {signature of Ty Coon}, 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | 341 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/codegangsta/cli/flag_test.go: -------------------------------------------------------------------------------- 1 | package cli_test 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "reflect" 7 | "strings" 8 | "testing" 9 | 10 | "github.com/johntdyer/anybar-go/Godeps/_workspace/src/github.com/codegangsta/cli" 11 | ) 12 | 13 | var boolFlagTests = []struct { 14 | name string 15 | expected string 16 | }{ 17 | {"help", "--help\t"}, 18 | {"h", "-h\t"}, 19 | } 20 | 21 | func TestBoolFlagHelpOutput(t *testing.T) { 22 | 23 | for _, test := range boolFlagTests { 24 | flag := cli.BoolFlag{Name: test.name} 25 | output := flag.String() 26 | 27 | if output != test.expected { 28 | t.Errorf("%s does not match %s", output, test.expected) 29 | } 30 | } 31 | } 32 | 33 | var stringFlagTests = []struct { 34 | name string 35 | value string 36 | expected string 37 | }{ 38 | {"help", "", "--help \t"}, 39 | {"h", "", "-h \t"}, 40 | {"h", "", "-h \t"}, 41 | {"test", "Something", "--test 'Something'\t"}, 42 | } 43 | 44 | func TestStringFlagHelpOutput(t *testing.T) { 45 | 46 | for _, test := range stringFlagTests { 47 | flag := cli.StringFlag{Name: test.name, Value: test.value} 48 | output := flag.String() 49 | 50 | if output != test.expected { 51 | t.Errorf("%s does not match %s", output, test.expected) 52 | } 53 | } 54 | } 55 | 56 | func TestStringFlagWithEnvVarHelpOutput(t *testing.T) { 57 | os.Clearenv() 58 | os.Setenv("APP_FOO", "derp") 59 | for _, test := range stringFlagTests { 60 | flag := cli.StringFlag{Name: test.name, Value: test.value, EnvVar: "APP_FOO"} 61 | output := flag.String() 62 | 63 | if !strings.HasSuffix(output, " [$APP_FOO]") { 64 | t.Errorf("%s does not end with [$APP_FOO]", output) 65 | } 66 | } 67 | } 68 | 69 | var stringSliceFlagTests = []struct { 70 | name string 71 | value *cli.StringSlice 72 | expected string 73 | }{ 74 | {"help", func() *cli.StringSlice { 75 | s := &cli.StringSlice{} 76 | s.Set("") 77 | return s 78 | }(), "--help '--help option --help option'\t"}, 79 | {"h", func() *cli.StringSlice { 80 | s := &cli.StringSlice{} 81 | s.Set("") 82 | return s 83 | }(), "-h '-h option -h option'\t"}, 84 | {"h", func() *cli.StringSlice { 85 | s := &cli.StringSlice{} 86 | s.Set("") 87 | return s 88 | }(), "-h '-h option -h option'\t"}, 89 | {"test", func() *cli.StringSlice { 90 | s := &cli.StringSlice{} 91 | s.Set("Something") 92 | return s 93 | }(), "--test '--test option --test option'\t"}, 94 | } 95 | 96 | func TestStringSliceFlagHelpOutput(t *testing.T) { 97 | 98 | for _, test := range stringSliceFlagTests { 99 | flag := cli.StringSliceFlag{Name: test.name, Value: test.value} 100 | output := flag.String() 101 | 102 | if output != test.expected { 103 | t.Errorf("%q does not match %q", output, test.expected) 104 | } 105 | } 106 | } 107 | 108 | func TestStringSliceFlagWithEnvVarHelpOutput(t *testing.T) { 109 | os.Clearenv() 110 | os.Setenv("APP_QWWX", "11,4") 111 | for _, test := range stringSliceFlagTests { 112 | flag := cli.StringSliceFlag{Name: test.name, Value: test.value, EnvVar: "APP_QWWX"} 113 | output := flag.String() 114 | 115 | if !strings.HasSuffix(output, " [$APP_QWWX]") { 116 | t.Errorf("%q does not end with [$APP_QWWX]", output) 117 | } 118 | } 119 | } 120 | 121 | var intFlagTests = []struct { 122 | name string 123 | expected string 124 | }{ 125 | {"help", "--help '0'\t"}, 126 | {"h", "-h '0'\t"}, 127 | } 128 | 129 | func TestIntFlagHelpOutput(t *testing.T) { 130 | 131 | for _, test := range intFlagTests { 132 | flag := cli.IntFlag{Name: test.name} 133 | output := flag.String() 134 | 135 | if output != test.expected { 136 | t.Errorf("%s does not match %s", output, test.expected) 137 | } 138 | } 139 | } 140 | 141 | func TestIntFlagWithEnvVarHelpOutput(t *testing.T) { 142 | os.Clearenv() 143 | os.Setenv("APP_BAR", "2") 144 | for _, test := range intFlagTests { 145 | flag := cli.IntFlag{Name: test.name, EnvVar: "APP_BAR"} 146 | output := flag.String() 147 | 148 | if !strings.HasSuffix(output, " [$APP_BAR]") { 149 | t.Errorf("%s does not end with [$APP_BAR]", output) 150 | } 151 | } 152 | } 153 | 154 | var durationFlagTests = []struct { 155 | name string 156 | expected string 157 | }{ 158 | {"help", "--help '0'\t"}, 159 | {"h", "-h '0'\t"}, 160 | } 161 | 162 | func TestDurationFlagHelpOutput(t *testing.T) { 163 | 164 | for _, test := range durationFlagTests { 165 | flag := cli.DurationFlag{Name: test.name} 166 | output := flag.String() 167 | 168 | if output != test.expected { 169 | t.Errorf("%s does not match %s", output, test.expected) 170 | } 171 | } 172 | } 173 | 174 | func TestDurationFlagWithEnvVarHelpOutput(t *testing.T) { 175 | os.Clearenv() 176 | os.Setenv("APP_BAR", "2h3m6s") 177 | for _, test := range durationFlagTests { 178 | flag := cli.DurationFlag{Name: test.name, EnvVar: "APP_BAR"} 179 | output := flag.String() 180 | 181 | if !strings.HasSuffix(output, " [$APP_BAR]") { 182 | t.Errorf("%s does not end with [$APP_BAR]", output) 183 | } 184 | } 185 | } 186 | 187 | var intSliceFlagTests = []struct { 188 | name string 189 | value *cli.IntSlice 190 | expected string 191 | }{ 192 | {"help", &cli.IntSlice{}, "--help '--help option --help option'\t"}, 193 | {"h", &cli.IntSlice{}, "-h '-h option -h option'\t"}, 194 | {"h", &cli.IntSlice{}, "-h '-h option -h option'\t"}, 195 | {"test", func() *cli.IntSlice { 196 | i := &cli.IntSlice{} 197 | i.Set("9") 198 | return i 199 | }(), "--test '--test option --test option'\t"}, 200 | } 201 | 202 | func TestIntSliceFlagHelpOutput(t *testing.T) { 203 | 204 | for _, test := range intSliceFlagTests { 205 | flag := cli.IntSliceFlag{Name: test.name, Value: test.value} 206 | output := flag.String() 207 | 208 | if output != test.expected { 209 | t.Errorf("%q does not match %q", output, test.expected) 210 | } 211 | } 212 | } 213 | 214 | func TestIntSliceFlagWithEnvVarHelpOutput(t *testing.T) { 215 | os.Clearenv() 216 | os.Setenv("APP_SMURF", "42,3") 217 | for _, test := range intSliceFlagTests { 218 | flag := cli.IntSliceFlag{Name: test.name, Value: test.value, EnvVar: "APP_SMURF"} 219 | output := flag.String() 220 | 221 | if !strings.HasSuffix(output, " [$APP_SMURF]") { 222 | t.Errorf("%q does not end with [$APP_SMURF]", output) 223 | } 224 | } 225 | } 226 | 227 | var float64FlagTests = []struct { 228 | name string 229 | expected string 230 | }{ 231 | {"help", "--help '0'\t"}, 232 | {"h", "-h '0'\t"}, 233 | } 234 | 235 | func TestFloat64FlagHelpOutput(t *testing.T) { 236 | 237 | for _, test := range float64FlagTests { 238 | flag := cli.Float64Flag{Name: test.name} 239 | output := flag.String() 240 | 241 | if output != test.expected { 242 | t.Errorf("%s does not match %s", output, test.expected) 243 | } 244 | } 245 | } 246 | 247 | func TestFloat64FlagWithEnvVarHelpOutput(t *testing.T) { 248 | os.Clearenv() 249 | os.Setenv("APP_BAZ", "99.4") 250 | for _, test := range float64FlagTests { 251 | flag := cli.Float64Flag{Name: test.name, EnvVar: "APP_BAZ"} 252 | output := flag.String() 253 | 254 | if !strings.HasSuffix(output, " [$APP_BAZ]") { 255 | t.Errorf("%s does not end with [$APP_BAZ]", output) 256 | } 257 | } 258 | } 259 | 260 | var genericFlagTests = []struct { 261 | name string 262 | value cli.Generic 263 | expected string 264 | }{ 265 | {"help", &Parser{}, "--help \t`-help option -help option` "}, 266 | {"h", &Parser{}, "-h \t`-h option -h option` "}, 267 | {"test", &Parser{}, "--test \t`-test option -test option` "}, 268 | } 269 | 270 | func TestGenericFlagHelpOutput(t *testing.T) { 271 | 272 | for _, test := range genericFlagTests { 273 | flag := cli.GenericFlag{Name: test.name} 274 | output := flag.String() 275 | 276 | if output != test.expected { 277 | t.Errorf("%q does not match %q", output, test.expected) 278 | } 279 | } 280 | } 281 | 282 | func TestGenericFlagWithEnvVarHelpOutput(t *testing.T) { 283 | os.Clearenv() 284 | os.Setenv("APP_ZAP", "3") 285 | for _, test := range genericFlagTests { 286 | flag := cli.GenericFlag{Name: test.name, EnvVar: "APP_ZAP"} 287 | output := flag.String() 288 | 289 | if !strings.HasSuffix(output, " [$APP_ZAP]") { 290 | t.Errorf("%s does not end with [$APP_ZAP]", output) 291 | } 292 | } 293 | } 294 | 295 | func TestParseMultiString(t *testing.T) { 296 | (&cli.App{ 297 | Flags: []cli.Flag{ 298 | cli.StringFlag{Name: "serve, s"}, 299 | }, 300 | Action: func(ctx *cli.Context) { 301 | if ctx.String("serve") != "10" { 302 | t.Errorf("main name not set") 303 | } 304 | if ctx.String("s") != "10" { 305 | t.Errorf("short name not set") 306 | } 307 | }, 308 | }).Run([]string{"run", "-s", "10"}) 309 | } 310 | 311 | func TestParseMultiStringFromEnv(t *testing.T) { 312 | os.Clearenv() 313 | os.Setenv("APP_COUNT", "20") 314 | (&cli.App{ 315 | Flags: []cli.Flag{ 316 | cli.StringFlag{Name: "count, c", EnvVar: "APP_COUNT"}, 317 | }, 318 | Action: func(ctx *cli.Context) { 319 | if ctx.String("count") != "20" { 320 | t.Errorf("main name not set") 321 | } 322 | if ctx.String("c") != "20" { 323 | t.Errorf("short name not set") 324 | } 325 | }, 326 | }).Run([]string{"run"}) 327 | } 328 | 329 | func TestParseMultiStringFromEnvCascade(t *testing.T) { 330 | os.Clearenv() 331 | os.Setenv("APP_COUNT", "20") 332 | (&cli.App{ 333 | Flags: []cli.Flag{ 334 | cli.StringFlag{Name: "count, c", EnvVar: "COMPAT_COUNT,APP_COUNT"}, 335 | }, 336 | Action: func(ctx *cli.Context) { 337 | if ctx.String("count") != "20" { 338 | t.Errorf("main name not set") 339 | } 340 | if ctx.String("c") != "20" { 341 | t.Errorf("short name not set") 342 | } 343 | }, 344 | }).Run([]string{"run"}) 345 | } 346 | 347 | func TestParseMultiStringSlice(t *testing.T) { 348 | (&cli.App{ 349 | Flags: []cli.Flag{ 350 | cli.StringSliceFlag{Name: "serve, s", Value: &cli.StringSlice{}}, 351 | }, 352 | Action: func(ctx *cli.Context) { 353 | if !reflect.DeepEqual(ctx.StringSlice("serve"), []string{"10", "20"}) { 354 | t.Errorf("main name not set") 355 | } 356 | if !reflect.DeepEqual(ctx.StringSlice("s"), []string{"10", "20"}) { 357 | t.Errorf("short name not set") 358 | } 359 | }, 360 | }).Run([]string{"run", "-s", "10", "-s", "20"}) 361 | } 362 | 363 | func TestParseMultiStringSliceFromEnv(t *testing.T) { 364 | os.Clearenv() 365 | os.Setenv("APP_INTERVALS", "20,30,40") 366 | 367 | (&cli.App{ 368 | Flags: []cli.Flag{ 369 | cli.StringSliceFlag{Name: "intervals, i", Value: &cli.StringSlice{}, EnvVar: "APP_INTERVALS"}, 370 | }, 371 | Action: func(ctx *cli.Context) { 372 | if !reflect.DeepEqual(ctx.StringSlice("intervals"), []string{"20", "30", "40"}) { 373 | t.Errorf("main name not set from env") 374 | } 375 | if !reflect.DeepEqual(ctx.StringSlice("i"), []string{"20", "30", "40"}) { 376 | t.Errorf("short name not set from env") 377 | } 378 | }, 379 | }).Run([]string{"run"}) 380 | } 381 | 382 | func TestParseMultiStringSliceFromEnvCascade(t *testing.T) { 383 | os.Clearenv() 384 | os.Setenv("APP_INTERVALS", "20,30,40") 385 | 386 | (&cli.App{ 387 | Flags: []cli.Flag{ 388 | cli.StringSliceFlag{Name: "intervals, i", Value: &cli.StringSlice{}, EnvVar: "COMPAT_INTERVALS,APP_INTERVALS"}, 389 | }, 390 | Action: func(ctx *cli.Context) { 391 | if !reflect.DeepEqual(ctx.StringSlice("intervals"), []string{"20", "30", "40"}) { 392 | t.Errorf("main name not set from env") 393 | } 394 | if !reflect.DeepEqual(ctx.StringSlice("i"), []string{"20", "30", "40"}) { 395 | t.Errorf("short name not set from env") 396 | } 397 | }, 398 | }).Run([]string{"run"}) 399 | } 400 | 401 | func TestParseMultiInt(t *testing.T) { 402 | a := cli.App{ 403 | Flags: []cli.Flag{ 404 | cli.IntFlag{Name: "serve, s"}, 405 | }, 406 | Action: func(ctx *cli.Context) { 407 | if ctx.Int("serve") != 10 { 408 | t.Errorf("main name not set") 409 | } 410 | if ctx.Int("s") != 10 { 411 | t.Errorf("short name not set") 412 | } 413 | }, 414 | } 415 | a.Run([]string{"run", "-s", "10"}) 416 | } 417 | 418 | func TestParseMultiIntFromEnv(t *testing.T) { 419 | os.Clearenv() 420 | os.Setenv("APP_TIMEOUT_SECONDS", "10") 421 | a := cli.App{ 422 | Flags: []cli.Flag{ 423 | cli.IntFlag{Name: "timeout, t", EnvVar: "APP_TIMEOUT_SECONDS"}, 424 | }, 425 | Action: func(ctx *cli.Context) { 426 | if ctx.Int("timeout") != 10 { 427 | t.Errorf("main name not set") 428 | } 429 | if ctx.Int("t") != 10 { 430 | t.Errorf("short name not set") 431 | } 432 | }, 433 | } 434 | a.Run([]string{"run"}) 435 | } 436 | 437 | func TestParseMultiIntFromEnvCascade(t *testing.T) { 438 | os.Clearenv() 439 | os.Setenv("APP_TIMEOUT_SECONDS", "10") 440 | a := cli.App{ 441 | Flags: []cli.Flag{ 442 | cli.IntFlag{Name: "timeout, t", EnvVar: "COMPAT_TIMEOUT_SECONDS,APP_TIMEOUT_SECONDS"}, 443 | }, 444 | Action: func(ctx *cli.Context) { 445 | if ctx.Int("timeout") != 10 { 446 | t.Errorf("main name not set") 447 | } 448 | if ctx.Int("t") != 10 { 449 | t.Errorf("short name not set") 450 | } 451 | }, 452 | } 453 | a.Run([]string{"run"}) 454 | } 455 | 456 | func TestParseMultiIntSlice(t *testing.T) { 457 | (&cli.App{ 458 | Flags: []cli.Flag{ 459 | cli.IntSliceFlag{Name: "serve, s", Value: &cli.IntSlice{}}, 460 | }, 461 | Action: func(ctx *cli.Context) { 462 | if !reflect.DeepEqual(ctx.IntSlice("serve"), []int{10, 20}) { 463 | t.Errorf("main name not set") 464 | } 465 | if !reflect.DeepEqual(ctx.IntSlice("s"), []int{10, 20}) { 466 | t.Errorf("short name not set") 467 | } 468 | }, 469 | }).Run([]string{"run", "-s", "10", "-s", "20"}) 470 | } 471 | 472 | func TestParseMultiIntSliceFromEnv(t *testing.T) { 473 | os.Clearenv() 474 | os.Setenv("APP_INTERVALS", "20,30,40") 475 | 476 | (&cli.App{ 477 | Flags: []cli.Flag{ 478 | cli.IntSliceFlag{Name: "intervals, i", Value: &cli.IntSlice{}, EnvVar: "APP_INTERVALS"}, 479 | }, 480 | Action: func(ctx *cli.Context) { 481 | if !reflect.DeepEqual(ctx.IntSlice("intervals"), []int{20, 30, 40}) { 482 | t.Errorf("main name not set from env") 483 | } 484 | if !reflect.DeepEqual(ctx.IntSlice("i"), []int{20, 30, 40}) { 485 | t.Errorf("short name not set from env") 486 | } 487 | }, 488 | }).Run([]string{"run"}) 489 | } 490 | 491 | func TestParseMultiIntSliceFromEnvCascade(t *testing.T) { 492 | os.Clearenv() 493 | os.Setenv("APP_INTERVALS", "20,30,40") 494 | 495 | (&cli.App{ 496 | Flags: []cli.Flag{ 497 | cli.IntSliceFlag{Name: "intervals, i", Value: &cli.IntSlice{}, EnvVar: "COMPAT_INTERVALS,APP_INTERVALS"}, 498 | }, 499 | Action: func(ctx *cli.Context) { 500 | if !reflect.DeepEqual(ctx.IntSlice("intervals"), []int{20, 30, 40}) { 501 | t.Errorf("main name not set from env") 502 | } 503 | if !reflect.DeepEqual(ctx.IntSlice("i"), []int{20, 30, 40}) { 504 | t.Errorf("short name not set from env") 505 | } 506 | }, 507 | }).Run([]string{"run"}) 508 | } 509 | 510 | func TestParseMultiFloat64(t *testing.T) { 511 | a := cli.App{ 512 | Flags: []cli.Flag{ 513 | cli.Float64Flag{Name: "serve, s"}, 514 | }, 515 | Action: func(ctx *cli.Context) { 516 | if ctx.Float64("serve") != 10.2 { 517 | t.Errorf("main name not set") 518 | } 519 | if ctx.Float64("s") != 10.2 { 520 | t.Errorf("short name not set") 521 | } 522 | }, 523 | } 524 | a.Run([]string{"run", "-s", "10.2"}) 525 | } 526 | 527 | func TestParseMultiFloat64FromEnv(t *testing.T) { 528 | os.Clearenv() 529 | os.Setenv("APP_TIMEOUT_SECONDS", "15.5") 530 | a := cli.App{ 531 | Flags: []cli.Flag{ 532 | cli.Float64Flag{Name: "timeout, t", EnvVar: "APP_TIMEOUT_SECONDS"}, 533 | }, 534 | Action: func(ctx *cli.Context) { 535 | if ctx.Float64("timeout") != 15.5 { 536 | t.Errorf("main name not set") 537 | } 538 | if ctx.Float64("t") != 15.5 { 539 | t.Errorf("short name not set") 540 | } 541 | }, 542 | } 543 | a.Run([]string{"run"}) 544 | } 545 | 546 | func TestParseMultiFloat64FromEnvCascade(t *testing.T) { 547 | os.Clearenv() 548 | os.Setenv("APP_TIMEOUT_SECONDS", "15.5") 549 | a := cli.App{ 550 | Flags: []cli.Flag{ 551 | cli.Float64Flag{Name: "timeout, t", EnvVar: "COMPAT_TIMEOUT_SECONDS,APP_TIMEOUT_SECONDS"}, 552 | }, 553 | Action: func(ctx *cli.Context) { 554 | if ctx.Float64("timeout") != 15.5 { 555 | t.Errorf("main name not set") 556 | } 557 | if ctx.Float64("t") != 15.5 { 558 | t.Errorf("short name not set") 559 | } 560 | }, 561 | } 562 | a.Run([]string{"run"}) 563 | } 564 | 565 | func TestParseMultiBool(t *testing.T) { 566 | a := cli.App{ 567 | Flags: []cli.Flag{ 568 | cli.BoolFlag{Name: "serve, s"}, 569 | }, 570 | Action: func(ctx *cli.Context) { 571 | if ctx.Bool("serve") != true { 572 | t.Errorf("main name not set") 573 | } 574 | if ctx.Bool("s") != true { 575 | t.Errorf("short name not set") 576 | } 577 | }, 578 | } 579 | a.Run([]string{"run", "--serve"}) 580 | } 581 | 582 | func TestParseMultiBoolFromEnv(t *testing.T) { 583 | os.Clearenv() 584 | os.Setenv("APP_DEBUG", "1") 585 | a := cli.App{ 586 | Flags: []cli.Flag{ 587 | cli.BoolFlag{Name: "debug, d", EnvVar: "APP_DEBUG"}, 588 | }, 589 | Action: func(ctx *cli.Context) { 590 | if ctx.Bool("debug") != true { 591 | t.Errorf("main name not set from env") 592 | } 593 | if ctx.Bool("d") != true { 594 | t.Errorf("short name not set from env") 595 | } 596 | }, 597 | } 598 | a.Run([]string{"run"}) 599 | } 600 | 601 | func TestParseMultiBoolFromEnvCascade(t *testing.T) { 602 | os.Clearenv() 603 | os.Setenv("APP_DEBUG", "1") 604 | a := cli.App{ 605 | Flags: []cli.Flag{ 606 | cli.BoolFlag{Name: "debug, d", EnvVar: "COMPAT_DEBUG,APP_DEBUG"}, 607 | }, 608 | Action: func(ctx *cli.Context) { 609 | if ctx.Bool("debug") != true { 610 | t.Errorf("main name not set from env") 611 | } 612 | if ctx.Bool("d") != true { 613 | t.Errorf("short name not set from env") 614 | } 615 | }, 616 | } 617 | a.Run([]string{"run"}) 618 | } 619 | 620 | func TestParseMultiBoolT(t *testing.T) { 621 | a := cli.App{ 622 | Flags: []cli.Flag{ 623 | cli.BoolTFlag{Name: "serve, s"}, 624 | }, 625 | Action: func(ctx *cli.Context) { 626 | if ctx.BoolT("serve") != true { 627 | t.Errorf("main name not set") 628 | } 629 | if ctx.BoolT("s") != true { 630 | t.Errorf("short name not set") 631 | } 632 | }, 633 | } 634 | a.Run([]string{"run", "--serve"}) 635 | } 636 | 637 | func TestParseMultiBoolTFromEnv(t *testing.T) { 638 | os.Clearenv() 639 | os.Setenv("APP_DEBUG", "0") 640 | a := cli.App{ 641 | Flags: []cli.Flag{ 642 | cli.BoolTFlag{Name: "debug, d", EnvVar: "APP_DEBUG"}, 643 | }, 644 | Action: func(ctx *cli.Context) { 645 | if ctx.BoolT("debug") != false { 646 | t.Errorf("main name not set from env") 647 | } 648 | if ctx.BoolT("d") != false { 649 | t.Errorf("short name not set from env") 650 | } 651 | }, 652 | } 653 | a.Run([]string{"run"}) 654 | } 655 | 656 | func TestParseMultiBoolTFromEnvCascade(t *testing.T) { 657 | os.Clearenv() 658 | os.Setenv("APP_DEBUG", "0") 659 | a := cli.App{ 660 | Flags: []cli.Flag{ 661 | cli.BoolTFlag{Name: "debug, d", EnvVar: "COMPAT_DEBUG,APP_DEBUG"}, 662 | }, 663 | Action: func(ctx *cli.Context) { 664 | if ctx.BoolT("debug") != false { 665 | t.Errorf("main name not set from env") 666 | } 667 | if ctx.BoolT("d") != false { 668 | t.Errorf("short name not set from env") 669 | } 670 | }, 671 | } 672 | a.Run([]string{"run"}) 673 | } 674 | 675 | type Parser [2]string 676 | 677 | func (p *Parser) Set(value string) error { 678 | parts := strings.Split(value, ",") 679 | if len(parts) != 2 { 680 | return fmt.Errorf("invalid format") 681 | } 682 | 683 | (*p)[0] = parts[0] 684 | (*p)[1] = parts[1] 685 | 686 | return nil 687 | } 688 | 689 | func (p *Parser) String() string { 690 | return fmt.Sprintf("%s,%s", p[0], p[1]) 691 | } 692 | 693 | func TestParseGeneric(t *testing.T) { 694 | a := cli.App{ 695 | Flags: []cli.Flag{ 696 | cli.GenericFlag{Name: "serve, s", Value: &Parser{}}, 697 | }, 698 | Action: func(ctx *cli.Context) { 699 | if !reflect.DeepEqual(ctx.Generic("serve"), &Parser{"10", "20"}) { 700 | t.Errorf("main name not set") 701 | } 702 | if !reflect.DeepEqual(ctx.Generic("s"), &Parser{"10", "20"}) { 703 | t.Errorf("short name not set") 704 | } 705 | }, 706 | } 707 | a.Run([]string{"run", "-s", "10,20"}) 708 | } 709 | 710 | func TestParseGenericFromEnv(t *testing.T) { 711 | os.Clearenv() 712 | os.Setenv("APP_SERVE", "20,30") 713 | a := cli.App{ 714 | Flags: []cli.Flag{ 715 | cli.GenericFlag{Name: "serve, s", Value: &Parser{}, EnvVar: "APP_SERVE"}, 716 | }, 717 | Action: func(ctx *cli.Context) { 718 | if !reflect.DeepEqual(ctx.Generic("serve"), &Parser{"20", "30"}) { 719 | t.Errorf("main name not set from env") 720 | } 721 | if !reflect.DeepEqual(ctx.Generic("s"), &Parser{"20", "30"}) { 722 | t.Errorf("short name not set from env") 723 | } 724 | }, 725 | } 726 | a.Run([]string{"run"}) 727 | } 728 | 729 | func TestParseGenericFromEnvCascade(t *testing.T) { 730 | os.Clearenv() 731 | os.Setenv("APP_FOO", "99,2000") 732 | a := cli.App{ 733 | Flags: []cli.Flag{ 734 | cli.GenericFlag{Name: "foos", Value: &Parser{}, EnvVar: "COMPAT_FOO,APP_FOO"}, 735 | }, 736 | Action: func(ctx *cli.Context) { 737 | if !reflect.DeepEqual(ctx.Generic("foos"), &Parser{"99", "2000"}) { 738 | t.Errorf("value not set from env") 739 | } 740 | }, 741 | } 742 | a.Run([]string{"run"}) 743 | } 744 | --------------------------------------------------------------------------------