├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── Godeps ├── Godeps.json ├── Readme └── _workspace │ ├── .gitignore │ └── src │ └── github.com │ └── jessevdk │ └── go-flags │ ├── .travis.yml │ ├── LICENSE │ ├── README.md │ ├── arg.go │ ├── arg_test.go │ ├── assert_test.go │ ├── check_crosscompile.sh │ ├── closest.go │ ├── command.go │ ├── command_private.go │ ├── command_test.go │ ├── completion.go │ ├── completion_test.go │ ├── convert.go │ ├── convert_test.go │ ├── error.go │ ├── example_test.go │ ├── examples │ ├── add.go │ ├── bash-completion │ ├── main.go │ └── rm.go │ ├── flags.go │ ├── group.go │ ├── group_private.go │ ├── group_test.go │ ├── help.go │ ├── help_test.go │ ├── ini.go │ ├── ini_private.go │ ├── ini_test.go │ ├── long_test.go │ ├── man.go │ ├── marshal_test.go │ ├── multitag.go │ ├── option.go │ ├── option_private.go │ ├── options_test.go │ ├── optstyle_other.go │ ├── optstyle_windows.go │ ├── parser.go │ ├── parser_private.go │ ├── parser_test.go │ ├── pointer_test.go │ ├── short_test.go │ ├── tag_test.go │ ├── termsize.go │ ├── termsize_linux.go │ ├── termsize_nosysioctl.go │ ├── termsize_other.go │ ├── termsize_unix.go │ └── unknown_test.go ├── LICENSE ├── Makefile ├── README.md ├── github-commit-status ├── cli.go ├── cli_test.go ├── main.go ├── main_test.go └── version.go └── script ├── build_binaries.sh └── release /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | /bin 10 | bin/* 11 | bin 12 | pkg/* 13 | pkg 14 | 15 | # Architecture specific extensions/prefixes 16 | *.[568vq] 17 | [568vq].out 18 | 19 | *.cgo1.go 20 | *.cgo2.c 21 | _cgo_defun.c 22 | _cgo_gotypes.go 23 | _cgo_export.* 24 | 25 | _testmain.go 26 | 27 | *.exe 28 | 29 | *.sublime-* 30 | .gopath 31 | 32 | *.test 33 | .release.env 34 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: go 3 | 4 | go: 5 | - 1.3 6 | 7 | install: 8 | - go get code.google.com/p/go.tools/cmd/goimports 9 | - go get code.google.com/p/go.tools/cmd/vet 10 | - go get github.com/tools/godep 11 | - go get code.google.com/p/go.tools/cmd/cover 12 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## head 2 | 3 | ## 1.0.0 (1/9/2015) 4 | 5 | * initial release 6 | -------------------------------------------------------------------------------- /Godeps/Godeps.json: -------------------------------------------------------------------------------- 1 | { 2 | "ImportPath": "github.com/thbishop/github-commit-status", 3 | "GoVersion": "go1.4", 4 | "Packages": [ 5 | "./github-commit-status/..." 6 | ], 7 | "Deps": [ 8 | { 9 | "ImportPath": "github.com/jessevdk/go-flags", 10 | "Comment": "v1-265-g1c87b97", 11 | "Rev": "1c87b9727cd7c200be13a5d8280d7ea514469235" 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /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/.gitignore: -------------------------------------------------------------------------------- 1 | /pkg 2 | /bin 3 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jessevdk/go-flags/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | install: 4 | # go-flags 5 | - go get -d -v ./... 6 | - go build -v ./... 7 | 8 | # linting 9 | - go get code.google.com/p/go.tools/cmd/vet 10 | - go get github.com/golang/lint 11 | - go install github.com/golang/lint/golint 12 | 13 | # code coverage 14 | - go get code.google.com/p/go.tools/cmd/cover 15 | - go get github.com/onsi/ginkgo/ginkgo 16 | - go get github.com/modocache/gover 17 | - if [ "$TRAVIS_SECURE_ENV_VARS" = "true" ]; then go get github.com/mattn/goveralls; fi 18 | 19 | script: 20 | # go-flags 21 | - $(exit $(gofmt -l . | wc -l)) 22 | - go test -v ./... 23 | 24 | # linting 25 | - go tool vet -all=true -v=true . || true 26 | - $(go env GOPATH | awk 'BEGIN{FS=":"} {print $1}')/bin/golint ./... 27 | 28 | # code coverage 29 | - $(go env GOPATH | awk 'BEGIN{FS=":"} {print $1}')/bin/ginkgo -r -cover 30 | - $(go env GOPATH | awk 'BEGIN{FS=":"} {print $1}')/bin/gover 31 | - if [ "$TRAVIS_SECURE_ENV_VARS" = "true" ]; then $(go env GOPATH | awk 'BEGIN{FS=":"} {print $1}')/bin/goveralls -coverprofile=gover.coverprofile -service=travis-ci -repotoken $COVERALLS_TOKEN; fi 32 | 33 | env: 34 | # coveralls.io 35 | secure: "RCYbiB4P0RjQRIoUx/vG/AjP3mmYCbzOmr86DCww1Z88yNcy3hYr3Cq8rpPtYU5v0g7wTpu4adaKIcqRE9xknYGbqj3YWZiCoBP1/n4Z+9sHW3Dsd9D/GRGeHUus0laJUGARjWoCTvoEtOgTdGQDoX7mH+pUUY0FBltNYUdOiiU=" 36 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jessevdk/go-flags/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Jesse van den Kieboom. All rights reserved. 2 | Redistribution and use in source and binary forms, with or without 3 | modification, are permitted provided that the following conditions are 4 | met: 5 | 6 | * Redistributions of source code must retain the above copyright 7 | notice, this list of conditions and the following disclaimer. 8 | * Redistributions in binary form must reproduce the above 9 | copyright notice, this list of conditions and the following disclaimer 10 | in the documentation and/or other materials provided with the 11 | distribution. 12 | * Neither the name of Google Inc. nor the names of its 13 | contributors may be used to endorse or promote products derived from 14 | this software without specific prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jessevdk/go-flags/README.md: -------------------------------------------------------------------------------- 1 | go-flags: a go library for parsing command line arguments 2 | ========================================================= 3 | 4 | [![GoDoc](https://godoc.org/github.com/jessevdk/go-flags?status.png)](https://godoc.org/github.com/jessevdk/go-flags) [![Build Status](https://travis-ci.org/jessevdk/go-flags.svg?branch=master)](https://travis-ci.org/jessevdk/go-flags) [![Coverage Status](https://img.shields.io/coveralls/jessevdk/go-flags.svg)](https://coveralls.io/r/jessevdk/go-flags?branch=master) 5 | 6 | This library provides similar functionality to the builtin flag library of 7 | go, but provides much more functionality and nicer formatting. From the 8 | documentation: 9 | 10 | Package flags provides an extensive command line option parser. 11 | The flags package is similar in functionality to the go builtin flag package 12 | but provides more options and uses reflection to provide a convenient and 13 | succinct way of specifying command line options. 14 | 15 | Supported features: 16 | * Options with short names (-v) 17 | * Options with long names (--verbose) 18 | * Options with and without arguments (bool v.s. other type) 19 | * Options with optional arguments and default values 20 | * Multiple option groups each containing a set of options 21 | * Generate and print well-formatted help message 22 | * Passing remaining command line arguments after -- (optional) 23 | * Ignoring unknown command line options (optional) 24 | * Supports -I/usr/include -I=/usr/include -I /usr/include option argument specification 25 | * Supports multiple short options -aux 26 | * Supports all primitive go types (string, int{8..64}, uint{8..64}, float) 27 | * Supports same option multiple times (can store in slice or last option counts) 28 | * Supports maps 29 | * Supports function callbacks 30 | * Supports namespaces for (nested) option groups 31 | 32 | The flags package uses structs, reflection and struct field tags 33 | to allow users to specify command line options. This results in very simple 34 | and concise specification of your application options. For example: 35 | 36 | type Options struct { 37 | Verbose []bool `short:"v" long:"verbose" description:"Show verbose debug information"` 38 | } 39 | 40 | This specifies one option with a short name -v and a long name --verbose. 41 | When either -v or --verbose is found on the command line, a 'true' value 42 | will be appended to the Verbose field. e.g. when specifying -vvv, the 43 | resulting value of Verbose will be {[true, true, true]}. 44 | 45 | Example: 46 | -------- 47 | var opts struct { 48 | // Slice of bool will append 'true' each time the option 49 | // is encountered (can be set multiple times, like -vvv) 50 | Verbose []bool `short:"v" long:"verbose" description:"Show verbose debug information"` 51 | 52 | // Example of automatic marshalling to desired type (uint) 53 | Offset uint `long:"offset" description:"Offset"` 54 | 55 | // Example of a callback, called each time the option is found. 56 | Call func(string) `short:"c" description:"Call phone number"` 57 | 58 | // Example of a required flag 59 | Name string `short:"n" long:"name" description:"A name" required:"true"` 60 | 61 | // Example of a value name 62 | File string `short:"f" long:"file" description:"A file" value-name:"FILE"` 63 | 64 | // Example of a pointer 65 | Ptr *int `short:"p" description:"A pointer to an integer"` 66 | 67 | // Example of a slice of strings 68 | StringSlice []string `short:"s" description:"A slice of strings"` 69 | 70 | // Example of a slice of pointers 71 | PtrSlice []*string `long:"ptrslice" description:"A slice of pointers to string"` 72 | 73 | // Example of a map 74 | IntMap map[string]int `long:"intmap" description:"A map from string to int"` 75 | } 76 | 77 | // Callback which will invoke callto: to call a number. 78 | // Note that this works just on OS X (and probably only with 79 | // Skype) but it shows the idea. 80 | opts.Call = func(num string) { 81 | cmd := exec.Command("open", "callto:"+num) 82 | cmd.Start() 83 | cmd.Process.Release() 84 | } 85 | 86 | // Make some fake arguments to parse. 87 | args := []string{ 88 | "-vv", 89 | "--offset=5", 90 | "-n", "Me", 91 | "-p", "3", 92 | "-s", "hello", 93 | "-s", "world", 94 | "--ptrslice", "hello", 95 | "--ptrslice", "world", 96 | "--intmap", "a:1", 97 | "--intmap", "b:5", 98 | "arg1", 99 | "arg2", 100 | "arg3", 101 | } 102 | 103 | // Parse flags from `args'. Note that here we use flags.ParseArgs for 104 | // the sake of making a working example. Normally, you would simply use 105 | // flags.Parse(&opts) which uses os.Args 106 | args, err := flags.ParseArgs(&opts, args) 107 | 108 | if err != nil { 109 | panic(err) 110 | os.Exit(1) 111 | } 112 | 113 | fmt.Printf("Verbosity: %v\n", opts.Verbose) 114 | fmt.Printf("Offset: %d\n", opts.Offset) 115 | fmt.Printf("Name: %s\n", opts.Name) 116 | fmt.Printf("Ptr: %d\n", *opts.Ptr) 117 | fmt.Printf("StringSlice: %v\n", opts.StringSlice) 118 | fmt.Printf("PtrSlice: [%v %v]\n", *opts.PtrSlice[0], *opts.PtrSlice[1]) 119 | fmt.Printf("IntMap: [a:%v b:%v]\n", opts.IntMap["a"], opts.IntMap["b"]) 120 | fmt.Printf("Remaining args: %s\n", strings.Join(args, " ")) 121 | 122 | // Output: Verbosity: [true true] 123 | // Offset: 5 124 | // Name: Me 125 | // Ptr: 3 126 | // StringSlice: [hello world] 127 | // PtrSlice: [hello world] 128 | // IntMap: [a:1 b:5] 129 | // Remaining args: arg1 arg2 arg3 130 | 131 | More information can be found in the godocs: 132 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jessevdk/go-flags/arg.go: -------------------------------------------------------------------------------- 1 | package flags 2 | 3 | import ( 4 | "reflect" 5 | ) 6 | 7 | // Arg represents a positional argument on the command line. 8 | type Arg struct { 9 | // The name of the positional argument (used in the help) 10 | Name string 11 | 12 | // A description of the positional argument (used in the help) 13 | Description string 14 | 15 | value reflect.Value 16 | tag multiTag 17 | } 18 | 19 | func (a *Arg) isRemaining() bool { 20 | return a.value.Type().Kind() == reflect.Slice 21 | } 22 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jessevdk/go-flags/arg_test.go: -------------------------------------------------------------------------------- 1 | package flags 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestPositional(t *testing.T) { 8 | var opts = struct { 9 | Value bool `short:"v"` 10 | 11 | Positional struct { 12 | Command int 13 | Filename string 14 | Rest []string 15 | } `positional-args:"yes" required:"yes"` 16 | }{} 17 | 18 | p := NewParser(&opts, Default) 19 | ret, err := p.ParseArgs([]string{"10", "arg_test.go", "a", "b"}) 20 | 21 | if err != nil { 22 | t.Fatalf("Unexpected error: %v", err) 23 | return 24 | } 25 | 26 | if opts.Positional.Command != 10 { 27 | t.Fatalf("Expected opts.Positional.Command to be 10, but got %v", opts.Positional.Command) 28 | } 29 | 30 | if opts.Positional.Filename != "arg_test.go" { 31 | t.Fatalf("Expected opts.Positional.Filename to be \"arg_test.go\", but got %v", opts.Positional.Filename) 32 | } 33 | 34 | assertStringArray(t, opts.Positional.Rest, []string{"a", "b"}) 35 | assertStringArray(t, ret, []string{}) 36 | } 37 | 38 | func TestPositionalRequired(t *testing.T) { 39 | var opts = struct { 40 | Value bool `short:"v"` 41 | 42 | Positional struct { 43 | Command int 44 | Filename string 45 | Rest []string 46 | } `positional-args:"yes" required:"yes"` 47 | }{} 48 | 49 | p := NewParser(&opts, None) 50 | _, err := p.ParseArgs([]string{"10"}) 51 | 52 | assertError(t, err, ErrRequired, "the required argument `Filename` was not provided") 53 | } 54 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jessevdk/go-flags/assert_test.go: -------------------------------------------------------------------------------- 1 | package flags 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "io/ioutil" 7 | "os" 8 | "os/exec" 9 | "path" 10 | "runtime" 11 | "testing" 12 | ) 13 | 14 | func assertCallerInfo() (string, int) { 15 | ptr := make([]uintptr, 15) 16 | n := runtime.Callers(1, ptr) 17 | 18 | if n == 0 { 19 | return "", 0 20 | } 21 | 22 | mef := runtime.FuncForPC(ptr[0]) 23 | mefile, meline := mef.FileLine(ptr[0]) 24 | 25 | for i := 2; i < n; i++ { 26 | f := runtime.FuncForPC(ptr[i]) 27 | file, line := f.FileLine(ptr[i]) 28 | 29 | if file != mefile { 30 | return file, line 31 | } 32 | } 33 | 34 | return mefile, meline 35 | } 36 | 37 | func assertErrorf(t *testing.T, format string, args ...interface{}) { 38 | msg := fmt.Sprintf(format, args...) 39 | 40 | file, line := assertCallerInfo() 41 | 42 | t.Errorf("%s:%d: %s", path.Base(file), line, msg) 43 | } 44 | 45 | func assertFatalf(t *testing.T, format string, args ...interface{}) { 46 | msg := fmt.Sprintf(format, args...) 47 | 48 | file, line := assertCallerInfo() 49 | 50 | t.Fatalf("%s:%d: %s", path.Base(file), line, msg) 51 | } 52 | 53 | func assertString(t *testing.T, a string, b string) { 54 | if a != b { 55 | assertErrorf(t, "Expected %#v, but got %#v", b, a) 56 | } 57 | } 58 | 59 | func assertStringArray(t *testing.T, a []string, b []string) { 60 | if len(a) != len(b) { 61 | assertErrorf(t, "Expected %#v, but got %#v", b, a) 62 | return 63 | } 64 | 65 | for i, v := range a { 66 | if b[i] != v { 67 | assertErrorf(t, "Expected %#v, but got %#v", b, a) 68 | return 69 | } 70 | } 71 | } 72 | 73 | func assertBoolArray(t *testing.T, a []bool, b []bool) { 74 | if len(a) != len(b) { 75 | assertErrorf(t, "Expected %#v, but got %#v", b, a) 76 | return 77 | } 78 | 79 | for i, v := range a { 80 | if b[i] != v { 81 | assertErrorf(t, "Expected %#v, but got %#v", b, a) 82 | return 83 | } 84 | } 85 | } 86 | 87 | func assertParserSuccess(t *testing.T, data interface{}, args ...string) (*Parser, []string) { 88 | parser := NewParser(data, Default&^PrintErrors) 89 | ret, err := parser.ParseArgs(args) 90 | 91 | if err != nil { 92 | t.Fatalf("Unexpected parse error: %s", err) 93 | return nil, nil 94 | } 95 | 96 | return parser, ret 97 | } 98 | 99 | func assertParseSuccess(t *testing.T, data interface{}, args ...string) []string { 100 | _, ret := assertParserSuccess(t, data, args...) 101 | return ret 102 | } 103 | 104 | func assertError(t *testing.T, err error, typ ErrorType, msg string) { 105 | if err == nil { 106 | assertFatalf(t, "Expected error: %s", msg) 107 | return 108 | } 109 | 110 | if e, ok := err.(*Error); !ok { 111 | assertFatalf(t, "Expected Error type, but got %#v", err) 112 | } else { 113 | if e.Type != typ { 114 | assertErrorf(t, "Expected error type {%s}, but got {%s}", typ, e.Type) 115 | } 116 | 117 | if e.Message != msg { 118 | assertErrorf(t, "Expected error message %#v, but got %#v", msg, e.Message) 119 | } 120 | } 121 | } 122 | 123 | func assertParseFail(t *testing.T, typ ErrorType, msg string, data interface{}, args ...string) []string { 124 | parser := NewParser(data, Default&^PrintErrors) 125 | ret, err := parser.ParseArgs(args) 126 | 127 | assertError(t, err, typ, msg) 128 | return ret 129 | } 130 | 131 | func diff(a, b string) (string, error) { 132 | atmp, err := ioutil.TempFile("", "help-diff") 133 | 134 | if err != nil { 135 | return "", err 136 | } 137 | 138 | btmp, err := ioutil.TempFile("", "help-diff") 139 | 140 | if err != nil { 141 | return "", err 142 | } 143 | 144 | if _, err := io.WriteString(atmp, a); err != nil { 145 | return "", err 146 | } 147 | 148 | if _, err := io.WriteString(btmp, b); err != nil { 149 | return "", err 150 | } 151 | 152 | ret, err := exec.Command("diff", "-u", "-d", "--label", "got", atmp.Name(), "--label", "expected", btmp.Name()).Output() 153 | 154 | os.Remove(atmp.Name()) 155 | os.Remove(btmp.Name()) 156 | 157 | if err.Error() == "exit status 1" { 158 | return string(ret), nil 159 | } 160 | 161 | return string(ret), err 162 | } 163 | 164 | func assertDiff(t *testing.T, actual, expected, msg string) { 165 | if actual == expected { 166 | return 167 | } 168 | 169 | ret, err := diff(actual, expected) 170 | 171 | if err != nil { 172 | assertErrorf(t, "Unexpected diff error: %s", err) 173 | assertErrorf(t, "Unexpected %s, expected:\n\n%s\n\nbut got\n\n%s", msg, expected, actual) 174 | } else { 175 | assertErrorf(t, "Unexpected %s:\n\n%s", msg, ret) 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jessevdk/go-flags/check_crosscompile.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | echo '# linux arm7' 6 | GOARM=7 GOARCH=arm GOOS=linux go build 7 | echo '# linux arm5' 8 | GOARM=5 GOARCH=arm GOOS=linux go build 9 | echo '# windows 386' 10 | GOARCH=386 GOOS=windows go build 11 | echo '# windows amd64' 12 | GOARCH=amd64 GOOS=windows go build 13 | echo '# darwin' 14 | GOARCH=amd64 GOOS=darwin go build 15 | echo '# freebsd' 16 | GOARCH=amd64 GOOS=freebsd go build 17 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jessevdk/go-flags/closest.go: -------------------------------------------------------------------------------- 1 | package flags 2 | 3 | func levenshtein(s string, t string) int { 4 | if len(s) == 0 { 5 | return len(t) 6 | } 7 | 8 | if len(t) == 0 { 9 | return len(s) 10 | } 11 | 12 | dists := make([][]int, len(s)+1) 13 | for i := range dists { 14 | dists[i] = make([]int, len(t)+1) 15 | dists[i][0] = i 16 | } 17 | 18 | for j := range t { 19 | dists[0][j] = j 20 | } 21 | 22 | for i, sc := range s { 23 | for j, tc := range t { 24 | if sc == tc { 25 | dists[i+1][j+1] = dists[i][j] 26 | } else { 27 | dists[i+1][j+1] = dists[i][j] + 1 28 | if dists[i+1][j] < dists[i+1][j+1] { 29 | dists[i+1][j+1] = dists[i+1][j] + 1 30 | } 31 | if dists[i][j+1] < dists[i+1][j+1] { 32 | dists[i+1][j+1] = dists[i][j+1] + 1 33 | } 34 | } 35 | } 36 | } 37 | 38 | return dists[len(s)][len(t)] 39 | } 40 | 41 | func closestChoice(cmd string, choices []string) (string, int) { 42 | if len(choices) == 0 { 43 | return "", 0 44 | } 45 | 46 | mincmd := -1 47 | mindist := -1 48 | 49 | for i, c := range choices { 50 | l := levenshtein(cmd, c) 51 | 52 | if mincmd < 0 || l < mindist { 53 | mindist = l 54 | mincmd = i 55 | } 56 | } 57 | 58 | return choices[mincmd], mindist 59 | } 60 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jessevdk/go-flags/command.go: -------------------------------------------------------------------------------- 1 | package flags 2 | 3 | // Command represents an application command. Commands can be added to the 4 | // parser (which itself is a command) and are selected/executed when its name 5 | // is specified on the command line. The Command type embeds a Group and 6 | // therefore also carries a set of command specific options. 7 | type Command struct { 8 | // Embedded, see Group for more information 9 | *Group 10 | 11 | // The name by which the command can be invoked 12 | Name string 13 | 14 | // The active sub command (set by parsing) or nil 15 | Active *Command 16 | 17 | // Whether subcommands are optional 18 | SubcommandsOptional bool 19 | 20 | // Aliases for the command 21 | Aliases []string 22 | 23 | // Whether positional arguments are required 24 | ArgsRequired bool 25 | 26 | commands []*Command 27 | hasBuiltinHelpGroup bool 28 | args []*Arg 29 | } 30 | 31 | // Commander is an interface which can be implemented by any command added in 32 | // the options. When implemented, the Execute method will be called for the last 33 | // specified (sub)command providing the remaining command line arguments. 34 | type Commander interface { 35 | // Execute will be called for the last active (sub)command. The 36 | // args argument contains the remaining command line arguments. The 37 | // error that Execute returns will be eventually passed out of the 38 | // Parse method of the Parser. 39 | Execute(args []string) error 40 | } 41 | 42 | // Usage is an interface which can be implemented to show a custom usage string 43 | // in the help message shown for a command. 44 | type Usage interface { 45 | // Usage is called for commands to allow customized printing of command 46 | // usage in the generated help message. 47 | Usage() string 48 | } 49 | 50 | // AddCommand adds a new command to the parser with the given name and data. The 51 | // data needs to be a pointer to a struct from which the fields indicate which 52 | // options are in the command. The provided data can implement the Command and 53 | // Usage interfaces. 54 | func (c *Command) AddCommand(command string, shortDescription string, longDescription string, data interface{}) (*Command, error) { 55 | cmd := newCommand(command, shortDescription, longDescription, data) 56 | 57 | cmd.parent = c 58 | 59 | if err := cmd.scan(); err != nil { 60 | return nil, err 61 | } 62 | 63 | c.commands = append(c.commands, cmd) 64 | return cmd, nil 65 | } 66 | 67 | // AddGroup adds a new group to the command with the given name and data. The 68 | // data needs to be a pointer to a struct from which the fields indicate which 69 | // options are in the group. 70 | func (c *Command) AddGroup(shortDescription string, longDescription string, data interface{}) (*Group, error) { 71 | group := newGroup(shortDescription, longDescription, data) 72 | 73 | group.parent = c 74 | 75 | if err := group.scanType(c.scanSubcommandHandler(group)); err != nil { 76 | return nil, err 77 | } 78 | 79 | c.groups = append(c.groups, group) 80 | return group, nil 81 | } 82 | 83 | // Commands returns a list of subcommands of this command. 84 | func (c *Command) Commands() []*Command { 85 | return c.commands 86 | } 87 | 88 | // Find locates the subcommand with the given name and returns it. If no such 89 | // command can be found Find will return nil. 90 | func (c *Command) Find(name string) *Command { 91 | for _, cc := range c.commands { 92 | if cc.match(name) { 93 | return cc 94 | } 95 | } 96 | 97 | return nil 98 | } 99 | 100 | // Args returns a list of positional arguments associated with this command. 101 | func (c *Command) Args() []*Arg { 102 | ret := make([]*Arg, len(c.args)) 103 | copy(ret, c.args) 104 | 105 | return ret 106 | } 107 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jessevdk/go-flags/command_private.go: -------------------------------------------------------------------------------- 1 | package flags 2 | 3 | import ( 4 | "reflect" 5 | "sort" 6 | "strings" 7 | "unsafe" 8 | ) 9 | 10 | type lookup struct { 11 | shortNames map[string]*Option 12 | longNames map[string]*Option 13 | 14 | commands map[string]*Command 15 | } 16 | 17 | func newCommand(name string, shortDescription string, longDescription string, data interface{}) *Command { 18 | return &Command{ 19 | Group: newGroup(shortDescription, longDescription, data), 20 | Name: name, 21 | } 22 | } 23 | 24 | func (c *Command) scanSubcommandHandler(parentg *Group) scanHandler { 25 | f := func(realval reflect.Value, sfield *reflect.StructField) (bool, error) { 26 | mtag := newMultiTag(string(sfield.Tag)) 27 | 28 | if err := mtag.Parse(); err != nil { 29 | return true, err 30 | } 31 | 32 | positional := mtag.Get("positional-args") 33 | 34 | if len(positional) != 0 { 35 | stype := realval.Type() 36 | 37 | for i := 0; i < stype.NumField(); i++ { 38 | field := stype.Field(i) 39 | 40 | m := newMultiTag((string(field.Tag))) 41 | 42 | if err := m.Parse(); err != nil { 43 | return true, err 44 | } 45 | 46 | name := m.Get("name") 47 | 48 | if len(name) == 0 { 49 | name = field.Name 50 | } 51 | 52 | arg := &Arg{ 53 | Name: name, 54 | Description: m.Get("description"), 55 | 56 | value: realval.Field(i), 57 | tag: m, 58 | } 59 | 60 | c.args = append(c.args, arg) 61 | 62 | if len(mtag.Get("required")) != 0 { 63 | c.ArgsRequired = true 64 | } 65 | } 66 | 67 | return true, nil 68 | } 69 | 70 | subcommand := mtag.Get("command") 71 | 72 | if len(subcommand) != 0 { 73 | ptrval := reflect.NewAt(realval.Type(), unsafe.Pointer(realval.UnsafeAddr())) 74 | 75 | shortDescription := mtag.Get("description") 76 | longDescription := mtag.Get("long-description") 77 | subcommandsOptional := mtag.Get("subcommands-optional") 78 | aliases := mtag.GetMany("alias") 79 | 80 | subc, err := c.AddCommand(subcommand, shortDescription, longDescription, ptrval.Interface()) 81 | 82 | if err != nil { 83 | return true, err 84 | } 85 | 86 | if len(subcommandsOptional) > 0 { 87 | subc.SubcommandsOptional = true 88 | } 89 | 90 | if len(aliases) > 0 { 91 | subc.Aliases = aliases 92 | } 93 | 94 | return true, nil 95 | } 96 | 97 | return parentg.scanSubGroupHandler(realval, sfield) 98 | } 99 | 100 | return f 101 | } 102 | 103 | func (c *Command) scan() error { 104 | return c.scanType(c.scanSubcommandHandler(c.Group)) 105 | } 106 | 107 | func (c *Command) eachCommand(f func(*Command), recurse bool) { 108 | f(c) 109 | 110 | for _, cc := range c.commands { 111 | if recurse { 112 | cc.eachCommand(f, true) 113 | } else { 114 | f(cc) 115 | } 116 | } 117 | } 118 | 119 | func (c *Command) eachActiveGroup(f func(cc *Command, g *Group)) { 120 | c.eachGroup(func(g *Group) { 121 | f(c, g) 122 | }) 123 | 124 | if c.Active != nil { 125 | c.Active.eachActiveGroup(f) 126 | } 127 | } 128 | 129 | func (c *Command) addHelpGroups(showHelp func() error) { 130 | if !c.hasBuiltinHelpGroup { 131 | c.addHelpGroup(showHelp) 132 | c.hasBuiltinHelpGroup = true 133 | } 134 | 135 | for _, cc := range c.commands { 136 | cc.addHelpGroups(showHelp) 137 | } 138 | } 139 | 140 | func (c *Command) makeLookup() lookup { 141 | ret := lookup{ 142 | shortNames: make(map[string]*Option), 143 | longNames: make(map[string]*Option), 144 | commands: make(map[string]*Command), 145 | } 146 | 147 | c.eachGroup(func(g *Group) { 148 | for _, option := range g.options { 149 | if option.ShortName != 0 { 150 | ret.shortNames[string(option.ShortName)] = option 151 | } 152 | 153 | if len(option.LongName) > 0 { 154 | ret.longNames[option.LongNameWithNamespace()] = option 155 | } 156 | } 157 | }) 158 | 159 | for _, subcommand := range c.commands { 160 | ret.commands[subcommand.Name] = subcommand 161 | 162 | for _, a := range subcommand.Aliases { 163 | ret.commands[a] = subcommand 164 | } 165 | } 166 | 167 | return ret 168 | } 169 | 170 | func (c *Command) groupByName(name string) *Group { 171 | if grp := c.Group.groupByName(name); grp != nil { 172 | return grp 173 | } 174 | 175 | for _, subc := range c.commands { 176 | prefix := subc.Name + "." 177 | 178 | if strings.HasPrefix(name, prefix) { 179 | if grp := subc.groupByName(name[len(prefix):]); grp != nil { 180 | return grp 181 | } 182 | } else if name == subc.Name { 183 | return subc.Group 184 | } 185 | } 186 | 187 | return nil 188 | } 189 | 190 | type commandList []*Command 191 | 192 | func (c commandList) Less(i, j int) bool { 193 | return c[i].Name < c[j].Name 194 | } 195 | 196 | func (c commandList) Len() int { 197 | return len(c) 198 | } 199 | 200 | func (c commandList) Swap(i, j int) { 201 | c[i], c[j] = c[j], c[i] 202 | } 203 | 204 | func (c *Command) sortedCommands() []*Command { 205 | ret := make(commandList, len(c.commands)) 206 | copy(ret, c.commands) 207 | 208 | sort.Sort(ret) 209 | return []*Command(ret) 210 | } 211 | 212 | func (c *Command) match(name string) bool { 213 | if c.Name == name { 214 | return true 215 | } 216 | 217 | for _, v := range c.Aliases { 218 | if v == name { 219 | return true 220 | } 221 | } 222 | 223 | return false 224 | } 225 | 226 | func (c *Command) hasCliOptions() bool { 227 | ret := false 228 | 229 | c.eachGroup(func(g *Group) { 230 | if g.isBuiltinHelp { 231 | return 232 | } 233 | 234 | for _, opt := range g.options { 235 | if opt.canCli() { 236 | ret = true 237 | } 238 | } 239 | }) 240 | 241 | return ret 242 | } 243 | 244 | func (c *Command) fillParseState(s *parseState) { 245 | s.positional = make([]*Arg, len(c.args)) 246 | copy(s.positional, c.args) 247 | 248 | s.lookup = c.makeLookup() 249 | s.command = c 250 | } 251 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jessevdk/go-flags/command_test.go: -------------------------------------------------------------------------------- 1 | package flags 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestCommandInline(t *testing.T) { 9 | var opts = struct { 10 | Value bool `short:"v"` 11 | 12 | Command struct { 13 | G bool `short:"g"` 14 | } `command:"cmd"` 15 | }{} 16 | 17 | p, ret := assertParserSuccess(t, &opts, "-v", "cmd", "-g") 18 | 19 | assertStringArray(t, ret, []string{}) 20 | 21 | if p.Active == nil { 22 | t.Errorf("Expected active command") 23 | } 24 | 25 | if !opts.Value { 26 | t.Errorf("Expected Value to be true") 27 | } 28 | 29 | if !opts.Command.G { 30 | t.Errorf("Expected Command.G to be true") 31 | } 32 | 33 | if p.Command.Find("cmd") != p.Active { 34 | t.Errorf("Expected to find command `cmd' to be active") 35 | } 36 | } 37 | 38 | func TestCommandInlineMulti(t *testing.T) { 39 | var opts = struct { 40 | Value bool `short:"v"` 41 | 42 | C1 struct { 43 | } `command:"c1"` 44 | 45 | C2 struct { 46 | G bool `short:"g"` 47 | } `command:"c2"` 48 | }{} 49 | 50 | p, ret := assertParserSuccess(t, &opts, "-v", "c2", "-g") 51 | 52 | assertStringArray(t, ret, []string{}) 53 | 54 | if p.Active == nil { 55 | t.Errorf("Expected active command") 56 | } 57 | 58 | if !opts.Value { 59 | t.Errorf("Expected Value to be true") 60 | } 61 | 62 | if !opts.C2.G { 63 | t.Errorf("Expected C2.G to be true") 64 | } 65 | 66 | if p.Command.Find("c1") == nil { 67 | t.Errorf("Expected to find command `c1'") 68 | } 69 | 70 | if c2 := p.Command.Find("c2"); c2 == nil { 71 | t.Errorf("Expected to find command `c2'") 72 | } else if c2 != p.Active { 73 | t.Errorf("Expected to find command `c2' to be active") 74 | } 75 | } 76 | 77 | func TestCommandFlagOrder1(t *testing.T) { 78 | var opts = struct { 79 | Value bool `short:"v"` 80 | 81 | Command struct { 82 | G bool `short:"g"` 83 | } `command:"cmd"` 84 | }{} 85 | 86 | assertParseFail(t, ErrUnknownFlag, "unknown flag `g'", &opts, "-v", "-g", "cmd") 87 | } 88 | 89 | func TestCommandFlagOrder2(t *testing.T) { 90 | var opts = struct { 91 | Value bool `short:"v"` 92 | 93 | Command struct { 94 | G bool `short:"g"` 95 | } `command:"cmd"` 96 | }{} 97 | 98 | assertParseFail(t, ErrUnknownFlag, "unknown flag `v'", &opts, "cmd", "-v", "-g") 99 | } 100 | 101 | func TestCommandEstimate(t *testing.T) { 102 | var opts = struct { 103 | Value bool `short:"v"` 104 | 105 | Cmd1 struct { 106 | } `command:"remove"` 107 | 108 | Cmd2 struct { 109 | } `command:"add"` 110 | }{} 111 | 112 | p := NewParser(&opts, None) 113 | _, err := p.ParseArgs([]string{}) 114 | 115 | assertError(t, err, ErrCommandRequired, "Please specify one command of: add or remove") 116 | } 117 | 118 | func TestCommandEstimate2(t *testing.T) { 119 | var opts = struct { 120 | Value bool `short:"v"` 121 | 122 | Cmd1 struct { 123 | } `command:"remove"` 124 | 125 | Cmd2 struct { 126 | } `command:"add"` 127 | }{} 128 | 129 | p := NewParser(&opts, None) 130 | _, err := p.ParseArgs([]string{"rmive"}) 131 | 132 | assertError(t, err, ErrUnknownCommand, "Unknown command `rmive', did you mean `remove'?") 133 | } 134 | 135 | type testCommand struct { 136 | G bool `short:"g"` 137 | Executed bool 138 | EArgs []string 139 | } 140 | 141 | func (c *testCommand) Execute(args []string) error { 142 | c.Executed = true 143 | c.EArgs = args 144 | 145 | return nil 146 | } 147 | 148 | func TestCommandExecute(t *testing.T) { 149 | var opts = struct { 150 | Value bool `short:"v"` 151 | 152 | Command testCommand `command:"cmd"` 153 | }{} 154 | 155 | assertParseSuccess(t, &opts, "-v", "cmd", "-g", "a", "b") 156 | 157 | if !opts.Value { 158 | t.Errorf("Expected Value to be true") 159 | } 160 | 161 | if !opts.Command.Executed { 162 | t.Errorf("Did not execute command") 163 | } 164 | 165 | if !opts.Command.G { 166 | t.Errorf("Expected Command.C to be true") 167 | } 168 | 169 | assertStringArray(t, opts.Command.EArgs, []string{"a", "b"}) 170 | } 171 | 172 | func TestCommandClosest(t *testing.T) { 173 | var opts = struct { 174 | Value bool `short:"v"` 175 | 176 | Cmd1 struct { 177 | } `command:"remove"` 178 | 179 | Cmd2 struct { 180 | } `command:"add"` 181 | }{} 182 | 183 | args := assertParseFail(t, ErrUnknownCommand, "Unknown command `addd', did you mean `add'?", &opts, "-v", "addd") 184 | 185 | assertStringArray(t, args, []string{"addd"}) 186 | } 187 | 188 | func TestCommandAdd(t *testing.T) { 189 | var opts = struct { 190 | Value bool `short:"v"` 191 | }{} 192 | 193 | var cmd = struct { 194 | G bool `short:"g"` 195 | }{} 196 | 197 | p := NewParser(&opts, Default) 198 | c, err := p.AddCommand("cmd", "", "", &cmd) 199 | 200 | if err != nil { 201 | t.Fatalf("Unexpected error: %v", err) 202 | return 203 | } 204 | 205 | ret, err := p.ParseArgs([]string{"-v", "cmd", "-g", "rest"}) 206 | 207 | if err != nil { 208 | t.Fatalf("Unexpected error: %v", err) 209 | return 210 | } 211 | 212 | assertStringArray(t, ret, []string{"rest"}) 213 | 214 | if !opts.Value { 215 | t.Errorf("Expected Value to be true") 216 | } 217 | 218 | if !cmd.G { 219 | t.Errorf("Expected Command.G to be true") 220 | } 221 | 222 | if p.Command.Find("cmd") != c { 223 | t.Errorf("Expected to find command `cmd'") 224 | } 225 | 226 | if p.Commands()[0] != c { 227 | t.Errorf("Expected command %#v, but got %#v", c, p.Commands()[0]) 228 | } 229 | 230 | if c.Options()[0].ShortName != 'g' { 231 | t.Errorf("Expected short name `g' but got %v", c.Options()[0].ShortName) 232 | } 233 | } 234 | 235 | func TestCommandNestedInline(t *testing.T) { 236 | var opts = struct { 237 | Value bool `short:"v"` 238 | 239 | Command struct { 240 | G bool `short:"g"` 241 | 242 | Nested struct { 243 | N string `long:"n"` 244 | } `command:"nested"` 245 | } `command:"cmd"` 246 | }{} 247 | 248 | p, ret := assertParserSuccess(t, &opts, "-v", "cmd", "-g", "nested", "--n", "n", "rest") 249 | 250 | assertStringArray(t, ret, []string{"rest"}) 251 | 252 | if !opts.Value { 253 | t.Errorf("Expected Value to be true") 254 | } 255 | 256 | if !opts.Command.G { 257 | t.Errorf("Expected Command.G to be true") 258 | } 259 | 260 | assertString(t, opts.Command.Nested.N, "n") 261 | 262 | if c := p.Command.Find("cmd"); c == nil { 263 | t.Errorf("Expected to find command `cmd'") 264 | } else { 265 | if c != p.Active { 266 | t.Errorf("Expected `cmd' to be the active parser command") 267 | } 268 | 269 | if nested := c.Find("nested"); nested == nil { 270 | t.Errorf("Expected to find command `nested'") 271 | } else if nested != c.Active { 272 | t.Errorf("Expected to find command `nested' to be the active `cmd' command") 273 | } 274 | } 275 | } 276 | 277 | func TestRequiredOnCommand(t *testing.T) { 278 | var opts = struct { 279 | Value bool `short:"v" required:"true"` 280 | 281 | Command struct { 282 | G bool `short:"g"` 283 | } `command:"cmd"` 284 | }{} 285 | 286 | assertParseFail(t, ErrRequired, fmt.Sprintf("the required flag `%cv' was not specified", defaultShortOptDelimiter), &opts, "cmd") 287 | } 288 | 289 | func TestRequiredAllOnCommand(t *testing.T) { 290 | var opts = struct { 291 | Value bool `short:"v" required:"true"` 292 | Missing bool `long:"missing" required:"true"` 293 | 294 | Command struct { 295 | G bool `short:"g"` 296 | } `command:"cmd"` 297 | }{} 298 | 299 | assertParseFail(t, ErrRequired, fmt.Sprintf("the required flags `%smissing' and `%cv' were not specified", defaultLongOptDelimiter, defaultShortOptDelimiter), &opts, "cmd") 300 | } 301 | 302 | func TestDefaultOnCommand(t *testing.T) { 303 | var opts = struct { 304 | Command struct { 305 | G bool `short:"g" default:"true"` 306 | } `command:"cmd"` 307 | }{} 308 | 309 | assertParseSuccess(t, &opts, "cmd") 310 | 311 | if !opts.Command.G { 312 | t.Errorf("Expected G to be true") 313 | } 314 | } 315 | 316 | func TestSubcommandsOptional(t *testing.T) { 317 | var opts = struct { 318 | Value bool `short:"v"` 319 | 320 | Cmd1 struct { 321 | } `command:"remove"` 322 | 323 | Cmd2 struct { 324 | } `command:"add"` 325 | }{} 326 | 327 | p := NewParser(&opts, None) 328 | p.SubcommandsOptional = true 329 | 330 | _, err := p.ParseArgs([]string{"-v"}) 331 | 332 | if err != nil { 333 | t.Fatalf("Unexpected error: %v", err) 334 | return 335 | } 336 | 337 | if !opts.Value { 338 | t.Errorf("Expected Value to be true") 339 | } 340 | } 341 | 342 | func TestCommandAlias(t *testing.T) { 343 | var opts = struct { 344 | Command struct { 345 | G bool `short:"g" default:"true"` 346 | } `command:"cmd" alias:"cm"` 347 | }{} 348 | 349 | assertParseSuccess(t, &opts, "cm") 350 | 351 | if !opts.Command.G { 352 | t.Errorf("Expected G to be true") 353 | } 354 | } 355 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jessevdk/go-flags/completion.go: -------------------------------------------------------------------------------- 1 | package flags 2 | 3 | import ( 4 | "fmt" 5 | "path/filepath" 6 | "reflect" 7 | "sort" 8 | "strings" 9 | "unicode/utf8" 10 | ) 11 | 12 | // Completion is a type containing information of a completion. 13 | type Completion struct { 14 | // The completed item 15 | Item string 16 | 17 | // A description of the completed item (optional) 18 | Description string 19 | } 20 | 21 | type completions []Completion 22 | 23 | func (c completions) Len() int { 24 | return len(c) 25 | } 26 | 27 | func (c completions) Less(i, j int) bool { 28 | return c[i].Item < c[j].Item 29 | } 30 | 31 | func (c completions) Swap(i, j int) { 32 | c[i], c[j] = c[j], c[i] 33 | } 34 | 35 | // Completer is an interface which can be implemented by types 36 | // to provide custom command line argument completion. 37 | type Completer interface { 38 | // Complete receives a prefix representing a (partial) value 39 | // for its type and should provide a list of possible valid 40 | // completions. 41 | Complete(match string) []Completion 42 | } 43 | 44 | type completion struct { 45 | parser *Parser 46 | 47 | ShowDescriptions bool 48 | } 49 | 50 | // Filename is a string alias which provides filename completion. 51 | type Filename string 52 | 53 | func completionsWithoutDescriptions(items []string) []Completion { 54 | ret := make([]Completion, len(items)) 55 | 56 | for i, v := range items { 57 | ret[i].Item = v 58 | } 59 | 60 | return ret 61 | } 62 | 63 | // Complete returns a list of existing files with the given 64 | // prefix. 65 | func (f *Filename) Complete(match string) []Completion { 66 | ret, _ := filepath.Glob(match + "*") 67 | return completionsWithoutDescriptions(ret) 68 | } 69 | 70 | func (c *completion) skipPositional(s *parseState, n int) { 71 | if n >= len(s.positional) { 72 | s.positional = nil 73 | } else { 74 | s.positional = s.positional[n:] 75 | } 76 | } 77 | 78 | func (c *completion) completeOptionNames(names map[string]*Option, prefix string, match string) []Completion { 79 | n := make([]Completion, 0, len(names)) 80 | 81 | for k, opt := range names { 82 | if strings.HasPrefix(k, match) { 83 | n = append(n, Completion{ 84 | Item: prefix + k, 85 | Description: opt.Description, 86 | }) 87 | } 88 | } 89 | 90 | return n 91 | } 92 | 93 | func (c *completion) completeLongNames(s *parseState, prefix string, match string) []Completion { 94 | return c.completeOptionNames(s.lookup.longNames, prefix, match) 95 | } 96 | 97 | func (c *completion) completeShortNames(s *parseState, prefix string, match string) []Completion { 98 | if len(match) != 0 { 99 | return []Completion{ 100 | Completion{ 101 | Item: prefix + match, 102 | }, 103 | } 104 | } 105 | 106 | return c.completeOptionNames(s.lookup.shortNames, prefix, match) 107 | } 108 | 109 | func (c *completion) completeCommands(s *parseState, match string) []Completion { 110 | n := make([]Completion, 0, len(s.command.commands)) 111 | 112 | for _, cmd := range s.command.commands { 113 | if cmd.data != c && strings.HasPrefix(cmd.Name, match) { 114 | n = append(n, Completion{ 115 | Item: cmd.Name, 116 | Description: cmd.ShortDescription, 117 | }) 118 | } 119 | } 120 | 121 | return n 122 | } 123 | 124 | func (c *completion) completeValue(value reflect.Value, prefix string, match string) []Completion { 125 | i := value.Interface() 126 | 127 | var ret []Completion 128 | 129 | if cmp, ok := i.(Completer); ok { 130 | ret = cmp.Complete(match) 131 | } else if value.CanAddr() { 132 | if cmp, ok = value.Addr().Interface().(Completer); ok { 133 | ret = cmp.Complete(match) 134 | } 135 | } 136 | 137 | for i, v := range ret { 138 | ret[i].Item = prefix + v.Item 139 | } 140 | 141 | return ret 142 | } 143 | 144 | func (c *completion) completeArg(arg *Arg, prefix string, match string) []Completion { 145 | if arg.isRemaining() { 146 | // For remaining positional args (that are parsed into a slice), complete 147 | // based on the element type. 148 | return c.completeValue(reflect.New(arg.value.Type().Elem()), prefix, match) 149 | } 150 | 151 | return c.completeValue(arg.value, prefix, match) 152 | } 153 | 154 | func (c *completion) complete(args []string) []Completion { 155 | if len(args) == 0 { 156 | args = []string{""} 157 | } 158 | 159 | s := &parseState{ 160 | args: args, 161 | } 162 | 163 | c.parser.fillParseState(s) 164 | 165 | var opt *Option 166 | 167 | for len(s.args) > 1 { 168 | arg := s.pop() 169 | 170 | if (c.parser.Options&PassDoubleDash) != None && arg == "--" { 171 | opt = nil 172 | c.skipPositional(s, len(s.args)-1) 173 | 174 | break 175 | } 176 | 177 | if argumentIsOption(arg) { 178 | prefix, optname, islong := stripOptionPrefix(arg) 179 | optname, _, argument := splitOption(prefix, optname, islong) 180 | 181 | if argument == nil { 182 | var o *Option 183 | canarg := true 184 | 185 | if islong { 186 | o = s.lookup.longNames[optname] 187 | } else { 188 | for i, r := range optname { 189 | sname := string(r) 190 | o = s.lookup.shortNames[sname] 191 | 192 | if o == nil { 193 | break 194 | } 195 | 196 | if i == 0 && o.canArgument() && len(optname) != len(sname) { 197 | canarg = false 198 | break 199 | } 200 | } 201 | } 202 | 203 | if o == nil && (c.parser.Options&PassAfterNonOption) != None { 204 | opt = nil 205 | c.skipPositional(s, len(s.args)-1) 206 | 207 | break 208 | } else if o != nil && o.canArgument() && !o.OptionalArgument && canarg { 209 | if len(s.args) > 1 { 210 | s.pop() 211 | } else { 212 | opt = o 213 | } 214 | } 215 | } 216 | } else { 217 | if len(s.positional) > 0 { 218 | if !s.positional[0].isRemaining() { 219 | // Don't advance beyond a remaining positional arg (because 220 | // it consumes all subsequent args). 221 | s.positional = s.positional[1:] 222 | } 223 | } else if cmd, ok := s.lookup.commands[arg]; ok { 224 | cmd.fillParseState(s) 225 | } 226 | 227 | opt = nil 228 | } 229 | } 230 | 231 | lastarg := s.args[len(s.args)-1] 232 | var ret []Completion 233 | 234 | if opt != nil { 235 | // Completion for the argument of 'opt' 236 | ret = c.completeValue(opt.value, "", lastarg) 237 | } else if argumentIsOption(lastarg) { 238 | // Complete the option 239 | prefix, optname, islong := stripOptionPrefix(lastarg) 240 | optname, split, argument := splitOption(prefix, optname, islong) 241 | 242 | if argument == nil && !islong { 243 | rname, n := utf8.DecodeRuneInString(optname) 244 | sname := string(rname) 245 | 246 | if opt := s.lookup.shortNames[sname]; opt != nil && opt.canArgument() { 247 | ret = c.completeValue(opt.value, prefix+sname, optname[n:]) 248 | } else { 249 | ret = c.completeShortNames(s, prefix, optname) 250 | } 251 | } else if argument != nil { 252 | if islong { 253 | opt = s.lookup.longNames[optname] 254 | } else { 255 | opt = s.lookup.shortNames[optname] 256 | } 257 | 258 | if opt != nil { 259 | ret = c.completeValue(opt.value, prefix+optname+split, *argument) 260 | } 261 | } else if islong { 262 | ret = c.completeLongNames(s, prefix, optname) 263 | } else { 264 | ret = c.completeShortNames(s, prefix, optname) 265 | } 266 | } else if len(s.positional) > 0 { 267 | // Complete for positional argument 268 | ret = c.completeArg(s.positional[0], "", lastarg) 269 | } else if len(s.command.commands) > 0 { 270 | // Complete for command 271 | ret = c.completeCommands(s, lastarg) 272 | } 273 | 274 | sort.Sort(completions(ret)) 275 | return ret 276 | } 277 | 278 | func (c *completion) execute(args []string) { 279 | ret := c.complete(args) 280 | 281 | if c.ShowDescriptions && len(ret) > 1 { 282 | maxl := 0 283 | 284 | for _, v := range ret { 285 | if len(v.Item) > maxl { 286 | maxl = len(v.Item) 287 | } 288 | } 289 | 290 | for _, v := range ret { 291 | fmt.Printf("%s", v.Item) 292 | 293 | if len(v.Description) > 0 { 294 | fmt.Printf("%s # %s", strings.Repeat(" ", maxl-len(v.Item)), v.Description) 295 | } 296 | 297 | fmt.Printf("\n") 298 | } 299 | } else { 300 | for _, v := range ret { 301 | fmt.Println(v.Item) 302 | } 303 | } 304 | } 305 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jessevdk/go-flags/completion_test.go: -------------------------------------------------------------------------------- 1 | package flags 2 | 3 | import ( 4 | "bytes" 5 | "io" 6 | "os" 7 | "path" 8 | "path/filepath" 9 | "reflect" 10 | "runtime" 11 | "strings" 12 | "testing" 13 | ) 14 | 15 | type TestComplete struct { 16 | } 17 | 18 | func (t *TestComplete) Complete(match string) []Completion { 19 | options := []string{ 20 | "hello world", 21 | "hello universe", 22 | "hello multiverse", 23 | } 24 | 25 | ret := make([]Completion, 0, len(options)) 26 | 27 | for _, o := range options { 28 | if strings.HasPrefix(o, match) { 29 | ret = append(ret, Completion{ 30 | Item: o, 31 | }) 32 | } 33 | } 34 | 35 | return ret 36 | } 37 | 38 | var completionTestOptions struct { 39 | Verbose bool `short:"v" long:"verbose" description:"Verbose messages"` 40 | Debug bool `short:"d" long:"debug" description:"Enable debug"` 41 | Version bool `long:"version" description:"Show version"` 42 | Required bool `long:"required" required:"true" description:"This is required"` 43 | 44 | AddCommand struct { 45 | Positional struct { 46 | Filename Filename 47 | } `positional-args:"yes"` 48 | } `command:"add" description:"add an item"` 49 | 50 | AddMultiCommand struct { 51 | Positional struct { 52 | Filename []Filename 53 | } `positional-args:"yes"` 54 | } `command:"add-multi" description:"add multiple items"` 55 | 56 | RemoveCommand struct { 57 | Other bool `short:"o"` 58 | File Filename `short:"f" long:"filename"` 59 | } `command:"rm" description:"remove an item"` 60 | 61 | RenameCommand struct { 62 | Completed TestComplete `short:"c" long:"completed"` 63 | } `command:"rename" description:"rename an item"` 64 | } 65 | 66 | type completionTest struct { 67 | Args []string 68 | Completed []string 69 | ShowDescriptions bool 70 | } 71 | 72 | var completionTests []completionTest 73 | 74 | func init() { 75 | _, sourcefile, _, _ := runtime.Caller(0) 76 | completionTestSourcedir := filepath.Join(filepath.SplitList(path.Dir(sourcefile))...) 77 | 78 | completionTestFilename := []string{filepath.Join(completionTestSourcedir, "completion.go"), filepath.Join(completionTestSourcedir, "completion_test.go")} 79 | 80 | completionTests = []completionTest{ 81 | { 82 | // Short names 83 | []string{"-"}, 84 | []string{"-d", "-v"}, 85 | false, 86 | }, 87 | 88 | { 89 | // Short names concatenated 90 | []string{"-dv"}, 91 | []string{"-dv"}, 92 | false, 93 | }, 94 | 95 | { 96 | // Long names 97 | []string{"--"}, 98 | []string{"--debug", "--required", "--verbose", "--version"}, 99 | false, 100 | }, 101 | 102 | { 103 | // Long names with descriptions 104 | []string{"--"}, 105 | []string{ 106 | "--debug # Enable debug", 107 | "--required # This is required", 108 | "--verbose # Verbose messages", 109 | "--version # Show version", 110 | }, 111 | true, 112 | }, 113 | 114 | { 115 | // Long names partial 116 | []string{"--ver"}, 117 | []string{"--verbose", "--version"}, 118 | false, 119 | }, 120 | 121 | { 122 | // Commands 123 | []string{""}, 124 | []string{"add", "add-multi", "rename", "rm"}, 125 | false, 126 | }, 127 | 128 | { 129 | // Commands with descriptions 130 | []string{""}, 131 | []string{ 132 | "add # add an item", 133 | "add-multi # add multiple items", 134 | "rename # rename an item", 135 | "rm # remove an item", 136 | }, 137 | true, 138 | }, 139 | 140 | { 141 | // Commands partial 142 | []string{"r"}, 143 | []string{"rename", "rm"}, 144 | false, 145 | }, 146 | 147 | { 148 | // Positional filename 149 | []string{"add", filepath.Join(completionTestSourcedir, "completion")}, 150 | completionTestFilename, 151 | false, 152 | }, 153 | 154 | { 155 | // Multiple positional filename (1 arg) 156 | []string{"add-multi", filepath.Join(completionTestSourcedir, "completion")}, 157 | completionTestFilename, 158 | false, 159 | }, 160 | { 161 | // Multiple positional filename (2 args) 162 | []string{"add-multi", filepath.Join(completionTestSourcedir, "completion.go"), filepath.Join(completionTestSourcedir, "completion")}, 163 | completionTestFilename, 164 | false, 165 | }, 166 | { 167 | // Multiple positional filename (3 args) 168 | []string{"add-multi", filepath.Join(completionTestSourcedir, "completion.go"), filepath.Join(completionTestSourcedir, "completion.go"), filepath.Join(completionTestSourcedir, "completion")}, 169 | completionTestFilename, 170 | false, 171 | }, 172 | 173 | { 174 | // Flag filename 175 | []string{"rm", "-f", path.Join(completionTestSourcedir, "completion")}, 176 | completionTestFilename, 177 | false, 178 | }, 179 | 180 | { 181 | // Flag short concat last filename 182 | []string{"rm", "-of", path.Join(completionTestSourcedir, "completion")}, 183 | completionTestFilename, 184 | false, 185 | }, 186 | 187 | { 188 | // Flag concat filename 189 | []string{"rm", "-f" + path.Join(completionTestSourcedir, "completion")}, 190 | []string{"-f" + completionTestFilename[0], "-f" + completionTestFilename[1]}, 191 | false, 192 | }, 193 | 194 | { 195 | // Flag equal concat filename 196 | []string{"rm", "-f=" + path.Join(completionTestSourcedir, "completion")}, 197 | []string{"-f=" + completionTestFilename[0], "-f=" + completionTestFilename[1]}, 198 | false, 199 | }, 200 | 201 | { 202 | // Flag concat long filename 203 | []string{"rm", "--filename=" + path.Join(completionTestSourcedir, "completion")}, 204 | []string{"--filename=" + completionTestFilename[0], "--filename=" + completionTestFilename[1]}, 205 | false, 206 | }, 207 | 208 | { 209 | // Flag long filename 210 | []string{"rm", "--filename", path.Join(completionTestSourcedir, "completion")}, 211 | completionTestFilename, 212 | false, 213 | }, 214 | 215 | { 216 | // Custom completed 217 | []string{"rename", "-c", "hello un"}, 218 | []string{"hello universe"}, 219 | false, 220 | }, 221 | } 222 | } 223 | 224 | func TestCompletion(t *testing.T) { 225 | p := NewParser(&completionTestOptions, Default) 226 | c := &completion{parser: p} 227 | 228 | for _, test := range completionTests { 229 | if test.ShowDescriptions { 230 | continue 231 | } 232 | 233 | ret := c.complete(test.Args) 234 | items := make([]string, len(ret)) 235 | 236 | for i, v := range ret { 237 | items[i] = v.Item 238 | } 239 | 240 | if !reflect.DeepEqual(items, test.Completed) { 241 | t.Errorf("Args: %#v, %#v\n Expected: %#v\n Got: %#v", test.Args, test.ShowDescriptions, test.Completed, items) 242 | } 243 | } 244 | } 245 | 246 | func TestParserCompletion(t *testing.T) { 247 | for _, test := range completionTests { 248 | if test.ShowDescriptions { 249 | os.Setenv("GO_FLAGS_COMPLETION", "verbose") 250 | } else { 251 | os.Setenv("GO_FLAGS_COMPLETION", "1") 252 | } 253 | 254 | tmp := os.Stdout 255 | 256 | r, w, _ := os.Pipe() 257 | os.Stdout = w 258 | 259 | out := make(chan string) 260 | 261 | go func() { 262 | var buf bytes.Buffer 263 | 264 | io.Copy(&buf, r) 265 | 266 | out <- buf.String() 267 | }() 268 | 269 | p := NewParser(&completionTestOptions, None) 270 | 271 | _, err := p.ParseArgs(test.Args) 272 | 273 | w.Close() 274 | 275 | os.Stdout = tmp 276 | 277 | if err != nil { 278 | t.Fatalf("Unexpected error: %s", err) 279 | } 280 | 281 | got := strings.Split(strings.Trim(<-out, "\n"), "\n") 282 | 283 | if !reflect.DeepEqual(got, test.Completed) { 284 | t.Errorf("Expected: %#v\nGot: %#v", test.Completed, got) 285 | } 286 | } 287 | 288 | os.Setenv("GO_FLAGS_COMPLETION", "") 289 | } 290 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jessevdk/go-flags/convert.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Jesse van den Kieboom. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package flags 6 | 7 | import ( 8 | "fmt" 9 | "reflect" 10 | "strconv" 11 | "strings" 12 | "time" 13 | ) 14 | 15 | // Marshaler is the interface implemented by types that can marshal themselves 16 | // to a string representation of the flag. 17 | type Marshaler interface { 18 | // MarshalFlag marshals a flag value to its string representation. 19 | MarshalFlag() (string, error) 20 | } 21 | 22 | // Unmarshaler is the interface implemented by types that can unmarshal a flag 23 | // argument to themselves. The provided value is directly passed from the 24 | // command line. 25 | type Unmarshaler interface { 26 | // UnmarshalFlag unmarshals a string value representation to the flag 27 | // value (which therefore needs to be a pointer receiver). 28 | UnmarshalFlag(value string) error 29 | } 30 | 31 | func getBase(options multiTag, base int) (int, error) { 32 | sbase := options.Get("base") 33 | 34 | var err error 35 | var ivbase int64 36 | 37 | if sbase != "" { 38 | ivbase, err = strconv.ParseInt(sbase, 10, 32) 39 | base = int(ivbase) 40 | } 41 | 42 | return base, err 43 | } 44 | 45 | func convertMarshal(val reflect.Value) (bool, string, error) { 46 | // Check first for the Marshaler interface 47 | if val.Type().NumMethod() > 0 && val.CanInterface() { 48 | if marshaler, ok := val.Interface().(Marshaler); ok { 49 | ret, err := marshaler.MarshalFlag() 50 | return true, ret, err 51 | } 52 | } 53 | 54 | return false, "", nil 55 | } 56 | 57 | func convertToString(val reflect.Value, options multiTag) (string, error) { 58 | if ok, ret, err := convertMarshal(val); ok { 59 | return ret, err 60 | } 61 | 62 | tp := val.Type() 63 | 64 | // Support for time.Duration 65 | if tp == reflect.TypeOf((*time.Duration)(nil)).Elem() { 66 | stringer := val.Interface().(fmt.Stringer) 67 | return stringer.String(), nil 68 | } 69 | 70 | switch tp.Kind() { 71 | case reflect.String: 72 | return val.String(), nil 73 | case reflect.Bool: 74 | if val.Bool() { 75 | return "true", nil 76 | } 77 | 78 | return "false", nil 79 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 80 | base, err := getBase(options, 10) 81 | 82 | if err != nil { 83 | return "", err 84 | } 85 | 86 | return strconv.FormatInt(val.Int(), base), nil 87 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 88 | base, err := getBase(options, 10) 89 | 90 | if err != nil { 91 | return "", err 92 | } 93 | 94 | return strconv.FormatUint(val.Uint(), base), nil 95 | case reflect.Float32, reflect.Float64: 96 | return strconv.FormatFloat(val.Float(), 'g', -1, tp.Bits()), nil 97 | case reflect.Slice: 98 | if val.Len() == 0 { 99 | return "", nil 100 | } 101 | 102 | ret := "[" 103 | 104 | for i := 0; i < val.Len(); i++ { 105 | if i != 0 { 106 | ret += ", " 107 | } 108 | 109 | item, err := convertToString(val.Index(i), options) 110 | 111 | if err != nil { 112 | return "", err 113 | } 114 | 115 | ret += item 116 | } 117 | 118 | return ret + "]", nil 119 | case reflect.Map: 120 | ret := "{" 121 | 122 | for i, key := range val.MapKeys() { 123 | if i != 0 { 124 | ret += ", " 125 | } 126 | 127 | keyitem, err := convertToString(key, options) 128 | 129 | if err != nil { 130 | return "", err 131 | } 132 | 133 | item, err := convertToString(val.MapIndex(key), options) 134 | 135 | if err != nil { 136 | return "", err 137 | } 138 | 139 | ret += keyitem + ":" + item 140 | } 141 | 142 | return ret + "}", nil 143 | case reflect.Ptr: 144 | return convertToString(reflect.Indirect(val), options) 145 | case reflect.Interface: 146 | if !val.IsNil() { 147 | return convertToString(val.Elem(), options) 148 | } 149 | } 150 | 151 | return "", nil 152 | } 153 | 154 | func convertUnmarshal(val string, retval reflect.Value) (bool, error) { 155 | if retval.Type().NumMethod() > 0 && retval.CanInterface() { 156 | if unmarshaler, ok := retval.Interface().(Unmarshaler); ok { 157 | return true, unmarshaler.UnmarshalFlag(val) 158 | } 159 | } 160 | 161 | if retval.Type().Kind() != reflect.Ptr && retval.CanAddr() { 162 | return convertUnmarshal(val, retval.Addr()) 163 | } 164 | 165 | if retval.Type().Kind() == reflect.Interface && !retval.IsNil() { 166 | return convertUnmarshal(val, retval.Elem()) 167 | } 168 | 169 | return false, nil 170 | } 171 | 172 | func convert(val string, retval reflect.Value, options multiTag) error { 173 | if ok, err := convertUnmarshal(val, retval); ok { 174 | return err 175 | } 176 | 177 | tp := retval.Type() 178 | 179 | // Support for time.Duration 180 | if tp == reflect.TypeOf((*time.Duration)(nil)).Elem() { 181 | parsed, err := time.ParseDuration(val) 182 | 183 | if err != nil { 184 | return err 185 | } 186 | 187 | retval.SetInt(int64(parsed)) 188 | return nil 189 | } 190 | 191 | switch tp.Kind() { 192 | case reflect.String: 193 | retval.SetString(val) 194 | case reflect.Bool: 195 | if val == "" { 196 | retval.SetBool(true) 197 | } else { 198 | b, err := strconv.ParseBool(val) 199 | 200 | if err != nil { 201 | return err 202 | } 203 | 204 | retval.SetBool(b) 205 | } 206 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 207 | base, err := getBase(options, 10) 208 | 209 | if err != nil { 210 | return err 211 | } 212 | 213 | parsed, err := strconv.ParseInt(val, base, tp.Bits()) 214 | 215 | if err != nil { 216 | return err 217 | } 218 | 219 | retval.SetInt(parsed) 220 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 221 | base, err := getBase(options, 10) 222 | 223 | if err != nil { 224 | return err 225 | } 226 | 227 | parsed, err := strconv.ParseUint(val, base, tp.Bits()) 228 | 229 | if err != nil { 230 | return err 231 | } 232 | 233 | retval.SetUint(parsed) 234 | case reflect.Float32, reflect.Float64: 235 | parsed, err := strconv.ParseFloat(val, tp.Bits()) 236 | 237 | if err != nil { 238 | return err 239 | } 240 | 241 | retval.SetFloat(parsed) 242 | case reflect.Slice: 243 | elemtp := tp.Elem() 244 | 245 | elemvalptr := reflect.New(elemtp) 246 | elemval := reflect.Indirect(elemvalptr) 247 | 248 | if err := convert(val, elemval, options); err != nil { 249 | return err 250 | } 251 | 252 | retval.Set(reflect.Append(retval, elemval)) 253 | case reflect.Map: 254 | parts := strings.SplitN(val, ":", 2) 255 | 256 | key := parts[0] 257 | var value string 258 | 259 | if len(parts) == 2 { 260 | value = parts[1] 261 | } 262 | 263 | keytp := tp.Key() 264 | keyval := reflect.New(keytp) 265 | 266 | if err := convert(key, keyval, options); err != nil { 267 | return err 268 | } 269 | 270 | valuetp := tp.Elem() 271 | valueval := reflect.New(valuetp) 272 | 273 | if err := convert(value, valueval, options); err != nil { 274 | return err 275 | } 276 | 277 | if retval.IsNil() { 278 | retval.Set(reflect.MakeMap(tp)) 279 | } 280 | 281 | retval.SetMapIndex(reflect.Indirect(keyval), reflect.Indirect(valueval)) 282 | case reflect.Ptr: 283 | if retval.IsNil() { 284 | retval.Set(reflect.New(retval.Type().Elem())) 285 | } 286 | 287 | return convert(val, reflect.Indirect(retval), options) 288 | case reflect.Interface: 289 | if !retval.IsNil() { 290 | return convert(val, retval.Elem(), options) 291 | } 292 | } 293 | 294 | return nil 295 | } 296 | 297 | func isPrint(s string) bool { 298 | for _, c := range s { 299 | if !strconv.IsPrint(c) { 300 | return false 301 | } 302 | } 303 | 304 | return true 305 | } 306 | 307 | func quoteIfNeeded(s string) string { 308 | if !isPrint(s) { 309 | return strconv.Quote(s) 310 | } 311 | 312 | return s 313 | } 314 | 315 | func unquoteIfPossible(s string) (string, error) { 316 | if len(s) == 0 || s[0] != '"' { 317 | return s, nil 318 | } 319 | 320 | return strconv.Unquote(s) 321 | } 322 | 323 | func wrapText(s string, l int, prefix string) string { 324 | // Basic text wrapping of s at spaces to fit in l 325 | var ret string 326 | 327 | s = strings.TrimSpace(s) 328 | 329 | for len(s) > l { 330 | // Try to split on space 331 | suffix := "" 332 | 333 | pos := strings.LastIndex(s[:l], " ") 334 | 335 | if pos < 0 { 336 | pos = l - 1 337 | suffix = "-\n" 338 | } 339 | 340 | if len(ret) != 0 { 341 | ret += "\n" + prefix 342 | } 343 | 344 | ret += strings.TrimSpace(s[:pos]) + suffix 345 | s = strings.TrimSpace(s[pos:]) 346 | } 347 | 348 | if len(s) > 0 { 349 | if len(ret) != 0 { 350 | ret += "\n" + prefix 351 | } 352 | 353 | return ret + s 354 | } 355 | 356 | return ret 357 | } 358 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jessevdk/go-flags/convert_test.go: -------------------------------------------------------------------------------- 1 | package flags 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | ) 7 | 8 | func expectConvert(t *testing.T, o *Option, expected string) { 9 | s, err := convertToString(o.value, o.tag) 10 | 11 | if err != nil { 12 | t.Errorf("Unexpected error: %v", err) 13 | return 14 | } 15 | 16 | assertString(t, s, expected) 17 | } 18 | 19 | func TestConvertToString(t *testing.T) { 20 | d, _ := time.ParseDuration("1h2m4s") 21 | 22 | var opts = struct { 23 | String string `long:"string"` 24 | 25 | Int int `long:"int"` 26 | Int8 int8 `long:"int8"` 27 | Int16 int16 `long:"int16"` 28 | Int32 int32 `long:"int32"` 29 | Int64 int64 `long:"int64"` 30 | 31 | Uint uint `long:"uint"` 32 | Uint8 uint8 `long:"uint8"` 33 | Uint16 uint16 `long:"uint16"` 34 | Uint32 uint32 `long:"uint32"` 35 | Uint64 uint64 `long:"uint64"` 36 | 37 | Float32 float32 `long:"float32"` 38 | Float64 float64 `long:"float64"` 39 | 40 | Duration time.Duration `long:"duration"` 41 | 42 | Bool bool `long:"bool"` 43 | 44 | IntSlice []int `long:"int-slice"` 45 | IntFloatMap map[int]float64 `long:"int-float-map"` 46 | 47 | PtrBool *bool `long:"ptr-bool"` 48 | Interface interface{} `long:"interface"` 49 | 50 | Int32Base int32 `long:"int32-base" base:"16"` 51 | Uint32Base uint32 `long:"uint32-base" base:"16"` 52 | }{ 53 | "string", 54 | 55 | -2, 56 | -1, 57 | 0, 58 | 1, 59 | 2, 60 | 61 | 1, 62 | 2, 63 | 3, 64 | 4, 65 | 5, 66 | 67 | 1.2, 68 | -3.4, 69 | 70 | d, 71 | true, 72 | 73 | []int{-3, 4, -2}, 74 | map[int]float64{-2: 4.5}, 75 | 76 | new(bool), 77 | float32(5.2), 78 | 79 | -5823, 80 | 4232, 81 | } 82 | 83 | p := NewNamedParser("test", Default) 84 | grp, _ := p.AddGroup("test group", "", &opts) 85 | 86 | expects := []string{ 87 | "string", 88 | "-2", 89 | "-1", 90 | "0", 91 | "1", 92 | "2", 93 | 94 | "1", 95 | "2", 96 | "3", 97 | "4", 98 | "5", 99 | 100 | "1.2", 101 | "-3.4", 102 | 103 | "1h2m4s", 104 | "true", 105 | 106 | "[-3, 4, -2]", 107 | "{-2:4.5}", 108 | 109 | "false", 110 | "5.2", 111 | 112 | "-16bf", 113 | "1088", 114 | } 115 | 116 | for i, v := range grp.Options() { 117 | expectConvert(t, v, expects[i]) 118 | } 119 | } 120 | 121 | func TestConvertToStringInvalidIntBase(t *testing.T) { 122 | var opts = struct { 123 | Int int `long:"int" base:"no"` 124 | }{ 125 | 2, 126 | } 127 | 128 | p := NewNamedParser("test", Default) 129 | grp, _ := p.AddGroup("test group", "", &opts) 130 | o := grp.Options()[0] 131 | 132 | _, err := convertToString(o.value, o.tag) 133 | 134 | if err != nil { 135 | err = newErrorf(ErrMarshal, "%v", err) 136 | } 137 | 138 | assertError(t, err, ErrMarshal, "strconv.ParseInt: parsing \"no\": invalid syntax") 139 | } 140 | 141 | func TestConvertToStringInvalidUintBase(t *testing.T) { 142 | var opts = struct { 143 | Uint uint `long:"uint" base:"no"` 144 | }{ 145 | 2, 146 | } 147 | 148 | p := NewNamedParser("test", Default) 149 | grp, _ := p.AddGroup("test group", "", &opts) 150 | o := grp.Options()[0] 151 | 152 | _, err := convertToString(o.value, o.tag) 153 | 154 | if err != nil { 155 | err = newErrorf(ErrMarshal, "%v", err) 156 | } 157 | 158 | assertError(t, err, ErrMarshal, "strconv.ParseInt: parsing \"no\": invalid syntax") 159 | } 160 | 161 | func TestWrapText(t *testing.T) { 162 | s := "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." 163 | 164 | got := wrapText(s, 60, " ") 165 | expected := `Lorem ipsum dolor sit amet, consectetur adipisicing elit, 166 | sed do eiusmod tempor incididunt ut labore et dolore magna 167 | aliqua. Ut enim ad minim veniam, quis nostrud exercitation 168 | ullamco laboris nisi ut aliquip ex ea commodo consequat. 169 | Duis aute irure dolor in reprehenderit in voluptate velit 170 | esse cillum dolore eu fugiat nulla pariatur. Excepteur sint 171 | occaecat cupidatat non proident, sunt in culpa qui officia 172 | deserunt mollit anim id est laborum.` 173 | 174 | assertDiff(t, got, expected, "wrapped text") 175 | } 176 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jessevdk/go-flags/error.go: -------------------------------------------------------------------------------- 1 | package flags 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // ErrorType represents the type of error. 8 | type ErrorType uint 9 | 10 | const ( 11 | // ErrUnknown indicates a generic error. 12 | ErrUnknown ErrorType = iota 13 | 14 | // ErrExpectedArgument indicates that an argument was expected. 15 | ErrExpectedArgument 16 | 17 | // ErrUnknownFlag indicates an unknown flag. 18 | ErrUnknownFlag 19 | 20 | // ErrUnknownGroup indicates an unknown group. 21 | ErrUnknownGroup 22 | 23 | // ErrMarshal indicates a marshalling error while converting values. 24 | ErrMarshal 25 | 26 | // ErrHelp indicates that the built-in help was shown (the error 27 | // contains the help message). 28 | ErrHelp 29 | 30 | // ErrNoArgumentForBool indicates that an argument was given for a 31 | // boolean flag (which don't not take any arguments). 32 | ErrNoArgumentForBool 33 | 34 | // ErrRequired indicates that a required flag was not provided. 35 | ErrRequired 36 | 37 | // ErrShortNameTooLong indicates that a short flag name was specified, 38 | // longer than one character. 39 | ErrShortNameTooLong 40 | 41 | // ErrDuplicatedFlag indicates that a short or long flag has been 42 | // defined more than once 43 | ErrDuplicatedFlag 44 | 45 | // ErrTag indicates an error while parsing flag tags. 46 | ErrTag 47 | 48 | // ErrCommandRequired indicates that a command was required but not 49 | // specified 50 | ErrCommandRequired 51 | 52 | // ErrUnknownCommand indicates that an unknown command was specified. 53 | ErrUnknownCommand 54 | ) 55 | 56 | func (e ErrorType) String() string { 57 | switch e { 58 | case ErrUnknown: 59 | return "unknown" 60 | case ErrExpectedArgument: 61 | return "expected argument" 62 | case ErrUnknownFlag: 63 | return "unknown flag" 64 | case ErrUnknownGroup: 65 | return "unknown group" 66 | case ErrMarshal: 67 | return "marshal" 68 | case ErrHelp: 69 | return "help" 70 | case ErrNoArgumentForBool: 71 | return "no argument for bool" 72 | case ErrRequired: 73 | return "required" 74 | case ErrShortNameTooLong: 75 | return "short name too long" 76 | case ErrDuplicatedFlag: 77 | return "duplicated flag" 78 | case ErrTag: 79 | return "tag" 80 | case ErrCommandRequired: 81 | return "command required" 82 | case ErrUnknownCommand: 83 | return "unknown command" 84 | } 85 | 86 | return "unrecognized error type" 87 | } 88 | 89 | // Error represents a parser error. The error returned from Parse is of this 90 | // type. The error contains both a Type and Message. 91 | type Error struct { 92 | // The type of error 93 | Type ErrorType 94 | 95 | // The error message 96 | Message string 97 | } 98 | 99 | // Error returns the error's message 100 | func (e *Error) Error() string { 101 | return e.Message 102 | } 103 | 104 | func newError(tp ErrorType, message string) *Error { 105 | return &Error{ 106 | Type: tp, 107 | Message: message, 108 | } 109 | } 110 | 111 | func newErrorf(tp ErrorType, format string, args ...interface{}) *Error { 112 | return newError(tp, fmt.Sprintf(format, args...)) 113 | } 114 | 115 | func wrapError(err error) *Error { 116 | ret, ok := err.(*Error) 117 | 118 | if !ok { 119 | return newError(ErrUnknown, err.Error()) 120 | } 121 | 122 | return ret 123 | } 124 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jessevdk/go-flags/example_test.go: -------------------------------------------------------------------------------- 1 | // Example of use of the flags package. 2 | package flags 3 | 4 | import ( 5 | "fmt" 6 | "os/exec" 7 | ) 8 | 9 | func Example() { 10 | var opts struct { 11 | // Slice of bool will append 'true' each time the option 12 | // is encountered (can be set multiple times, like -vvv) 13 | Verbose []bool `short:"v" long:"verbose" description:"Show verbose debug information"` 14 | 15 | // Example of automatic marshalling to desired type (uint) 16 | Offset uint `long:"offset" description:"Offset"` 17 | 18 | // Example of a callback, called each time the option is found. 19 | Call func(string) `short:"c" description:"Call phone number"` 20 | 21 | // Example of a required flag 22 | Name string `short:"n" long:"name" description:"A name" required:"true"` 23 | 24 | // Example of a value name 25 | File string `short:"f" long:"file" description:"A file" value-name:"FILE"` 26 | 27 | // Example of a pointer 28 | Ptr *int `short:"p" description:"A pointer to an integer"` 29 | 30 | // Example of a slice of strings 31 | StringSlice []string `short:"s" description:"A slice of strings"` 32 | 33 | // Example of a slice of pointers 34 | PtrSlice []*string `long:"ptrslice" description:"A slice of pointers to string"` 35 | 36 | // Example of a map 37 | IntMap map[string]int `long:"intmap" description:"A map from string to int"` 38 | 39 | // Example of a filename (useful for completion) 40 | Filename Filename `long:"filename" description:"A filename"` 41 | 42 | // Example of positional arguments 43 | Args struct { 44 | Id string 45 | Num int 46 | Rest []string 47 | } `positional-args:"yes" required:"yes"` 48 | } 49 | 50 | // Callback which will invoke callto: to call a number. 51 | // Note that this works just on OS X (and probably only with 52 | // Skype) but it shows the idea. 53 | opts.Call = func(num string) { 54 | cmd := exec.Command("open", "callto:"+num) 55 | cmd.Start() 56 | cmd.Process.Release() 57 | } 58 | 59 | // Make some fake arguments to parse. 60 | args := []string{ 61 | "-vv", 62 | "--offset=5", 63 | "-n", "Me", 64 | "-p", "3", 65 | "-s", "hello", 66 | "-s", "world", 67 | "--ptrslice", "hello", 68 | "--ptrslice", "world", 69 | "--intmap", "a:1", 70 | "--intmap", "b:5", 71 | "--filename", "hello.go", 72 | "id", 73 | "10", 74 | "remaining1", 75 | "remaining2", 76 | } 77 | 78 | // Parse flags from `args'. Note that here we use flags.ParseArgs for 79 | // the sake of making a working example. Normally, you would simply use 80 | // flags.Parse(&opts) which uses os.Args 81 | _, err := ParseArgs(&opts, args) 82 | 83 | if err != nil { 84 | panic(err) 85 | } 86 | 87 | fmt.Printf("Verbosity: %v\n", opts.Verbose) 88 | fmt.Printf("Offset: %d\n", opts.Offset) 89 | fmt.Printf("Name: %s\n", opts.Name) 90 | fmt.Printf("Ptr: %d\n", *opts.Ptr) 91 | fmt.Printf("StringSlice: %v\n", opts.StringSlice) 92 | fmt.Printf("PtrSlice: [%v %v]\n", *opts.PtrSlice[0], *opts.PtrSlice[1]) 93 | fmt.Printf("IntMap: [a:%v b:%v]\n", opts.IntMap["a"], opts.IntMap["b"]) 94 | fmt.Printf("Filename: %v\n", opts.Filename) 95 | fmt.Printf("Args.Id: %s\n", opts.Args.Id) 96 | fmt.Printf("Args.Num: %d\n", opts.Args.Num) 97 | fmt.Printf("Args.Rest: %v\n", opts.Args.Rest) 98 | 99 | // Output: Verbosity: [true true] 100 | // Offset: 5 101 | // Name: Me 102 | // Ptr: 3 103 | // StringSlice: [hello world] 104 | // PtrSlice: [hello world] 105 | // IntMap: [a:1 b:5] 106 | // Filename: hello.go 107 | // Args.Id: id 108 | // Args.Num: 10 109 | // Args.Rest: [remaining1 remaining2] 110 | } 111 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jessevdk/go-flags/examples/add.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type AddCommand struct { 8 | All bool `short:"a" long:"all" description:"Add all files"` 9 | } 10 | 11 | var addCommand AddCommand 12 | 13 | func (x *AddCommand) Execute(args []string) error { 14 | fmt.Printf("Adding (all=%v): %#v\n", x.All, args) 15 | return nil 16 | } 17 | 18 | func init() { 19 | parser.AddCommand("add", 20 | "Add a file", 21 | "The add command adds a file to the repository. Use -a to add all files.", 22 | &addCommand) 23 | } 24 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jessevdk/go-flags/examples/bash-completion: -------------------------------------------------------------------------------- 1 | _examples() { 2 | args=("${COMP_WORDS[@]:1:$COMP_CWORD}") 3 | 4 | local IFS=$'\n' 5 | COMPREPLY=($(GO_FLAGS_COMPLETION=1 ${COMP_WORDS[0]} "${args[@]}")) 6 | return 1 7 | } 8 | 9 | complete -F _examples examples 10 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jessevdk/go-flags/examples/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "github.com/thbishop/github-commit-status/Godeps/_workspace/src/github.com/jessevdk/go-flags" 7 | "os" 8 | "strconv" 9 | "strings" 10 | ) 11 | 12 | type EditorOptions struct { 13 | Input flags.Filename `short:"i" long:"input" description:"Input file" default:"-"` 14 | Output flags.Filename `short:"o" long:"output" description:"Output file" default:"-"` 15 | } 16 | 17 | type Point struct { 18 | X, Y int 19 | } 20 | 21 | func (p *Point) UnmarshalFlag(value string) error { 22 | parts := strings.Split(value, ",") 23 | 24 | if len(parts) != 2 { 25 | return errors.New("expected two numbers separated by a ,") 26 | } 27 | 28 | x, err := strconv.ParseInt(parts[0], 10, 32) 29 | 30 | if err != nil { 31 | return err 32 | } 33 | 34 | y, err := strconv.ParseInt(parts[1], 10, 32) 35 | 36 | if err != nil { 37 | return err 38 | } 39 | 40 | p.X = int(x) 41 | p.Y = int(y) 42 | 43 | return nil 44 | } 45 | 46 | func (p Point) MarshalFlag() (string, error) { 47 | return fmt.Sprintf("%d,%d", p.X, p.Y), nil 48 | } 49 | 50 | type Options struct { 51 | // Example of verbosity with level 52 | Verbose []bool `short:"v" long:"verbose" description:"Verbose output"` 53 | 54 | // Example of optional value 55 | User string `short:"u" long:"user" description:"User name" optional:"yes" optional-value:"pancake"` 56 | 57 | // Example of map with multiple default values 58 | Users map[string]string `long:"users" description:"User e-mail map" default:"system:system@example.org" default:"admin:admin@example.org"` 59 | 60 | // Example of option group 61 | Editor EditorOptions `group:"Editor Options"` 62 | 63 | // Example of custom type Marshal/Unmarshal 64 | Point Point `long:"point" description:"A x,y point" default:"1,2"` 65 | } 66 | 67 | var options Options 68 | 69 | var parser = flags.NewParser(&options, flags.Default) 70 | 71 | func main() { 72 | if _, err := parser.Parse(); err != nil { 73 | os.Exit(1) 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jessevdk/go-flags/examples/rm.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type RmCommand struct { 8 | Force bool `short:"f" long:"force" description:"Force removal of files"` 9 | } 10 | 11 | var rmCommand RmCommand 12 | 13 | func (x *RmCommand) Execute(args []string) error { 14 | fmt.Printf("Removing (force=%v): %#v\n", x.Force, args) 15 | return nil 16 | } 17 | 18 | func init() { 19 | parser.AddCommand("rm", 20 | "Remove a file", 21 | "The rm command removes a file to the repository. Use -f to force removal of files.", 22 | &rmCommand) 23 | } 24 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jessevdk/go-flags/flags.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Jesse van den Kieboom. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | /* 6 | Package flags provides an extensive command line option parser. 7 | The flags package is similar in functionality to the go built-in flag package 8 | but provides more options and uses reflection to provide a convenient and 9 | succinct way of specifying command line options. 10 | 11 | 12 | Supported features 13 | 14 | The following features are supported in go-flags: 15 | 16 | Options with short names (-v) 17 | Options with long names (--verbose) 18 | Options with and without arguments (bool v.s. other type) 19 | Options with optional arguments and default values 20 | Option default values from ENVIRONMENT_VARIABLES, including slice and map values 21 | Multiple option groups each containing a set of options 22 | Generate and print well-formatted help message 23 | Passing remaining command line arguments after -- (optional) 24 | Ignoring unknown command line options (optional) 25 | Supports -I/usr/include -I=/usr/include -I /usr/include option argument specification 26 | Supports multiple short options -aux 27 | Supports all primitive go types (string, int{8..64}, uint{8..64}, float) 28 | Supports same option multiple times (can store in slice or last option counts) 29 | Supports maps 30 | Supports function callbacks 31 | Supports namespaces for (nested) option groups 32 | 33 | Additional features specific to Windows: 34 | Options with short names (/v) 35 | Options with long names (/verbose) 36 | Windows-style options with arguments use a colon as the delimiter 37 | Modify generated help message with Windows-style / options 38 | 39 | 40 | Basic usage 41 | 42 | The flags package uses structs, reflection and struct field tags 43 | to allow users to specify command line options. This results in very simple 44 | and concise specification of your application options. For example: 45 | 46 | type Options struct { 47 | Verbose []bool `short:"v" long:"verbose" description:"Show verbose debug information"` 48 | } 49 | 50 | This specifies one option with a short name -v and a long name --verbose. 51 | When either -v or --verbose is found on the command line, a 'true' value 52 | will be appended to the Verbose field. e.g. when specifying -vvv, the 53 | resulting value of Verbose will be {[true, true, true]}. 54 | 55 | Slice options work exactly the same as primitive type options, except that 56 | whenever the option is encountered, a value is appended to the slice. 57 | 58 | Map options from string to primitive type are also supported. On the command 59 | line, you specify the value for such an option as key:value. For example 60 | 61 | type Options struct { 62 | AuthorInfo string[string] `short:"a"` 63 | } 64 | 65 | Then, the AuthorInfo map can be filled with something like 66 | -a name:Jesse -a "surname:van den Kieboom". 67 | 68 | Finally, for full control over the conversion between command line argument 69 | values and options, user defined types can choose to implement the Marshaler 70 | and Unmarshaler interfaces. 71 | 72 | 73 | Available field tags 74 | 75 | The following is a list of tags for struct fields supported by go-flags: 76 | 77 | short: the short name of the option (single character) 78 | long: the long name of the option 79 | required: whether an option is required to appear on the command 80 | line. If a required option is not present, the parser will 81 | return ErrRequired (optional) 82 | description: the description of the option (optional) 83 | long-description: the long description of the option. Currently only 84 | displayed in generated man pages (optional) 85 | no-flag: if non-empty this field is ignored as an option (optional) 86 | 87 | optional: whether an argument of the option is optional (optional) 88 | optional-value: the value of an optional option when the option occurs 89 | without an argument. This tag can be specified multiple 90 | times in the case of maps or slices (optional) 91 | default: the default value of an option. This tag can be specified 92 | multiple times in the case of slices or maps (optional) 93 | default-mask: when specified, this value will be displayed in the help 94 | instead of the actual default value. This is useful 95 | mostly for hiding otherwise sensitive information from 96 | showing up in the help. If default-mask takes the special 97 | value "-", then no default value will be shown at all 98 | (optional) 99 | env: the default value of the option is overridden from the 100 | specified environment variable, if one has been defined. 101 | (optional) 102 | env-delim: the 'env' default value from environment is split into 103 | multiple values with the given delimiter string, use with 104 | slices and maps (optional) 105 | value-name: the name of the argument value (to be shown in the help, 106 | (optional) 107 | 108 | base: a base (radix) used to convert strings to integer values, the 109 | default base is 10 (i.e. decimal) (optional) 110 | 111 | ini-name: the explicit ini option name (optional) 112 | no-ini: if non-empty this field is ignored as an ini option 113 | (optional) 114 | 115 | group: when specified on a struct field, makes the struct 116 | field a separate group with the given name (optional) 117 | namespace: when specified on a group struct field, the namespace 118 | gets prepended to every option's long name and 119 | subgroup's namespace of this group, separated by 120 | the parser's namespace delimiter (optional) 121 | command: when specified on a struct field, makes the struct 122 | field a (sub)command with the given name (optional) 123 | subcommands-optional: when specified on a command struct field, makes 124 | any subcommands of that command optional (optional) 125 | alias: when specified on a command struct field, adds the 126 | specified name as an alias for the command. Can be 127 | be specified multiple times to add more than one 128 | alias (optional) 129 | positional-args: when specified on a field with a struct type, 130 | uses the fields of that struct to parse remaining 131 | positional command line arguments into (in order 132 | of the fields). If a field has a slice type, 133 | then all remaining arguments will be added to it. 134 | Positional arguments are optional by default, 135 | unless the "required" tag is specified together 136 | with the "positional-args" tag (optional) 137 | 138 | Either the `short:` tag or the `long:` must be specified to make the field eligible as an 139 | option. 140 | 141 | 142 | Option groups 143 | 144 | Option groups are a simple way to semantically separate your options. All 145 | options in a particular group are shown together in the help under the name 146 | of the group. Namespaces can be used to specify option long names more 147 | precisely and emphasize the options affiliation to their group. 148 | 149 | There are currently three ways to specify option groups. 150 | 151 | 1. Use NewNamedParser specifying the various option groups. 152 | 2. Use AddGroup to add a group to an existing parser. 153 | 3. Add a struct field to the top-level options annotated with the 154 | group:"group-name" tag. 155 | 156 | 157 | 158 | Commands 159 | 160 | The flags package also has basic support for commands. Commands are often 161 | used in monolithic applications that support various commands or actions. 162 | Take git for example, all of the add, commit, checkout, etc. are called 163 | commands. Using commands you can easily separate multiple functions of your 164 | application. 165 | 166 | There are currently two ways to specify a command. 167 | 168 | 1. Use AddCommand on an existing parser. 169 | 2. Add a struct field to your options struct annotated with the 170 | command:"command-name" tag. 171 | 172 | The most common, idiomatic way to implement commands is to define a global 173 | parser instance and implement each command in a separate file. These 174 | command files should define a go init function which calls AddCommand on 175 | the global parser. 176 | 177 | When parsing ends and there is an active command and that command implements 178 | the Commander interface, then its Execute method will be run with the 179 | remaining command line arguments. 180 | 181 | Command structs can have options which become valid to parse after the 182 | command has been specified on the command line. It is currently not valid 183 | to specify options from the parent level of the command after the command 184 | name has occurred. Thus, given a top-level option "-v" and a command "add": 185 | 186 | Valid: ./app -v add 187 | Invalid: ./app add -v 188 | 189 | 190 | Completion 191 | 192 | go-flags has builtin support to provide bash completion of flags, commands 193 | and argument values. To use completion, the binary which uses go-flags 194 | can be invoked in a special environment to list completion of the current 195 | command line argument. It should be noted that this `executes` your application, 196 | and it is up to the user to make sure there are no negative side effects (for 197 | example from init functions). 198 | 199 | Setting the environment variable `GO_FLAGS_COMPLETION=1` enables completion 200 | by replacing the argument parsing routine with the completion routine which 201 | outputs completions for the passed arguments. The basic invocation to 202 | complete a set of arguments is therefore: 203 | 204 | GO_FLAGS_COMPLETION=1 ./completion-example arg1 arg2 arg3 205 | 206 | where `completion-example` is the binary, `arg1` and `arg2` are 207 | the current arguments, and `arg3` (the last argument) is the argument 208 | to be completed. If the GO_FLAGS_COMPLETION is set to "verbose", then 209 | descriptions of possible completion items will also be shown, if there 210 | are more than 1 completion items. 211 | 212 | To use this with bash completion, a simple file can be written which 213 | calls the binary which supports go-flags completion: 214 | 215 | _completion_example() { 216 | # All arguments except the first one 217 | args=("${COMP_WORDS[@]:1:$COMP_CWORD}") 218 | 219 | # Only split on newlines 220 | local IFS=$'\n' 221 | 222 | # Call completion (note that the first element of COMP_WORDS is 223 | # the executable itself) 224 | COMPREPLY=($(GO_FLAGS_COMPLETION=1 ${COMP_WORDS[0]} "${args[@]}")) 225 | return 0 226 | } 227 | 228 | complete -F _completion_example completion-example 229 | 230 | Completion requires the parser option PassDoubleDash and is therefore enforced if the environment variable GO_FLAGS_COMPLETION is set. 231 | 232 | Customized completion for argument values is supported by implementing 233 | the flags.Completer interface for the argument value type. An example 234 | of a type which does so is the flags.Filename type, an alias of string 235 | allowing simple filename completion. A slice or array argument value 236 | whose element type implements flags.Completer will also be completed. 237 | */ 238 | package flags 239 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jessevdk/go-flags/group.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Jesse van den Kieboom. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package flags 6 | 7 | import ( 8 | "errors" 9 | "strings" 10 | ) 11 | 12 | // ErrNotPointerToStruct indicates that a provided data container is not 13 | // a pointer to a struct. Only pointers to structs are valid data containers 14 | // for options. 15 | var ErrNotPointerToStruct = errors.New("provided data is not a pointer to struct") 16 | 17 | // Group represents an option group. Option groups can be used to logically 18 | // group options together under a description. Groups are only used to provide 19 | // more structure to options both for the user (as displayed in the help message) 20 | // and for you, since groups can be nested. 21 | type Group struct { 22 | // A short description of the group. The 23 | // short description is primarily used in the built-in generated help 24 | // message 25 | ShortDescription string 26 | 27 | // A long description of the group. The long 28 | // description is primarily used to present information on commands 29 | // (Command embeds Group) in the built-in generated help and man pages. 30 | LongDescription string 31 | 32 | // The namespace of the group 33 | Namespace string 34 | 35 | // The parent of the group or nil if it has no parent 36 | parent interface{} 37 | 38 | // All the options in the group 39 | options []*Option 40 | 41 | // All the subgroups 42 | groups []*Group 43 | 44 | // Whether the group represents the built-in help group 45 | isBuiltinHelp bool 46 | 47 | data interface{} 48 | } 49 | 50 | // AddGroup adds a new group to the command with the given name and data. The 51 | // data needs to be a pointer to a struct from which the fields indicate which 52 | // options are in the group. 53 | func (g *Group) AddGroup(shortDescription string, longDescription string, data interface{}) (*Group, error) { 54 | group := newGroup(shortDescription, longDescription, data) 55 | 56 | group.parent = g 57 | 58 | if err := group.scan(); err != nil { 59 | return nil, err 60 | } 61 | 62 | g.groups = append(g.groups, group) 63 | return group, nil 64 | } 65 | 66 | // Groups returns the list of groups embedded in this group. 67 | func (g *Group) Groups() []*Group { 68 | return g.groups 69 | } 70 | 71 | // Options returns the list of options in this group. 72 | func (g *Group) Options() []*Option { 73 | return g.options 74 | } 75 | 76 | // Find locates the subgroup with the given short description and returns it. 77 | // If no such group can be found Find will return nil. Note that the description 78 | // is matched case insensitively. 79 | func (g *Group) Find(shortDescription string) *Group { 80 | lshortDescription := strings.ToLower(shortDescription) 81 | 82 | var ret *Group 83 | 84 | g.eachGroup(func(gg *Group) { 85 | if gg != g && strings.ToLower(gg.ShortDescription) == lshortDescription { 86 | ret = gg 87 | } 88 | }) 89 | 90 | return ret 91 | } 92 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jessevdk/go-flags/group_private.go: -------------------------------------------------------------------------------- 1 | package flags 2 | 3 | import ( 4 | "reflect" 5 | "unicode/utf8" 6 | "unsafe" 7 | ) 8 | 9 | type scanHandler func(reflect.Value, *reflect.StructField) (bool, error) 10 | 11 | func newGroup(shortDescription string, longDescription string, data interface{}) *Group { 12 | return &Group{ 13 | ShortDescription: shortDescription, 14 | LongDescription: longDescription, 15 | 16 | data: data, 17 | } 18 | } 19 | 20 | func (g *Group) optionByName(name string, namematch func(*Option, string) bool) *Option { 21 | prio := 0 22 | var retopt *Option 23 | 24 | for _, opt := range g.options { 25 | if namematch != nil && namematch(opt, name) && prio < 4 { 26 | retopt = opt 27 | prio = 4 28 | } 29 | 30 | if name == opt.field.Name && prio < 3 { 31 | retopt = opt 32 | prio = 3 33 | } 34 | 35 | if name == opt.LongNameWithNamespace() && prio < 2 { 36 | retopt = opt 37 | prio = 2 38 | } 39 | 40 | if opt.ShortName != 0 && name == string(opt.ShortName) && prio < 1 { 41 | retopt = opt 42 | prio = 1 43 | } 44 | } 45 | 46 | return retopt 47 | } 48 | 49 | func (g *Group) eachGroup(f func(*Group)) { 50 | f(g) 51 | 52 | for _, gg := range g.groups { 53 | gg.eachGroup(f) 54 | } 55 | } 56 | 57 | func (g *Group) scanStruct(realval reflect.Value, sfield *reflect.StructField, handler scanHandler) error { 58 | stype := realval.Type() 59 | 60 | if sfield != nil { 61 | if ok, err := handler(realval, sfield); err != nil { 62 | return err 63 | } else if ok { 64 | return nil 65 | } 66 | } 67 | 68 | for i := 0; i < stype.NumField(); i++ { 69 | field := stype.Field(i) 70 | 71 | // PkgName is set only for non-exported fields, which we ignore 72 | if field.PkgPath != "" { 73 | continue 74 | } 75 | 76 | mtag := newMultiTag(string(field.Tag)) 77 | 78 | if err := mtag.Parse(); err != nil { 79 | return err 80 | } 81 | 82 | // Skip fields with the no-flag tag 83 | if mtag.Get("no-flag") != "" { 84 | continue 85 | } 86 | 87 | // Dive deep into structs or pointers to structs 88 | kind := field.Type.Kind() 89 | fld := realval.Field(i) 90 | 91 | if kind == reflect.Struct { 92 | if err := g.scanStruct(fld, &field, handler); err != nil { 93 | return err 94 | } 95 | } else if kind == reflect.Ptr && field.Type.Elem().Kind() == reflect.Struct { 96 | if fld.IsNil() { 97 | fld.Set(reflect.New(fld.Type().Elem())) 98 | } 99 | 100 | if err := g.scanStruct(reflect.Indirect(fld), &field, handler); err != nil { 101 | return err 102 | } 103 | } 104 | 105 | longname := mtag.Get("long") 106 | shortname := mtag.Get("short") 107 | 108 | // Need at least either a short or long name 109 | if longname == "" && shortname == "" && mtag.Get("ini-name") == "" { 110 | continue 111 | } 112 | 113 | short := rune(0) 114 | rc := utf8.RuneCountInString(shortname) 115 | 116 | if rc > 1 { 117 | return newErrorf(ErrShortNameTooLong, 118 | "short names can only be 1 character long, not `%s'", 119 | shortname) 120 | 121 | } else if rc == 1 { 122 | short, _ = utf8.DecodeRuneInString(shortname) 123 | } 124 | 125 | description := mtag.Get("description") 126 | def := mtag.GetMany("default") 127 | 128 | optionalValue := mtag.GetMany("optional-value") 129 | valueName := mtag.Get("value-name") 130 | defaultMask := mtag.Get("default-mask") 131 | 132 | optional := (mtag.Get("optional") != "") 133 | required := (mtag.Get("required") != "") 134 | 135 | option := &Option{ 136 | Description: description, 137 | ShortName: short, 138 | LongName: longname, 139 | Default: def, 140 | EnvDefaultKey: mtag.Get("env"), 141 | EnvDefaultDelim: mtag.Get("env-delim"), 142 | OptionalArgument: optional, 143 | OptionalValue: optionalValue, 144 | Required: required, 145 | ValueName: valueName, 146 | DefaultMask: defaultMask, 147 | 148 | group: g, 149 | 150 | field: field, 151 | value: realval.Field(i), 152 | tag: mtag, 153 | } 154 | 155 | g.options = append(g.options, option) 156 | } 157 | 158 | return nil 159 | } 160 | 161 | func (g *Group) checkForDuplicateFlags() *Error { 162 | shortNames := make(map[rune]*Option) 163 | longNames := make(map[string]*Option) 164 | 165 | var duplicateError *Error 166 | 167 | g.eachGroup(func(g *Group) { 168 | for _, option := range g.options { 169 | if option.LongName != "" { 170 | longName := option.LongNameWithNamespace() 171 | 172 | if otherOption, ok := longNames[longName]; ok { 173 | duplicateError = newErrorf(ErrDuplicatedFlag, "option `%s' uses the same long name as option `%s'", option, otherOption) 174 | return 175 | } 176 | longNames[longName] = option 177 | } 178 | if option.ShortName != 0 { 179 | if otherOption, ok := shortNames[option.ShortName]; ok { 180 | duplicateError = newErrorf(ErrDuplicatedFlag, "option `%s' uses the same short name as option `%s'", option, otherOption) 181 | return 182 | } 183 | shortNames[option.ShortName] = option 184 | } 185 | } 186 | }) 187 | 188 | return duplicateError 189 | } 190 | 191 | func (g *Group) scanSubGroupHandler(realval reflect.Value, sfield *reflect.StructField) (bool, error) { 192 | mtag := newMultiTag(string(sfield.Tag)) 193 | 194 | if err := mtag.Parse(); err != nil { 195 | return true, err 196 | } 197 | 198 | subgroup := mtag.Get("group") 199 | 200 | if len(subgroup) != 0 { 201 | ptrval := reflect.NewAt(realval.Type(), unsafe.Pointer(realval.UnsafeAddr())) 202 | description := mtag.Get("description") 203 | 204 | group, err := g.AddGroup(subgroup, description, ptrval.Interface()) 205 | if err != nil { 206 | return true, err 207 | } 208 | 209 | group.Namespace = mtag.Get("namespace") 210 | 211 | return true, nil 212 | } 213 | 214 | return false, nil 215 | } 216 | 217 | func (g *Group) scanType(handler scanHandler) error { 218 | // Get all the public fields in the data struct 219 | ptrval := reflect.ValueOf(g.data) 220 | 221 | if ptrval.Type().Kind() != reflect.Ptr { 222 | panic(ErrNotPointerToStruct) 223 | } 224 | 225 | stype := ptrval.Type().Elem() 226 | 227 | if stype.Kind() != reflect.Struct { 228 | panic(ErrNotPointerToStruct) 229 | } 230 | 231 | realval := reflect.Indirect(ptrval) 232 | 233 | if err := g.scanStruct(realval, nil, handler); err != nil { 234 | return err 235 | } 236 | 237 | if err := g.checkForDuplicateFlags(); err != nil { 238 | return err 239 | } 240 | 241 | return nil 242 | } 243 | 244 | func (g *Group) scan() error { 245 | return g.scanType(g.scanSubGroupHandler) 246 | } 247 | 248 | func (g *Group) groupByName(name string) *Group { 249 | if len(name) == 0 { 250 | return g 251 | } 252 | 253 | return g.Find(name) 254 | } 255 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jessevdk/go-flags/group_test.go: -------------------------------------------------------------------------------- 1 | package flags 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestGroupInline(t *testing.T) { 8 | var opts = struct { 9 | Value bool `short:"v"` 10 | 11 | Group struct { 12 | G bool `short:"g"` 13 | } `group:"Grouped Options"` 14 | }{} 15 | 16 | p, ret := assertParserSuccess(t, &opts, "-v", "-g") 17 | 18 | assertStringArray(t, ret, []string{}) 19 | 20 | if !opts.Value { 21 | t.Errorf("Expected Value to be true") 22 | } 23 | 24 | if !opts.Group.G { 25 | t.Errorf("Expected Group.G to be true") 26 | } 27 | 28 | if p.Command.Group.Find("Grouped Options") == nil { 29 | t.Errorf("Expected to find group `Grouped Options'") 30 | } 31 | } 32 | 33 | func TestGroupAdd(t *testing.T) { 34 | var opts = struct { 35 | Value bool `short:"v"` 36 | }{} 37 | 38 | var grp = struct { 39 | G bool `short:"g"` 40 | }{} 41 | 42 | p := NewParser(&opts, Default) 43 | g, err := p.AddGroup("Grouped Options", "", &grp) 44 | 45 | if err != nil { 46 | t.Fatalf("Unexpected error: %v", err) 47 | return 48 | } 49 | 50 | ret, err := p.ParseArgs([]string{"-v", "-g", "rest"}) 51 | 52 | if err != nil { 53 | t.Fatalf("Unexpected error: %v", err) 54 | return 55 | } 56 | 57 | assertStringArray(t, ret, []string{"rest"}) 58 | 59 | if !opts.Value { 60 | t.Errorf("Expected Value to be true") 61 | } 62 | 63 | if !grp.G { 64 | t.Errorf("Expected Group.G to be true") 65 | } 66 | 67 | if p.Command.Group.Find("Grouped Options") != g { 68 | t.Errorf("Expected to find group `Grouped Options'") 69 | } 70 | 71 | if p.Groups()[1] != g { 72 | t.Errorf("Expected group %#v, but got %#v", g, p.Groups()[0]) 73 | } 74 | 75 | if g.Options()[0].ShortName != 'g' { 76 | t.Errorf("Expected short name `g' but got %v", g.Options()[0].ShortName) 77 | } 78 | } 79 | 80 | func TestGroupNestedInline(t *testing.T) { 81 | var opts = struct { 82 | Value bool `short:"v"` 83 | 84 | Group struct { 85 | G bool `short:"g"` 86 | 87 | Nested struct { 88 | N string `long:"n"` 89 | } `group:"Nested Options"` 90 | } `group:"Grouped Options"` 91 | }{} 92 | 93 | p, ret := assertParserSuccess(t, &opts, "-v", "-g", "--n", "n", "rest") 94 | 95 | assertStringArray(t, ret, []string{"rest"}) 96 | 97 | if !opts.Value { 98 | t.Errorf("Expected Value to be true") 99 | } 100 | 101 | if !opts.Group.G { 102 | t.Errorf("Expected Group.G to be true") 103 | } 104 | 105 | assertString(t, opts.Group.Nested.N, "n") 106 | 107 | if p.Command.Group.Find("Grouped Options") == nil { 108 | t.Errorf("Expected to find group `Grouped Options'") 109 | } 110 | 111 | if p.Command.Group.Find("Nested Options") == nil { 112 | t.Errorf("Expected to find group `Nested Options'") 113 | } 114 | } 115 | 116 | func TestGroupNestedInlineNamespace(t *testing.T) { 117 | var opts = struct { 118 | Opt string `long:"opt"` 119 | 120 | Group struct { 121 | Opt string `long:"opt"` 122 | Group struct { 123 | Opt string `long:"opt"` 124 | } `group:"Subsubgroup" namespace:"sap"` 125 | } `group:"Subgroup" namespace:"sip"` 126 | }{} 127 | 128 | p, ret := assertParserSuccess(t, &opts, "--opt", "a", "--sip.opt", "b", "--sip.sap.opt", "c", "rest") 129 | 130 | assertStringArray(t, ret, []string{"rest"}) 131 | 132 | assertString(t, opts.Opt, "a") 133 | assertString(t, opts.Group.Opt, "b") 134 | assertString(t, opts.Group.Group.Opt, "c") 135 | 136 | for _, name := range []string{"Subgroup", "Subsubgroup"} { 137 | if p.Command.Group.Find(name) == nil { 138 | t.Errorf("Expected to find group '%s'", name) 139 | } 140 | } 141 | } 142 | 143 | func TestDuplicateShortFlags(t *testing.T) { 144 | var opts struct { 145 | Verbose []bool `short:"v" long:"verbose" description:"Show verbose debug information"` 146 | Variables []string `short:"v" long:"variable" description:"Set a variable value."` 147 | } 148 | 149 | args := []string{ 150 | "--verbose", 151 | "-v", "123", 152 | "-v", "456", 153 | } 154 | 155 | _, err := ParseArgs(&opts, args) 156 | 157 | if err == nil { 158 | t.Errorf("Expected an error with type ErrDuplicatedFlag") 159 | } else { 160 | err2 := err.(*Error) 161 | if err2.Type != ErrDuplicatedFlag { 162 | t.Errorf("Expected an error with type ErrDuplicatedFlag") 163 | } 164 | } 165 | } 166 | 167 | func TestDuplicateLongFlags(t *testing.T) { 168 | var opts struct { 169 | Test1 []bool `short:"a" long:"testing" description:"Test 1"` 170 | Test2 []string `short:"b" long:"testing" description:"Test 2."` 171 | } 172 | 173 | args := []string{ 174 | "--testing", 175 | } 176 | 177 | _, err := ParseArgs(&opts, args) 178 | 179 | if err == nil { 180 | t.Errorf("Expected an error with type ErrDuplicatedFlag") 181 | } else { 182 | err2 := err.(*Error) 183 | if err2.Type != ErrDuplicatedFlag { 184 | t.Errorf("Expected an error with type ErrDuplicatedFlag") 185 | } 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jessevdk/go-flags/help.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Jesse van den Kieboom. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package flags 6 | 7 | import ( 8 | "bufio" 9 | "bytes" 10 | "fmt" 11 | "io" 12 | "reflect" 13 | "runtime" 14 | "strings" 15 | "unicode/utf8" 16 | ) 17 | 18 | type alignmentInfo struct { 19 | maxLongLen int 20 | hasShort bool 21 | hasValueName bool 22 | terminalColumns int 23 | indent bool 24 | } 25 | 26 | const ( 27 | paddingBeforeOption = 2 28 | distanceBetweenOptionAndDescription = 2 29 | ) 30 | 31 | func (a *alignmentInfo) descriptionStart() int { 32 | ret := a.maxLongLen + distanceBetweenOptionAndDescription 33 | 34 | if a.hasShort { 35 | ret += 2 36 | } 37 | 38 | if a.maxLongLen > 0 { 39 | ret += 4 40 | } 41 | 42 | if a.hasValueName { 43 | ret += 3 44 | } 45 | 46 | return ret 47 | } 48 | 49 | func (a *alignmentInfo) updateLen(name string, indent bool) { 50 | l := utf8.RuneCountInString(name) 51 | 52 | if indent { 53 | l = l + 4 54 | } 55 | 56 | if l > a.maxLongLen { 57 | a.maxLongLen = l 58 | } 59 | } 60 | 61 | func (p *Parser) getAlignmentInfo() alignmentInfo { 62 | ret := alignmentInfo{ 63 | maxLongLen: 0, 64 | hasShort: false, 65 | hasValueName: false, 66 | terminalColumns: getTerminalColumns(), 67 | } 68 | 69 | if ret.terminalColumns <= 0 { 70 | ret.terminalColumns = 80 71 | } 72 | 73 | var prevcmd *Command 74 | 75 | p.eachActiveGroup(func(c *Command, grp *Group) { 76 | if c != prevcmd { 77 | for _, arg := range c.args { 78 | ret.updateLen(arg.Name, c != p.Command) 79 | } 80 | } 81 | 82 | for _, info := range grp.options { 83 | if !info.canCli() { 84 | continue 85 | } 86 | 87 | if info.ShortName != 0 { 88 | ret.hasShort = true 89 | } 90 | 91 | if len(info.ValueName) > 0 { 92 | ret.hasValueName = true 93 | } 94 | 95 | ret.updateLen(info.LongNameWithNamespace()+info.ValueName, c != p.Command) 96 | } 97 | }) 98 | 99 | return ret 100 | } 101 | 102 | func (p *Parser) writeHelpOption(writer *bufio.Writer, option *Option, info alignmentInfo) { 103 | line := &bytes.Buffer{} 104 | 105 | prefix := paddingBeforeOption 106 | 107 | if info.indent { 108 | prefix += 4 109 | } 110 | 111 | line.WriteString(strings.Repeat(" ", prefix)) 112 | 113 | if option.ShortName != 0 { 114 | line.WriteRune(defaultShortOptDelimiter) 115 | line.WriteRune(option.ShortName) 116 | } else if info.hasShort { 117 | line.WriteString(" ") 118 | } 119 | 120 | descstart := info.descriptionStart() + paddingBeforeOption 121 | 122 | if len(option.LongName) > 0 { 123 | if option.ShortName != 0 { 124 | line.WriteString(", ") 125 | } else if info.hasShort { 126 | line.WriteString(" ") 127 | } 128 | 129 | line.WriteString(defaultLongOptDelimiter) 130 | line.WriteString(option.LongNameWithNamespace()) 131 | } 132 | 133 | if option.canArgument() { 134 | line.WriteRune(defaultNameArgDelimiter) 135 | 136 | if len(option.ValueName) > 0 { 137 | line.WriteString(option.ValueName) 138 | } 139 | } 140 | 141 | written := line.Len() 142 | line.WriteTo(writer) 143 | 144 | if option.Description != "" { 145 | dw := descstart - written 146 | writer.WriteString(strings.Repeat(" ", dw)) 147 | 148 | def := "" 149 | defs := option.Default 150 | 151 | if len(option.DefaultMask) != 0 { 152 | if option.DefaultMask != "-" { 153 | def = option.DefaultMask 154 | } 155 | } else if len(defs) == 0 && option.canArgument() { 156 | var showdef bool 157 | 158 | switch option.field.Type.Kind() { 159 | case reflect.Func, reflect.Ptr: 160 | showdef = !option.value.IsNil() 161 | case reflect.Slice, reflect.String, reflect.Array: 162 | showdef = option.value.Len() > 0 163 | case reflect.Map: 164 | showdef = !option.value.IsNil() && option.value.Len() > 0 165 | default: 166 | zeroval := reflect.Zero(option.field.Type) 167 | showdef = !reflect.DeepEqual(zeroval.Interface(), option.value.Interface()) 168 | } 169 | 170 | if showdef { 171 | def, _ = convertToString(option.value, option.tag) 172 | } 173 | } else if len(defs) != 0 { 174 | l := len(defs) - 1 175 | 176 | for i := 0; i < l; i++ { 177 | def += quoteIfNeeded(defs[i]) + ", " 178 | } 179 | 180 | def += quoteIfNeeded(defs[l]) 181 | } 182 | 183 | var envDef string 184 | if option.EnvDefaultKey != "" { 185 | var envPrintable string 186 | if runtime.GOOS == "windows" { 187 | envPrintable = "%" + option.EnvDefaultKey + "%" 188 | } else { 189 | envPrintable = "$" + option.EnvDefaultKey 190 | } 191 | envDef = fmt.Sprintf(" [%s]", envPrintable) 192 | } 193 | 194 | var desc string 195 | 196 | if def != "" { 197 | desc = fmt.Sprintf("%s (%v)%s", option.Description, def, envDef) 198 | } else { 199 | desc = option.Description + envDef 200 | } 201 | 202 | writer.WriteString(wrapText(desc, 203 | info.terminalColumns-descstart, 204 | strings.Repeat(" ", descstart))) 205 | } 206 | 207 | writer.WriteString("\n") 208 | } 209 | 210 | func maxCommandLength(s []*Command) int { 211 | if len(s) == 0 { 212 | return 0 213 | } 214 | 215 | ret := len(s[0].Name) 216 | 217 | for _, v := range s[1:] { 218 | l := len(v.Name) 219 | 220 | if l > ret { 221 | ret = l 222 | } 223 | } 224 | 225 | return ret 226 | } 227 | 228 | // WriteHelp writes a help message containing all the possible options and 229 | // their descriptions to the provided writer. Note that the HelpFlag parser 230 | // option provides a convenient way to add a -h/--help option group to the 231 | // command line parser which will automatically show the help messages using 232 | // this method. 233 | func (p *Parser) WriteHelp(writer io.Writer) { 234 | if writer == nil { 235 | return 236 | } 237 | 238 | wr := bufio.NewWriter(writer) 239 | aligninfo := p.getAlignmentInfo() 240 | 241 | cmd := p.Command 242 | 243 | for cmd.Active != nil { 244 | cmd = cmd.Active 245 | } 246 | 247 | if p.Name != "" { 248 | wr.WriteString("Usage:\n") 249 | wr.WriteString(" ") 250 | 251 | allcmd := p.Command 252 | 253 | for allcmd != nil { 254 | var usage string 255 | 256 | if allcmd == p.Command { 257 | if len(p.Usage) != 0 { 258 | usage = p.Usage 259 | } else if p.Options&HelpFlag != 0 { 260 | usage = "[OPTIONS]" 261 | } 262 | } else if us, ok := allcmd.data.(Usage); ok { 263 | usage = us.Usage() 264 | } else if allcmd.hasCliOptions() { 265 | usage = fmt.Sprintf("[%s-OPTIONS]", allcmd.Name) 266 | } 267 | 268 | if len(usage) != 0 { 269 | fmt.Fprintf(wr, " %s %s", allcmd.Name, usage) 270 | } else { 271 | fmt.Fprintf(wr, " %s", allcmd.Name) 272 | } 273 | 274 | if len(allcmd.args) > 0 { 275 | fmt.Fprintf(wr, " ") 276 | } 277 | 278 | for i, arg := range allcmd.args { 279 | if i != 0 { 280 | fmt.Fprintf(wr, " ") 281 | } 282 | 283 | name := arg.Name 284 | 285 | if arg.isRemaining() { 286 | name = name + "..." 287 | } 288 | 289 | if !allcmd.ArgsRequired { 290 | fmt.Fprintf(wr, "[%s]", name) 291 | } else { 292 | fmt.Fprintf(wr, "%s", name) 293 | } 294 | } 295 | 296 | if allcmd.Active == nil && len(allcmd.commands) > 0 { 297 | var co, cc string 298 | 299 | if allcmd.SubcommandsOptional { 300 | co, cc = "[", "]" 301 | } else { 302 | co, cc = "<", ">" 303 | } 304 | 305 | if len(allcmd.commands) > 3 { 306 | fmt.Fprintf(wr, " %scommand%s", co, cc) 307 | } else { 308 | subcommands := allcmd.sortedCommands() 309 | names := make([]string, len(subcommands)) 310 | 311 | for i, subc := range subcommands { 312 | names[i] = subc.Name 313 | } 314 | 315 | fmt.Fprintf(wr, " %s%s%s", co, strings.Join(names, " | "), cc) 316 | } 317 | } 318 | 319 | allcmd = allcmd.Active 320 | } 321 | 322 | fmt.Fprintln(wr) 323 | 324 | if len(cmd.LongDescription) != 0 { 325 | fmt.Fprintln(wr) 326 | 327 | t := wrapText(cmd.LongDescription, 328 | aligninfo.terminalColumns, 329 | "") 330 | 331 | fmt.Fprintln(wr, t) 332 | } 333 | } 334 | 335 | c := p.Command 336 | 337 | for c != nil { 338 | printcmd := c != p.Command 339 | 340 | c.eachGroup(func(grp *Group) { 341 | first := true 342 | 343 | // Skip built-in help group for all commands except the top-level 344 | // parser 345 | if grp.isBuiltinHelp && c != p.Command { 346 | return 347 | } 348 | 349 | for _, info := range grp.options { 350 | if !info.canCli() { 351 | continue 352 | } 353 | 354 | if printcmd { 355 | fmt.Fprintf(wr, "\n[%s command options]\n", c.Name) 356 | aligninfo.indent = true 357 | printcmd = false 358 | } 359 | 360 | if first && cmd.Group != grp { 361 | fmt.Fprintln(wr) 362 | 363 | if aligninfo.indent { 364 | wr.WriteString(" ") 365 | } 366 | 367 | fmt.Fprintf(wr, "%s:\n", grp.ShortDescription) 368 | first = false 369 | } 370 | 371 | p.writeHelpOption(wr, info, aligninfo) 372 | } 373 | }) 374 | 375 | if len(c.args) > 0 { 376 | if c == p.Command { 377 | fmt.Fprintf(wr, "\nArguments:\n") 378 | } else { 379 | fmt.Fprintf(wr, "\n[%s command arguments]\n", c.Name) 380 | } 381 | 382 | maxlen := aligninfo.descriptionStart() 383 | 384 | for _, arg := range c.args { 385 | prefix := strings.Repeat(" ", paddingBeforeOption) 386 | fmt.Fprintf(wr, "%s%s", prefix, arg.Name) 387 | 388 | if len(arg.Description) > 0 { 389 | align := strings.Repeat(" ", maxlen-len(arg.Name)-1) 390 | fmt.Fprintf(wr, ":%s%s", align, arg.Description) 391 | } 392 | 393 | fmt.Fprintln(wr) 394 | } 395 | } 396 | 397 | c = c.Active 398 | } 399 | 400 | scommands := cmd.sortedCommands() 401 | 402 | if len(scommands) > 0 { 403 | maxnamelen := maxCommandLength(scommands) 404 | 405 | fmt.Fprintln(wr) 406 | fmt.Fprintln(wr, "Available commands:") 407 | 408 | for _, c := range scommands { 409 | fmt.Fprintf(wr, " %s", c.Name) 410 | 411 | if len(c.ShortDescription) > 0 { 412 | pad := strings.Repeat(" ", maxnamelen-len(c.Name)) 413 | fmt.Fprintf(wr, "%s %s", pad, c.ShortDescription) 414 | 415 | if len(c.Aliases) > 0 { 416 | fmt.Fprintf(wr, " (aliases: %s)", strings.Join(c.Aliases, ", ")) 417 | } 418 | 419 | } 420 | 421 | fmt.Fprintln(wr) 422 | } 423 | } 424 | 425 | wr.Flush() 426 | } 427 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jessevdk/go-flags/help_test.go: -------------------------------------------------------------------------------- 1 | package flags 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "os" 7 | "runtime" 8 | "testing" 9 | "time" 10 | ) 11 | 12 | type helpOptions struct { 13 | Verbose []bool `short:"v" long:"verbose" description:"Show verbose debug information" ini-name:"verbose"` 14 | Call func(string) `short:"c" description:"Call phone number" ini-name:"call"` 15 | PtrSlice []*string `long:"ptrslice" description:"A slice of pointers to string"` 16 | EmptyDescription bool `long:"empty-description"` 17 | 18 | Default string `long:"default" default:"Some\nvalue" description:"Test default value"` 19 | DefaultArray []string `long:"default-array" default:"Some value" default:"Other\tvalue" description:"Test default array value"` 20 | DefaultMap map[string]string `long:"default-map" default:"some:value" default:"another:value" description:"Testdefault map value"` 21 | EnvDefault1 string `long:"env-default1" default:"Some value" env:"ENV_DEFAULT" description:"Test env-default1 value"` 22 | EnvDefault2 string `long:"env-default2" env:"ENV_DEFAULT" description:"Test env-default2 value"` 23 | 24 | OnlyIni string `ini-name:"only-ini" description:"Option only available in ini"` 25 | 26 | Other struct { 27 | StringSlice []string `short:"s" default:"some" default:"value" description:"A slice of strings"` 28 | IntMap map[string]int `long:"intmap" default:"a:1" description:"A map from string to int" ini-name:"int-map"` 29 | } `group:"Other Options"` 30 | 31 | Group struct { 32 | Opt string `long:"opt" description:"This is a subgroup option"` 33 | 34 | Group struct { 35 | Opt string `long:"opt" description:"This is a subsubgroup option"` 36 | } `group:"Subsubgroup" namespace:"sap"` 37 | } `group:"Subgroup" namespace:"sip"` 38 | 39 | Command struct { 40 | ExtraVerbose []bool `long:"extra-verbose" description:"Use for extra verbosity"` 41 | } `command:"command" alias:"cm" alias:"cmd" description:"A command"` 42 | 43 | Args struct { 44 | Filename string `name:"filename" description:"A filename"` 45 | Num int `name:"num" description:"A number"` 46 | } `positional-args:"yes"` 47 | } 48 | 49 | func TestHelp(t *testing.T) { 50 | oldEnv := EnvSnapshot() 51 | defer oldEnv.Restore() 52 | os.Setenv("ENV_DEFAULT", "env-def") 53 | 54 | var opts helpOptions 55 | p := NewNamedParser("TestHelp", HelpFlag) 56 | p.AddGroup("Application Options", "The application options", &opts) 57 | 58 | _, err := p.ParseArgs([]string{"--help"}) 59 | 60 | if err == nil { 61 | t.Fatalf("Expected help error") 62 | } 63 | 64 | if e, ok := err.(*Error); !ok { 65 | t.Fatalf("Expected flags.Error, but got %T", err) 66 | } else { 67 | if e.Type != ErrHelp { 68 | t.Errorf("Expected flags.ErrHelp type, but got %s", e.Type) 69 | } 70 | 71 | var expected string 72 | 73 | if runtime.GOOS == "windows" { 74 | expected = `Usage: 75 | TestHelp [OPTIONS] [filename] [num] 76 | 77 | Application Options: 78 | /v, /verbose Show verbose debug information 79 | /c: Call phone number 80 | /ptrslice: A slice of pointers to string 81 | /empty-description 82 | /default: Test default value ("Some\nvalue") 83 | /default-array: Test default array value (Some value, "Other\tvalue") 84 | /default-map: Testdefault map value (some:value, another:value) 85 | /env-default1: Test env-default1 value (Some value) [%ENV_DEFAULT%] 86 | /env-default2: Test env-default2 value [%ENV_DEFAULT%] 87 | 88 | Other Options: 89 | /s: A slice of strings (some, value) 90 | /intmap: A map from string to int (a:1) 91 | 92 | Subgroup: 93 | /sip.opt: This is a subgroup option 94 | 95 | Subsubgroup: 96 | /sip.sap.opt: This is a subsubgroup option 97 | 98 | Help Options: 99 | /? Show this help message 100 | /h, /help Show this help message 101 | 102 | Arguments: 103 | filename: A filename 104 | num: A number 105 | 106 | Available commands: 107 | command A command (aliases: cm, cmd) 108 | ` 109 | } else { 110 | expected = `Usage: 111 | TestHelp [OPTIONS] [filename] [num] 112 | 113 | Application Options: 114 | -v, --verbose Show verbose debug information 115 | -c= Call phone number 116 | --ptrslice= A slice of pointers to string 117 | --empty-description 118 | --default= Test default value ("Some\nvalue") 119 | --default-array= Test default array value (Some value, "Other\tvalue") 120 | --default-map= Testdefault map value (some:value, another:value) 121 | --env-default1= Test env-default1 value (Some value) [$ENV_DEFAULT] 122 | --env-default2= Test env-default2 value [$ENV_DEFAULT] 123 | 124 | Other Options: 125 | -s= A slice of strings (some, value) 126 | --intmap= A map from string to int (a:1) 127 | 128 | Subgroup: 129 | --sip.opt= This is a subgroup option 130 | 131 | Subsubgroup: 132 | --sip.sap.opt= This is a subsubgroup option 133 | 134 | Help Options: 135 | -h, --help Show this help message 136 | 137 | Arguments: 138 | filename: A filename 139 | num: A number 140 | 141 | Available commands: 142 | command A command (aliases: cm, cmd) 143 | ` 144 | } 145 | 146 | assertDiff(t, e.Message, expected, "help message") 147 | } 148 | } 149 | 150 | func TestMan(t *testing.T) { 151 | oldEnv := EnvSnapshot() 152 | defer oldEnv.Restore() 153 | os.Setenv("ENV_DEFAULT", "env-def") 154 | 155 | var opts helpOptions 156 | p := NewNamedParser("TestMan", HelpFlag) 157 | p.ShortDescription = "Test manpage generation" 158 | p.LongDescription = "This is a somewhat `longer' description of what this does" 159 | p.AddGroup("Application Options", "The application options", &opts) 160 | 161 | p.Commands()[0].LongDescription = "Longer `command' description" 162 | 163 | var buf bytes.Buffer 164 | p.WriteManPage(&buf) 165 | 166 | got := buf.String() 167 | 168 | tt := time.Now() 169 | 170 | expected := fmt.Sprintf(`.TH TestMan 1 "%s" 171 | .SH NAME 172 | TestMan \- Test manpage generation 173 | .SH SYNOPSIS 174 | \fBTestMan\fP [OPTIONS] 175 | .SH DESCRIPTION 176 | This is a somewhat \fBlonger\fP description of what this does 177 | .SH OPTIONS 178 | .TP 179 | \fB-v, --verbose\fP 180 | Show verbose debug information 181 | .TP 182 | \fB-c\fP 183 | Call phone number 184 | .TP 185 | \fB--ptrslice\fP 186 | A slice of pointers to string 187 | .TP 188 | \fB--empty-description\fP 189 | .TP 190 | \fB--default\fP 191 | Test default value 192 | .TP 193 | \fB--default-array\fP 194 | Test default array value 195 | .TP 196 | \fB--default-map\fP 197 | Testdefault map value 198 | .TP 199 | \fB--env-default1\fP 200 | Test env-default1 value 201 | .TP 202 | \fB--env-default2\fP 203 | Test env-default2 value 204 | .TP 205 | \fB-s\fP 206 | A slice of strings 207 | .TP 208 | \fB--intmap\fP 209 | A map from string to int 210 | .TP 211 | \fB--sip.opt\fP 212 | This is a subgroup option 213 | .TP 214 | \fB--sip.sap.opt\fP 215 | This is a subsubgroup option 216 | .SH COMMANDS 217 | .SS command 218 | A command 219 | 220 | Longer \fBcommand\fP description 221 | 222 | \fBUsage\fP: TestMan [OPTIONS] command [command-OPTIONS] 223 | 224 | 225 | \fBAliases\fP: cm, cmd 226 | 227 | .TP 228 | \fB--extra-verbose\fP 229 | Use for extra verbosity 230 | `, tt.Format("2 January 2006")) 231 | 232 | assertDiff(t, got, expected, "man page") 233 | } 234 | 235 | type helpCommandNoOptions struct { 236 | Command struct { 237 | } `command:"command" description:"A command"` 238 | } 239 | 240 | func TestHelpCommand(t *testing.T) { 241 | oldEnv := EnvSnapshot() 242 | defer oldEnv.Restore() 243 | os.Setenv("ENV_DEFAULT", "env-def") 244 | 245 | var opts helpCommandNoOptions 246 | p := NewNamedParser("TestHelpCommand", HelpFlag) 247 | p.AddGroup("Application Options", "The application options", &opts) 248 | 249 | _, err := p.ParseArgs([]string{"command", "--help"}) 250 | 251 | if err == nil { 252 | t.Fatalf("Expected help error") 253 | } 254 | 255 | if e, ok := err.(*Error); !ok { 256 | t.Fatalf("Expected flags.Error, but got %T", err) 257 | } else { 258 | if e.Type != ErrHelp { 259 | t.Errorf("Expected flags.ErrHelp type, but got %s", e.Type) 260 | } 261 | 262 | var expected string 263 | 264 | if runtime.GOOS == "windows" { 265 | expected = `Usage: 266 | TestHelpCommand [OPTIONS] command 267 | 268 | Help Options: 269 | /? Show this help message 270 | /h, /help Show this help message 271 | ` 272 | } else { 273 | expected = `Usage: 274 | TestHelpCommand [OPTIONS] command 275 | 276 | Help Options: 277 | -h, --help Show this help message 278 | ` 279 | } 280 | 281 | assertDiff(t, e.Message, expected, "help message") 282 | } 283 | } 284 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jessevdk/go-flags/ini.go: -------------------------------------------------------------------------------- 1 | package flags 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | ) 7 | 8 | // IniError contains location information on where an error occured. 9 | type IniError struct { 10 | // The error message. 11 | Message string 12 | 13 | // The filename of the file in which the error occurred. 14 | File string 15 | 16 | // The line number at which the error occurred. 17 | LineNumber uint 18 | } 19 | 20 | // Error provides a "file:line: message" formatted message of the ini error. 21 | func (x *IniError) Error() string { 22 | return fmt.Sprintf( 23 | "%s:%d: %s", 24 | x.File, 25 | x.LineNumber, 26 | x.Message, 27 | ) 28 | } 29 | 30 | // IniOptions for writing 31 | type IniOptions uint 32 | 33 | const ( 34 | // IniNone indicates no options. 35 | IniNone IniOptions = 0 36 | 37 | // IniIncludeDefaults indicates that default values should be written. 38 | IniIncludeDefaults = 1 << iota 39 | 40 | // IniCommentDefaults indicates that if IniIncludeDefaults is used 41 | // options with default values are written but commented out. 42 | IniCommentDefaults 43 | 44 | // IniIncludeComments indicates that comments containing the description 45 | // of an option should be written. 46 | IniIncludeComments 47 | 48 | // IniDefault provides a default set of options. 49 | IniDefault = IniIncludeComments 50 | ) 51 | 52 | // IniParser is a utility to read and write flags options from and to ini 53 | // formatted strings. 54 | type IniParser struct { 55 | parser *Parser 56 | } 57 | 58 | // NewIniParser creates a new ini parser for a given Parser. 59 | func NewIniParser(p *Parser) *IniParser { 60 | return &IniParser{ 61 | parser: p, 62 | } 63 | } 64 | 65 | // IniParse is a convenience function to parse command line options with default 66 | // settings from an ini formatted file. The provided data is a pointer to a struct 67 | // representing the default option group (named "Application Options"). For 68 | // more control, use flags.NewParser. 69 | func IniParse(filename string, data interface{}) error { 70 | p := NewParser(data, Default) 71 | 72 | return NewIniParser(p).ParseFile(filename) 73 | } 74 | 75 | // ParseFile parses flags from an ini formatted file. See Parse for more 76 | // information on the ini file format. The returned errors can be of the type 77 | // flags.Error or flags.IniError. 78 | func (i *IniParser) ParseFile(filename string) error { 79 | i.parser.clearIsSet() 80 | 81 | ini, err := readIniFromFile(filename) 82 | 83 | if err != nil { 84 | return err 85 | } 86 | 87 | return i.parse(ini) 88 | } 89 | 90 | // Parse parses flags from an ini format. You can use ParseFile as a 91 | // convenience function to parse from a filename instead of a general 92 | // io.Reader. 93 | // 94 | // The format of the ini file is as follows: 95 | // 96 | // [Option group name] 97 | // option = value 98 | // 99 | // Each section in the ini file represents an option group or command in the 100 | // flags parser. The default flags parser option group (i.e. when using 101 | // flags.Parse) is named 'Application Options'. The ini option name is matched 102 | // in the following order: 103 | // 104 | // 1. Compared to the ini-name tag on the option struct field (if present) 105 | // 2. Compared to the struct field name 106 | // 3. Compared to the option long name (if present) 107 | // 4. Compared to the option short name (if present) 108 | // 109 | // Sections for nested groups and commands can be addressed using a dot `.' 110 | // namespacing notation (i.e [subcommand.Options]). Group section names are 111 | // matched case insensitive. 112 | // 113 | // The returned errors can be of the type flags.Error or flags.IniError. 114 | func (i *IniParser) Parse(reader io.Reader) error { 115 | i.parser.clearIsSet() 116 | 117 | ini, err := readIni(reader, "") 118 | 119 | if err != nil { 120 | return err 121 | } 122 | 123 | return i.parse(ini) 124 | } 125 | 126 | // WriteFile writes the flags as ini format into a file. See WriteIni 127 | // for more information. The returned error occurs when the specified file 128 | // could not be opened for writing. 129 | func (i *IniParser) WriteFile(filename string, options IniOptions) error { 130 | return writeIniToFile(i, filename, options) 131 | } 132 | 133 | // Write writes the current values of all the flags to an ini format. 134 | // See Parse for more information on the ini file format. You typically 135 | // call this only after settings have been parsed since the default values of each 136 | // option are stored just before parsing the flags (this is only relevant when 137 | // IniIncludeDefaults is _not_ set in options). 138 | func (i *IniParser) Write(writer io.Writer, options IniOptions) { 139 | writeIni(i, writer, options) 140 | } 141 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jessevdk/go-flags/ini_private.go: -------------------------------------------------------------------------------- 1 | package flags 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "io" 7 | "os" 8 | "reflect" 9 | "sort" 10 | "strconv" 11 | "strings" 12 | ) 13 | 14 | type iniValue struct { 15 | Name string 16 | Value string 17 | Quoted bool 18 | LineNumber uint 19 | } 20 | 21 | type iniSection []iniValue 22 | type ini struct { 23 | File string 24 | Sections map[string]iniSection 25 | } 26 | 27 | func readFullLine(reader *bufio.Reader) (string, error) { 28 | var line []byte 29 | 30 | for { 31 | l, more, err := reader.ReadLine() 32 | 33 | if err != nil { 34 | return "", err 35 | } 36 | 37 | if line == nil && !more { 38 | return string(l), nil 39 | } 40 | 41 | line = append(line, l...) 42 | 43 | if !more { 44 | break 45 | } 46 | } 47 | 48 | return string(line), nil 49 | } 50 | 51 | func optionIniName(option *Option) string { 52 | name := option.tag.Get("_read-ini-name") 53 | 54 | if len(name) != 0 { 55 | return name 56 | } 57 | 58 | name = option.tag.Get("ini-name") 59 | 60 | if len(name) != 0 { 61 | return name 62 | } 63 | 64 | return option.field.Name 65 | } 66 | 67 | func writeGroupIni(cmd *Command, group *Group, namespace string, writer io.Writer, options IniOptions) { 68 | var sname string 69 | 70 | if len(namespace) != 0 { 71 | sname = namespace 72 | } 73 | 74 | if cmd.Group != group && len(group.ShortDescription) != 0 { 75 | if len(sname) != 0 { 76 | sname += "." 77 | } 78 | 79 | sname += group.ShortDescription 80 | } 81 | 82 | sectionwritten := false 83 | comments := (options & IniIncludeComments) != IniNone 84 | 85 | for _, option := range group.options { 86 | if option.isFunc() { 87 | continue 88 | } 89 | 90 | if len(option.tag.Get("no-ini")) != 0 { 91 | continue 92 | } 93 | 94 | val := option.value 95 | 96 | if (options&IniIncludeDefaults) == IniNone && option.valueIsDefault() { 97 | continue 98 | } 99 | 100 | if !sectionwritten { 101 | fmt.Fprintf(writer, "[%s]\n", sname) 102 | sectionwritten = true 103 | } 104 | 105 | if comments && len(option.Description) != 0 { 106 | fmt.Fprintf(writer, "; %s\n", option.Description) 107 | } 108 | 109 | oname := optionIniName(option) 110 | 111 | commentOption := (options&(IniIncludeDefaults|IniCommentDefaults)) == IniIncludeDefaults|IniCommentDefaults && option.valueIsDefault() 112 | 113 | kind := val.Type().Kind() 114 | switch kind { 115 | case reflect.Slice: 116 | kind = val.Type().Elem().Kind() 117 | 118 | if val.Len() == 0 { 119 | writeOption(writer, oname, kind, "", "", true, option.iniQuote) 120 | } else { 121 | for idx := 0; idx < val.Len(); idx++ { 122 | v, _ := convertToString(val.Index(idx), option.tag) 123 | 124 | writeOption(writer, oname, kind, "", v, commentOption, option.iniQuote) 125 | } 126 | } 127 | case reflect.Map: 128 | kind = val.Type().Elem().Kind() 129 | 130 | if val.Len() == 0 { 131 | writeOption(writer, oname, kind, "", "", true, option.iniQuote) 132 | } else { 133 | mkeys := val.MapKeys() 134 | keys := make([]string, len(val.MapKeys())) 135 | kkmap := make(map[string]reflect.Value) 136 | 137 | for i, k := range mkeys { 138 | keys[i], _ = convertToString(k, option.tag) 139 | kkmap[keys[i]] = k 140 | } 141 | 142 | sort.Strings(keys) 143 | 144 | for _, k := range keys { 145 | v, _ := convertToString(val.MapIndex(kkmap[k]), option.tag) 146 | 147 | writeOption(writer, oname, kind, k, v, commentOption, option.iniQuote) 148 | } 149 | } 150 | default: 151 | v, _ := convertToString(val, option.tag) 152 | 153 | writeOption(writer, oname, kind, "", v, commentOption, option.iniQuote) 154 | } 155 | 156 | if comments { 157 | fmt.Fprintln(writer) 158 | } 159 | } 160 | 161 | if sectionwritten && !comments { 162 | fmt.Fprintln(writer) 163 | } 164 | } 165 | 166 | func writeOption(writer io.Writer, optionName string, optionType reflect.Kind, optionKey string, optionValue string, commentOption bool, forceQuote bool) { 167 | if forceQuote || (optionType == reflect.String && !isPrint(optionValue)) { 168 | optionValue = strconv.Quote(optionValue) 169 | } 170 | 171 | comment := "" 172 | if commentOption { 173 | comment = "; " 174 | } 175 | 176 | fmt.Fprintf(writer, "%s%s =", comment, optionName) 177 | 178 | if optionKey != "" { 179 | fmt.Fprintf(writer, " %s:%s", optionKey, optionValue) 180 | } else if optionValue != "" { 181 | fmt.Fprintf(writer, " %s", optionValue) 182 | } 183 | 184 | fmt.Fprintln(writer) 185 | } 186 | 187 | func writeCommandIni(command *Command, namespace string, writer io.Writer, options IniOptions) { 188 | command.eachGroup(func(group *Group) { 189 | writeGroupIni(command, group, namespace, writer, options) 190 | }) 191 | 192 | for _, c := range command.commands { 193 | var nns string 194 | 195 | if len(namespace) != 0 { 196 | nns = c.Name + "." + nns 197 | } else { 198 | nns = c.Name 199 | } 200 | 201 | writeCommandIni(c, nns, writer, options) 202 | } 203 | } 204 | 205 | func writeIni(parser *IniParser, writer io.Writer, options IniOptions) { 206 | writeCommandIni(parser.parser.Command, "", writer, options) 207 | } 208 | 209 | func writeIniToFile(parser *IniParser, filename string, options IniOptions) error { 210 | file, err := os.Create(filename) 211 | 212 | if err != nil { 213 | return err 214 | } 215 | 216 | defer file.Close() 217 | 218 | writeIni(parser, file, options) 219 | 220 | return nil 221 | } 222 | 223 | func readIniFromFile(filename string) (*ini, error) { 224 | file, err := os.Open(filename) 225 | 226 | if err != nil { 227 | return nil, err 228 | } 229 | 230 | defer file.Close() 231 | 232 | return readIni(file, filename) 233 | } 234 | 235 | func readIni(contents io.Reader, filename string) (*ini, error) { 236 | ret := &ini{ 237 | File: filename, 238 | Sections: make(map[string]iniSection), 239 | } 240 | 241 | reader := bufio.NewReader(contents) 242 | 243 | // Empty global section 244 | section := make(iniSection, 0, 10) 245 | sectionname := "" 246 | 247 | ret.Sections[sectionname] = section 248 | 249 | var lineno uint 250 | 251 | for { 252 | line, err := readFullLine(reader) 253 | 254 | if err == io.EOF { 255 | break 256 | } else if err != nil { 257 | return nil, err 258 | } 259 | 260 | lineno++ 261 | line = strings.TrimSpace(line) 262 | 263 | // Skip empty lines and lines starting with ; (comments) 264 | if len(line) == 0 || line[0] == ';' || line[0] == '#' { 265 | continue 266 | } 267 | 268 | if line[0] == '[' { 269 | if line[0] != '[' || line[len(line)-1] != ']' { 270 | return nil, &IniError{ 271 | Message: "malformed section header", 272 | File: filename, 273 | LineNumber: lineno, 274 | } 275 | } 276 | 277 | name := strings.TrimSpace(line[1 : len(line)-1]) 278 | 279 | if len(name) == 0 { 280 | return nil, &IniError{ 281 | Message: "empty section name", 282 | File: filename, 283 | LineNumber: lineno, 284 | } 285 | } 286 | 287 | sectionname = name 288 | section = ret.Sections[name] 289 | 290 | if section == nil { 291 | section = make(iniSection, 0, 10) 292 | ret.Sections[name] = section 293 | } 294 | 295 | continue 296 | } 297 | 298 | // Parse option here 299 | keyval := strings.SplitN(line, "=", 2) 300 | 301 | if len(keyval) != 2 { 302 | return nil, &IniError{ 303 | Message: fmt.Sprintf("malformed key=value (%s)", line), 304 | File: filename, 305 | LineNumber: lineno, 306 | } 307 | } 308 | 309 | name := strings.TrimSpace(keyval[0]) 310 | value := strings.TrimSpace(keyval[1]) 311 | quoted := false 312 | 313 | if len(value) != 0 && value[0] == '"' { 314 | if v, err := strconv.Unquote(value); err == nil { 315 | value = v 316 | 317 | quoted = true 318 | } else { 319 | return nil, &IniError{ 320 | Message: err.Error(), 321 | File: filename, 322 | LineNumber: lineno, 323 | } 324 | } 325 | } 326 | 327 | section = append(section, iniValue{ 328 | Name: name, 329 | Value: value, 330 | Quoted: quoted, 331 | LineNumber: lineno, 332 | }) 333 | 334 | ret.Sections[sectionname] = section 335 | } 336 | 337 | return ret, nil 338 | } 339 | 340 | func (i *IniParser) matchingGroups(name string) []*Group { 341 | if len(name) == 0 { 342 | var ret []*Group 343 | 344 | i.parser.eachGroup(func(g *Group) { 345 | ret = append(ret, g) 346 | }) 347 | 348 | return ret 349 | } 350 | 351 | g := i.parser.groupByName(name) 352 | 353 | if g != nil { 354 | return []*Group{g} 355 | } 356 | 357 | return nil 358 | } 359 | 360 | func (i *IniParser) parse(ini *ini) error { 361 | p := i.parser 362 | 363 | var quotesLookup = make(map[*Option]bool) 364 | 365 | for name, section := range ini.Sections { 366 | groups := i.matchingGroups(name) 367 | 368 | if len(groups) == 0 { 369 | return newErrorf(ErrUnknownGroup, "could not find option group `%s'", name) 370 | } 371 | 372 | for _, inival := range section { 373 | var opt *Option 374 | 375 | for _, group := range groups { 376 | opt = group.optionByName(inival.Name, func(o *Option, n string) bool { 377 | return strings.ToLower(o.tag.Get("ini-name")) == strings.ToLower(n) 378 | }) 379 | 380 | if opt != nil && len(opt.tag.Get("no-ini")) != 0 { 381 | opt = nil 382 | } 383 | 384 | if opt != nil { 385 | break 386 | } 387 | } 388 | 389 | if opt == nil { 390 | if (p.Options & IgnoreUnknown) == None { 391 | return &IniError{ 392 | Message: fmt.Sprintf("unknown option: %s", inival.Name), 393 | File: ini.File, 394 | LineNumber: inival.LineNumber, 395 | } 396 | } 397 | 398 | continue 399 | } 400 | 401 | pval := &inival.Value 402 | 403 | if !opt.canArgument() && len(inival.Value) == 0 { 404 | pval = nil 405 | } else { 406 | if opt.value.Type().Kind() == reflect.Map { 407 | parts := strings.SplitN(inival.Value, ":", 2) 408 | 409 | // only handle unquoting 410 | if len(parts) == 2 && parts[1][0] == '"' { 411 | if v, err := strconv.Unquote(parts[1]); err == nil { 412 | parts[1] = v 413 | 414 | inival.Quoted = true 415 | } else { 416 | return &IniError{ 417 | Message: err.Error(), 418 | File: ini.File, 419 | LineNumber: inival.LineNumber, 420 | } 421 | } 422 | 423 | s := parts[0] + ":" + parts[1] 424 | 425 | pval = &s 426 | } 427 | } 428 | } 429 | 430 | if err := opt.set(pval); err != nil { 431 | return &IniError{ 432 | Message: err.Error(), 433 | File: ini.File, 434 | LineNumber: inival.LineNumber, 435 | } 436 | } 437 | 438 | // either all INI values are quoted or only values who need quoting 439 | if _, ok := quotesLookup[opt]; !inival.Quoted || !ok { 440 | quotesLookup[opt] = inival.Quoted 441 | } 442 | 443 | opt.tag.Set("_read-ini-name", inival.Name) 444 | } 445 | } 446 | 447 | for opt, quoted := range quotesLookup { 448 | opt.iniQuote = quoted 449 | } 450 | 451 | return nil 452 | } 453 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jessevdk/go-flags/long_test.go: -------------------------------------------------------------------------------- 1 | package flags 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestLong(t *testing.T) { 8 | var opts = struct { 9 | Value bool `long:"value"` 10 | }{} 11 | 12 | ret := assertParseSuccess(t, &opts, "--value") 13 | 14 | assertStringArray(t, ret, []string{}) 15 | 16 | if !opts.Value { 17 | t.Errorf("Expected Value to be true") 18 | } 19 | } 20 | 21 | func TestLongArg(t *testing.T) { 22 | var opts = struct { 23 | Value string `long:"value"` 24 | }{} 25 | 26 | ret := assertParseSuccess(t, &opts, "--value", "value") 27 | 28 | assertStringArray(t, ret, []string{}) 29 | assertString(t, opts.Value, "value") 30 | } 31 | 32 | func TestLongArgEqual(t *testing.T) { 33 | var opts = struct { 34 | Value string `long:"value"` 35 | }{} 36 | 37 | ret := assertParseSuccess(t, &opts, "--value=value") 38 | 39 | assertStringArray(t, ret, []string{}) 40 | assertString(t, opts.Value, "value") 41 | } 42 | 43 | func TestLongDefault(t *testing.T) { 44 | var opts = struct { 45 | Value string `long:"value" default:"value"` 46 | }{} 47 | 48 | ret := assertParseSuccess(t, &opts) 49 | 50 | assertStringArray(t, ret, []string{}) 51 | assertString(t, opts.Value, "value") 52 | } 53 | 54 | func TestLongOptional(t *testing.T) { 55 | var opts = struct { 56 | Value string `long:"value" optional:"yes" optional-value:"value"` 57 | }{} 58 | 59 | ret := assertParseSuccess(t, &opts, "--value") 60 | 61 | assertStringArray(t, ret, []string{}) 62 | assertString(t, opts.Value, "value") 63 | } 64 | 65 | func TestLongOptionalArg(t *testing.T) { 66 | var opts = struct { 67 | Value string `long:"value" optional:"yes" optional-value:"value"` 68 | }{} 69 | 70 | ret := assertParseSuccess(t, &opts, "--value", "no") 71 | 72 | assertStringArray(t, ret, []string{"no"}) 73 | assertString(t, opts.Value, "value") 74 | } 75 | 76 | func TestLongOptionalArgEqual(t *testing.T) { 77 | var opts = struct { 78 | Value string `long:"value" optional:"yes" optional-value:"value"` 79 | }{} 80 | 81 | ret := assertParseSuccess(t, &opts, "--value=value", "no") 82 | 83 | assertStringArray(t, ret, []string{"no"}) 84 | assertString(t, opts.Value, "value") 85 | } 86 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jessevdk/go-flags/man.go: -------------------------------------------------------------------------------- 1 | package flags 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "strings" 7 | "time" 8 | ) 9 | 10 | func formatForMan(wr io.Writer, s string) { 11 | for { 12 | idx := strings.IndexRune(s, '`') 13 | 14 | if idx < 0 { 15 | fmt.Fprintf(wr, "%s", s) 16 | break 17 | } 18 | 19 | fmt.Fprintf(wr, "%s", s[:idx]) 20 | 21 | s = s[idx+1:] 22 | idx = strings.IndexRune(s, '\'') 23 | 24 | if idx < 0 { 25 | fmt.Fprintf(wr, "%s", s) 26 | break 27 | } 28 | 29 | fmt.Fprintf(wr, "\\fB%s\\fP", s[:idx]) 30 | s = s[idx+1:] 31 | } 32 | } 33 | 34 | func writeManPageOptions(wr io.Writer, grp *Group) { 35 | grp.eachGroup(func(group *Group) { 36 | for _, opt := range group.options { 37 | if !opt.canCli() { 38 | continue 39 | } 40 | 41 | fmt.Fprintln(wr, ".TP") 42 | fmt.Fprintf(wr, "\\fB") 43 | 44 | if opt.ShortName != 0 { 45 | fmt.Fprintf(wr, "-%c", opt.ShortName) 46 | } 47 | 48 | if len(opt.LongName) != 0 { 49 | if opt.ShortName != 0 { 50 | fmt.Fprintf(wr, ", ") 51 | } 52 | 53 | fmt.Fprintf(wr, "--%s", opt.LongNameWithNamespace()) 54 | } 55 | 56 | fmt.Fprintln(wr, "\\fP") 57 | if len(opt.Description) != 0 { 58 | formatForMan(wr, opt.Description) 59 | fmt.Fprintln(wr, "") 60 | } 61 | } 62 | }) 63 | } 64 | 65 | func writeManPageSubcommands(wr io.Writer, name string, root *Command) { 66 | commands := root.sortedCommands() 67 | 68 | for _, c := range commands { 69 | var nn string 70 | 71 | if len(name) != 0 { 72 | nn = name + " " + c.Name 73 | } else { 74 | nn = c.Name 75 | } 76 | 77 | writeManPageCommand(wr, nn, root, c) 78 | } 79 | } 80 | 81 | func writeManPageCommand(wr io.Writer, name string, root *Command, command *Command) { 82 | fmt.Fprintf(wr, ".SS %s\n", name) 83 | fmt.Fprintln(wr, command.ShortDescription) 84 | 85 | if len(command.LongDescription) > 0 { 86 | fmt.Fprintln(wr, "") 87 | 88 | cmdstart := fmt.Sprintf("The %s command", command.Name) 89 | 90 | if strings.HasPrefix(command.LongDescription, cmdstart) { 91 | fmt.Fprintf(wr, "The \\fI%s\\fP command", command.Name) 92 | 93 | formatForMan(wr, command.LongDescription[len(cmdstart):]) 94 | fmt.Fprintln(wr, "") 95 | } else { 96 | formatForMan(wr, command.LongDescription) 97 | fmt.Fprintln(wr, "") 98 | } 99 | } 100 | 101 | var usage string 102 | if us, ok := command.data.(Usage); ok { 103 | usage = us.Usage() 104 | } else if command.hasCliOptions() { 105 | usage = fmt.Sprintf("[%s-OPTIONS]", command.Name) 106 | } 107 | 108 | var pre string 109 | if root.hasCliOptions() { 110 | pre = fmt.Sprintf("%s [OPTIONS] %s", root.Name, command.Name) 111 | } else { 112 | pre = fmt.Sprintf("%s %s", root.Name, command.Name) 113 | } 114 | 115 | if len(usage) > 0 { 116 | fmt.Fprintf(wr, "\n\\fBUsage\\fP: %s %s\n\n", pre, usage) 117 | } 118 | 119 | if len(command.Aliases) > 0 { 120 | fmt.Fprintf(wr, "\n\\fBAliases\\fP: %s\n\n", strings.Join(command.Aliases, ", ")) 121 | } 122 | 123 | writeManPageOptions(wr, command.Group) 124 | writeManPageSubcommands(wr, name, command) 125 | } 126 | 127 | // WriteManPage writes a basic man page in groff format to the specified 128 | // writer. 129 | func (p *Parser) WriteManPage(wr io.Writer) { 130 | t := time.Now() 131 | 132 | fmt.Fprintf(wr, ".TH %s 1 \"%s\"\n", p.Name, t.Format("2 January 2006")) 133 | fmt.Fprintln(wr, ".SH NAME") 134 | fmt.Fprintf(wr, "%s \\- %s\n", p.Name, p.ShortDescription) 135 | fmt.Fprintln(wr, ".SH SYNOPSIS") 136 | 137 | usage := p.Usage 138 | 139 | if len(usage) == 0 { 140 | usage = "[OPTIONS]" 141 | } 142 | 143 | fmt.Fprintf(wr, "\\fB%s\\fP %s\n", p.Name, usage) 144 | fmt.Fprintln(wr, ".SH DESCRIPTION") 145 | 146 | formatForMan(wr, p.LongDescription) 147 | fmt.Fprintln(wr, "") 148 | 149 | fmt.Fprintln(wr, ".SH OPTIONS") 150 | 151 | writeManPageOptions(wr, p.Command.Group) 152 | 153 | if len(p.commands) > 0 { 154 | fmt.Fprintln(wr, ".SH COMMANDS") 155 | 156 | writeManPageSubcommands(wr, "", p.Command) 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jessevdk/go-flags/marshal_test.go: -------------------------------------------------------------------------------- 1 | package flags 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | type marshalled bool 9 | 10 | func (m *marshalled) UnmarshalFlag(value string) error { 11 | if value == "yes" { 12 | *m = true 13 | } else if value == "no" { 14 | *m = false 15 | } else { 16 | return fmt.Errorf("`%s' is not a valid value, please specify `yes' or `no'", value) 17 | } 18 | 19 | return nil 20 | } 21 | 22 | func (m marshalled) MarshalFlag() (string, error) { 23 | if m { 24 | return "yes", nil 25 | } 26 | 27 | return "no", nil 28 | } 29 | 30 | type marshalledError bool 31 | 32 | func (m marshalledError) MarshalFlag() (string, error) { 33 | return "", newErrorf(ErrMarshal, "Failed to marshal") 34 | } 35 | 36 | func TestUnmarshal(t *testing.T) { 37 | var opts = struct { 38 | Value marshalled `short:"v"` 39 | }{} 40 | 41 | ret := assertParseSuccess(t, &opts, "-v=yes") 42 | 43 | assertStringArray(t, ret, []string{}) 44 | 45 | if !opts.Value { 46 | t.Errorf("Expected Value to be true") 47 | } 48 | } 49 | 50 | func TestUnmarshalDefault(t *testing.T) { 51 | var opts = struct { 52 | Value marshalled `short:"v" default:"yes"` 53 | }{} 54 | 55 | ret := assertParseSuccess(t, &opts) 56 | 57 | assertStringArray(t, ret, []string{}) 58 | 59 | if !opts.Value { 60 | t.Errorf("Expected Value to be true") 61 | } 62 | } 63 | 64 | func TestUnmarshalOptional(t *testing.T) { 65 | var opts = struct { 66 | Value marshalled `short:"v" optional:"yes" optional-value:"yes"` 67 | }{} 68 | 69 | ret := assertParseSuccess(t, &opts, "-v") 70 | 71 | assertStringArray(t, ret, []string{}) 72 | 73 | if !opts.Value { 74 | t.Errorf("Expected Value to be true") 75 | } 76 | } 77 | 78 | func TestUnmarshalError(t *testing.T) { 79 | var opts = struct { 80 | Value marshalled `short:"v"` 81 | }{} 82 | 83 | assertParseFail(t, ErrMarshal, fmt.Sprintf("invalid argument for flag `%cv' (expected flags.marshalled): `invalid' is not a valid value, please specify `yes' or `no'", defaultShortOptDelimiter), &opts, "-vinvalid") 84 | } 85 | 86 | func TestMarshalError(t *testing.T) { 87 | var opts = struct { 88 | Value marshalledError `short:"v"` 89 | }{} 90 | 91 | p := NewParser(&opts, Default) 92 | o := p.Command.Groups()[0].Options()[0] 93 | 94 | _, err := convertToString(o.value, o.tag) 95 | 96 | assertError(t, err, ErrMarshal, "Failed to marshal") 97 | } 98 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jessevdk/go-flags/multitag.go: -------------------------------------------------------------------------------- 1 | package flags 2 | 3 | import ( 4 | "strconv" 5 | ) 6 | 7 | type multiTag struct { 8 | value string 9 | cache map[string][]string 10 | } 11 | 12 | func newMultiTag(v string) multiTag { 13 | return multiTag{ 14 | value: v, 15 | } 16 | } 17 | 18 | func (x *multiTag) scan() (map[string][]string, error) { 19 | v := x.value 20 | 21 | ret := make(map[string][]string) 22 | 23 | // This is mostly copied from reflect.StructTag.Get 24 | for v != "" { 25 | i := 0 26 | 27 | // Skip whitespace 28 | for i < len(v) && v[i] == ' ' { 29 | i++ 30 | } 31 | 32 | v = v[i:] 33 | 34 | if v == "" { 35 | break 36 | } 37 | 38 | // Scan to colon to find key 39 | i = 0 40 | 41 | for i < len(v) && v[i] != ' ' && v[i] != ':' && v[i] != '"' { 42 | i++ 43 | } 44 | 45 | if i >= len(v) { 46 | return nil, newErrorf(ErrTag, "expected `:' after key name, but got end of tag (in `%v`)", x.value) 47 | } 48 | 49 | if v[i] != ':' { 50 | return nil, newErrorf(ErrTag, "expected `:' after key name, but got `%v' (in `%v`)", v[i], x.value) 51 | } 52 | 53 | if i+1 >= len(v) { 54 | return nil, newErrorf(ErrTag, "expected `\"' to start tag value at end of tag (in `%v`)", x.value) 55 | } 56 | 57 | if v[i+1] != '"' { 58 | return nil, newErrorf(ErrTag, "expected `\"' to start tag value, but got `%v' (in `%v`)", v[i+1], x.value) 59 | } 60 | 61 | name := v[:i] 62 | v = v[i+1:] 63 | 64 | // Scan quoted string to find value 65 | i = 1 66 | 67 | for i < len(v) && v[i] != '"' { 68 | if v[i] == '\n' { 69 | return nil, newErrorf(ErrTag, "unexpected newline in tag value `%v' (in `%v`)", name, x.value) 70 | } 71 | 72 | if v[i] == '\\' { 73 | i++ 74 | } 75 | i++ 76 | } 77 | 78 | if i >= len(v) { 79 | return nil, newErrorf(ErrTag, "expected end of tag value `\"' at end of tag (in `%v`)", x.value) 80 | } 81 | 82 | val, err := strconv.Unquote(v[:i+1]) 83 | 84 | if err != nil { 85 | return nil, newErrorf(ErrTag, "Malformed value of tag `%v:%v` => %v (in `%v`)", name, v[:i+1], err, x.value) 86 | } 87 | 88 | v = v[i+1:] 89 | 90 | ret[name] = append(ret[name], val) 91 | } 92 | 93 | return ret, nil 94 | } 95 | 96 | func (x *multiTag) Parse() error { 97 | vals, err := x.scan() 98 | x.cache = vals 99 | 100 | return err 101 | } 102 | 103 | func (x *multiTag) cached() map[string][]string { 104 | if x.cache == nil { 105 | cache, _ := x.scan() 106 | 107 | if cache == nil { 108 | cache = make(map[string][]string) 109 | } 110 | 111 | x.cache = cache 112 | } 113 | 114 | return x.cache 115 | } 116 | 117 | func (x *multiTag) Get(key string) string { 118 | c := x.cached() 119 | 120 | if v, ok := c[key]; ok { 121 | return v[len(v)-1] 122 | } 123 | 124 | return "" 125 | } 126 | 127 | func (x *multiTag) GetMany(key string) []string { 128 | c := x.cached() 129 | return c[key] 130 | } 131 | 132 | func (x *multiTag) Set(key string, value string) { 133 | c := x.cached() 134 | c[key] = []string{value} 135 | } 136 | 137 | func (x *multiTag) SetMany(key string, value []string) { 138 | c := x.cached() 139 | c[key] = value 140 | } 141 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jessevdk/go-flags/option.go: -------------------------------------------------------------------------------- 1 | package flags 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "unicode/utf8" 7 | ) 8 | 9 | // Option flag information. Contains a description of the option, short and 10 | // long name as well as a default value and whether an argument for this 11 | // flag is optional. 12 | type Option struct { 13 | // The description of the option flag. This description is shown 14 | // automatically in the built-in help. 15 | Description string 16 | 17 | // The short name of the option (a single character). If not 0, the 18 | // option flag can be 'activated' using -. Either ShortName 19 | // or LongName needs to be non-empty. 20 | ShortName rune 21 | 22 | // The long name of the option. If not "", the option flag can be 23 | // activated using --. Either ShortName or LongName needs 24 | // to be non-empty. 25 | LongName string 26 | 27 | // The default value of the option. 28 | Default []string 29 | 30 | // The optional environment default value key name. 31 | EnvDefaultKey string 32 | 33 | // The optional delimiter string for EnvDefaultKey values. 34 | EnvDefaultDelim string 35 | 36 | // If true, specifies that the argument to an option flag is optional. 37 | // When no argument to the flag is specified on the command line, the 38 | // value of Default will be set in the field this option represents. 39 | // This is only valid for non-boolean options. 40 | OptionalArgument bool 41 | 42 | // The optional value of the option. The optional value is used when 43 | // the option flag is marked as having an OptionalArgument. This means 44 | // that when the flag is specified, but no option argument is given, 45 | // the value of the field this option represents will be set to 46 | // OptionalValue. This is only valid for non-boolean options. 47 | OptionalValue []string 48 | 49 | // If true, the option _must_ be specified on the command line. If the 50 | // option is not specified, the parser will generate an ErrRequired type 51 | // error. 52 | Required bool 53 | 54 | // A name for the value of an option shown in the Help as --flag [ValueName] 55 | ValueName string 56 | 57 | // A mask value to show in the help instead of the default value. This 58 | // is useful for hiding sensitive information in the help, such as 59 | // passwords. 60 | DefaultMask string 61 | 62 | // The group which the option belongs to 63 | group *Group 64 | 65 | // The struct field which the option represents. 66 | field reflect.StructField 67 | 68 | // The struct field value which the option represents. 69 | value reflect.Value 70 | 71 | // Determines if the option will be always quoted in the INI output 72 | iniQuote bool 73 | 74 | tag multiTag 75 | isSet bool 76 | } 77 | 78 | // LongNameWithNamespace returns the option's long name with the group namespaces 79 | // prepended by walking up the option's group tree. Namespaces and the long name 80 | // itself are separated by the parser's namespace delimiter. If the long name is 81 | // empty an empty string is returned. 82 | func (option *Option) LongNameWithNamespace() string { 83 | if len(option.LongName) == 0 { 84 | return "" 85 | } 86 | 87 | // fetch the namespace delimiter from the parser which is always at the 88 | // end of the group hierarchy 89 | namespaceDelimiter := "" 90 | g := option.group 91 | 92 | for { 93 | if p, ok := g.parent.(*Parser); ok { 94 | namespaceDelimiter = p.NamespaceDelimiter 95 | 96 | break 97 | } 98 | 99 | switch i := g.parent.(type) { 100 | case *Command: 101 | g = i.Group 102 | case *Group: 103 | g = i 104 | } 105 | } 106 | 107 | // concatenate long name with namespace 108 | longName := option.LongName 109 | g = option.group 110 | 111 | for g != nil { 112 | if g.Namespace != "" { 113 | longName = g.Namespace + namespaceDelimiter + longName 114 | } 115 | 116 | switch i := g.parent.(type) { 117 | case *Command: 118 | g = i.Group 119 | case *Group: 120 | g = i 121 | case *Parser: 122 | g = nil 123 | } 124 | } 125 | 126 | return longName 127 | } 128 | 129 | // String converts an option to a human friendly readable string describing the 130 | // option. 131 | func (option *Option) String() string { 132 | var s string 133 | var short string 134 | 135 | if option.ShortName != 0 { 136 | data := make([]byte, utf8.RuneLen(option.ShortName)) 137 | utf8.EncodeRune(data, option.ShortName) 138 | short = string(data) 139 | 140 | if len(option.LongName) != 0 { 141 | s = fmt.Sprintf("%s%s, %s%s", 142 | string(defaultShortOptDelimiter), short, 143 | defaultLongOptDelimiter, option.LongNameWithNamespace()) 144 | } else { 145 | s = fmt.Sprintf("%s%s", string(defaultShortOptDelimiter), short) 146 | } 147 | } else if len(option.LongName) != 0 { 148 | s = fmt.Sprintf("%s%s", defaultLongOptDelimiter, option.LongNameWithNamespace()) 149 | } 150 | 151 | return s 152 | } 153 | 154 | // Value returns the option value as an interface{}. 155 | func (option *Option) Value() interface{} { 156 | return option.value.Interface() 157 | } 158 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jessevdk/go-flags/option_private.go: -------------------------------------------------------------------------------- 1 | package flags 2 | 3 | import ( 4 | "reflect" 5 | "strings" 6 | "syscall" 7 | ) 8 | 9 | // Set the value of an option to the specified value. An error will be returned 10 | // if the specified value could not be converted to the corresponding option 11 | // value type. 12 | func (option *Option) set(value *string) error { 13 | option.isSet = true 14 | 15 | if option.isFunc() { 16 | return option.call(value) 17 | } else if value != nil { 18 | return convert(*value, option.value, option.tag) 19 | } 20 | 21 | return convert("", option.value, option.tag) 22 | } 23 | 24 | func (option *Option) canCli() bool { 25 | return option.ShortName != 0 || len(option.LongName) != 0 26 | } 27 | 28 | func (option *Option) canArgument() bool { 29 | if u := option.isUnmarshaler(); u != nil { 30 | return true 31 | } 32 | 33 | return !option.isBool() 34 | } 35 | 36 | func (option *Option) emptyValue() reflect.Value { 37 | tp := option.value.Type() 38 | 39 | if tp.Kind() == reflect.Map { 40 | return reflect.MakeMap(tp) 41 | } 42 | 43 | return reflect.Zero(tp) 44 | } 45 | 46 | func (option *Option) empty() { 47 | if !option.isFunc() { 48 | option.value.Set(option.emptyValue()) 49 | } 50 | } 51 | 52 | func (option *Option) clearDefault() { 53 | usedDefault := option.Default 54 | if envKey := option.EnvDefaultKey; envKey != "" { 55 | // os.Getenv() makes no distinction between undefined and 56 | // empty values, so we use syscall.Getenv() 57 | if value, ok := syscall.Getenv(envKey); ok { 58 | if option.EnvDefaultDelim != "" { 59 | usedDefault = strings.Split(value, 60 | option.EnvDefaultDelim) 61 | } else { 62 | usedDefault = []string{value} 63 | } 64 | } 65 | } 66 | 67 | if len(usedDefault) > 0 { 68 | option.empty() 69 | 70 | for _, d := range usedDefault { 71 | option.set(&d) 72 | } 73 | } else { 74 | tp := option.value.Type() 75 | 76 | switch tp.Kind() { 77 | case reflect.Map: 78 | if option.value.IsNil() { 79 | option.empty() 80 | } 81 | case reflect.Slice: 82 | if option.value.IsNil() { 83 | option.empty() 84 | } 85 | } 86 | } 87 | } 88 | 89 | func (option *Option) valueIsDefault() bool { 90 | // Check if the value of the option corresponds to its 91 | // default value 92 | emptyval := option.emptyValue() 93 | 94 | checkvalptr := reflect.New(emptyval.Type()) 95 | checkval := reflect.Indirect(checkvalptr) 96 | 97 | checkval.Set(emptyval) 98 | 99 | if len(option.Default) != 0 { 100 | for _, v := range option.Default { 101 | convert(v, checkval, option.tag) 102 | } 103 | } 104 | 105 | return reflect.DeepEqual(option.value.Interface(), checkval.Interface()) 106 | } 107 | 108 | func (option *Option) isUnmarshaler() Unmarshaler { 109 | v := option.value 110 | 111 | for { 112 | if !v.CanInterface() { 113 | break 114 | } 115 | 116 | i := v.Interface() 117 | 118 | if u, ok := i.(Unmarshaler); ok { 119 | return u 120 | } 121 | 122 | if !v.CanAddr() { 123 | break 124 | } 125 | 126 | v = v.Addr() 127 | } 128 | 129 | return nil 130 | } 131 | 132 | func (option *Option) isBool() bool { 133 | tp := option.value.Type() 134 | 135 | for { 136 | switch tp.Kind() { 137 | case reflect.Bool: 138 | return true 139 | case reflect.Slice: 140 | return (tp.Elem().Kind() == reflect.Bool) 141 | case reflect.Func: 142 | return tp.NumIn() == 0 143 | case reflect.Ptr: 144 | tp = tp.Elem() 145 | default: 146 | return false 147 | } 148 | } 149 | } 150 | 151 | func (option *Option) isFunc() bool { 152 | return option.value.Type().Kind() == reflect.Func 153 | } 154 | 155 | func (option *Option) call(value *string) error { 156 | var retval []reflect.Value 157 | 158 | if value == nil { 159 | retval = option.value.Call(nil) 160 | } else { 161 | tp := option.value.Type().In(0) 162 | 163 | val := reflect.New(tp) 164 | val = reflect.Indirect(val) 165 | 166 | if err := convert(*value, val, option.tag); err != nil { 167 | return err 168 | } 169 | 170 | retval = option.value.Call([]reflect.Value{val}) 171 | } 172 | 173 | if len(retval) == 1 && retval[0].Type() == reflect.TypeOf((*error)(nil)).Elem() { 174 | if retval[0].Interface() == nil { 175 | return nil 176 | } 177 | 178 | return retval[0].Interface().(error) 179 | } 180 | 181 | return nil 182 | } 183 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jessevdk/go-flags/options_test.go: -------------------------------------------------------------------------------- 1 | package flags 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestPassDoubleDash(t *testing.T) { 8 | var opts = struct { 9 | Value bool `short:"v"` 10 | }{} 11 | 12 | p := NewParser(&opts, PassDoubleDash) 13 | ret, err := p.ParseArgs([]string{"-v", "--", "-v", "-g"}) 14 | 15 | if err != nil { 16 | t.Fatalf("Unexpected error: %v", err) 17 | return 18 | } 19 | 20 | if !opts.Value { 21 | t.Errorf("Expected Value to be true") 22 | } 23 | 24 | assertStringArray(t, ret, []string{"-v", "-g"}) 25 | } 26 | 27 | func TestPassAfterNonOption(t *testing.T) { 28 | var opts = struct { 29 | Value bool `short:"v"` 30 | }{} 31 | 32 | p := NewParser(&opts, PassAfterNonOption) 33 | ret, err := p.ParseArgs([]string{"-v", "arg", "-v", "-g"}) 34 | 35 | if err != nil { 36 | t.Fatalf("Unexpected error: %v", err) 37 | return 38 | } 39 | 40 | if !opts.Value { 41 | t.Errorf("Expected Value to be true") 42 | } 43 | 44 | assertStringArray(t, ret, []string{"arg", "-v", "-g"}) 45 | } 46 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jessevdk/go-flags/optstyle_other.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | package flags 4 | 5 | import ( 6 | "strings" 7 | ) 8 | 9 | const ( 10 | defaultShortOptDelimiter = '-' 11 | defaultLongOptDelimiter = "--" 12 | defaultNameArgDelimiter = '=' 13 | ) 14 | 15 | func argumentIsOption(arg string) bool { 16 | return len(arg) > 0 && arg[0] == '-' 17 | } 18 | 19 | // stripOptionPrefix returns the option without the prefix and whether or 20 | // not the option is a long option or not. 21 | func stripOptionPrefix(optname string) (prefix string, name string, islong bool) { 22 | if strings.HasPrefix(optname, "--") { 23 | return "--", optname[2:], true 24 | } else if strings.HasPrefix(optname, "-") { 25 | return "-", optname[1:], false 26 | } 27 | 28 | return "", optname, false 29 | } 30 | 31 | // splitOption attempts to split the passed option into a name and an argument. 32 | // When there is no argument specified, nil will be returned for it. 33 | func splitOption(prefix string, option string, islong bool) (string, string, *string) { 34 | pos := strings.Index(option, "=") 35 | 36 | if (islong && pos >= 0) || (!islong && pos == 1) { 37 | rest := option[pos+1:] 38 | return option[:pos], "=", &rest 39 | } 40 | 41 | return option, "", nil 42 | } 43 | 44 | // addHelpGroup adds a new group that contains default help parameters. 45 | func (c *Command) addHelpGroup(showHelp func() error) *Group { 46 | var help struct { 47 | ShowHelp func() error `short:"h" long:"help" description:"Show this help message"` 48 | } 49 | 50 | help.ShowHelp = showHelp 51 | ret, _ := c.AddGroup("Help Options", "", &help) 52 | ret.isBuiltinHelp = true 53 | 54 | return ret 55 | } 56 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jessevdk/go-flags/optstyle_windows.go: -------------------------------------------------------------------------------- 1 | package flags 2 | 3 | import ( 4 | "strings" 5 | ) 6 | 7 | // Windows uses a front slash for both short and long options. Also it uses 8 | // a colon for name/argument delimter. 9 | const ( 10 | defaultShortOptDelimiter = '/' 11 | defaultLongOptDelimiter = "/" 12 | defaultNameArgDelimiter = ':' 13 | ) 14 | 15 | func argumentIsOption(arg string) bool { 16 | // Windows-style options allow front slash for the option 17 | // delimiter. 18 | return len(arg) > 0 && (arg[0] == '-' || arg[0] == '/') 19 | } 20 | 21 | // stripOptionPrefix returns the option without the prefix and whether or 22 | // not the option is a long option or not. 23 | func stripOptionPrefix(optname string) (prefix string, name string, islong bool) { 24 | // Determine if the argument is a long option or not. Windows 25 | // typically supports both long and short options with a single 26 | // front slash as the option delimiter, so handle this situation 27 | // nicely. 28 | possplit := 0 29 | 30 | if strings.HasPrefix(optname, "--") { 31 | possplit = 2 32 | islong = true 33 | } else if strings.HasPrefix(optname, "-") { 34 | possplit = 1 35 | islong = false 36 | } else if strings.HasPrefix(optname, "/") { 37 | possplit = 1 38 | islong = len(optname) > 2 39 | } 40 | 41 | return optname[:possplit], optname[possplit:], islong 42 | } 43 | 44 | // splitOption attempts to split the passed option into a name and an argument. 45 | // When there is no argument specified, nil will be returned for it. 46 | func splitOption(prefix string, option string, islong bool) (string, string, *string) { 47 | if len(option) == 0 { 48 | return option, "", nil 49 | } 50 | 51 | // Windows typically uses a colon for the option name and argument 52 | // delimiter while POSIX typically uses an equals. Support both styles, 53 | // but don't allow the two to be mixed. That is to say /foo:bar and 54 | // --foo=bar are acceptable, but /foo=bar and --foo:bar are not. 55 | var pos int 56 | var sp string 57 | 58 | if prefix == "/" { 59 | sp = ":" 60 | pos = strings.Index(option, sp) 61 | } else if len(prefix) > 0 { 62 | sp = "=" 63 | pos = strings.Index(option, sp) 64 | } 65 | 66 | if (islong && pos >= 0) || (!islong && pos == 1) { 67 | rest := option[pos+1:] 68 | return option[:pos], sp, &rest 69 | } 70 | 71 | return option, "", nil 72 | } 73 | 74 | // addHelpGroup adds a new group that contains default help parameters. 75 | func (c *Command) addHelpGroup(showHelp func() error) *Group { 76 | // Windows CLI applications typically use /? for help, so make both 77 | // that available as well as the POSIX style h and help. 78 | var help struct { 79 | ShowHelpWindows func() error `short:"?" description:"Show this help message"` 80 | ShowHelpPosix func() error `short:"h" long:"help" description:"Show this help message"` 81 | } 82 | 83 | help.ShowHelpWindows = showHelp 84 | help.ShowHelpPosix = showHelp 85 | 86 | ret, _ := c.AddGroup("Help Options", "", &help) 87 | ret.isBuiltinHelp = true 88 | 89 | return ret 90 | } 91 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jessevdk/go-flags/parser.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Jesse van den Kieboom. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package flags 6 | 7 | import ( 8 | "os" 9 | "path" 10 | ) 11 | 12 | // A Parser provides command line option parsing. It can contain several 13 | // option groups each with their own set of options. 14 | type Parser struct { 15 | // Embedded, see Command for more information 16 | *Command 17 | 18 | // A usage string to be displayed in the help message. 19 | Usage string 20 | 21 | // Option flags changing the behavior of the parser. 22 | Options Options 23 | 24 | // NamespaceDelimiter separates group namespaces and option long names 25 | NamespaceDelimiter string 26 | 27 | internalError error 28 | } 29 | 30 | // Options provides parser options that change the behavior of the option 31 | // parser. 32 | type Options uint 33 | 34 | const ( 35 | // None indicates no options. 36 | None Options = 0 37 | 38 | // HelpFlag adds a default Help Options group to the parser containing 39 | // -h and --help options. When either -h or --help is specified on the 40 | // command line, the parser will return the special error of type 41 | // ErrHelp. When PrintErrors is also specified, then the help message 42 | // will also be automatically printed to os.Stderr. 43 | HelpFlag = 1 << iota 44 | 45 | // PassDoubleDash passes all arguments after a double dash, --, as 46 | // remaining command line arguments (i.e. they will not be parsed for 47 | // flags). 48 | PassDoubleDash 49 | 50 | // IgnoreUnknown ignores any unknown options and passes them as 51 | // remaining command line arguments instead of generating an error. 52 | IgnoreUnknown 53 | 54 | // PrintErrors prints any errors which occurred during parsing to 55 | // os.Stderr. 56 | PrintErrors 57 | 58 | // PassAfterNonOption passes all arguments after the first non option 59 | // as remaining command line arguments. This is equivalent to strict 60 | // POSIX processing. 61 | PassAfterNonOption 62 | 63 | // Default is a convenient default set of options which should cover 64 | // most of the uses of the flags package. 65 | Default = HelpFlag | PrintErrors | PassDoubleDash 66 | ) 67 | 68 | // Parse is a convenience function to parse command line options with default 69 | // settings. The provided data is a pointer to a struct representing the 70 | // default option group (named "Application Options"). For more control, use 71 | // flags.NewParser. 72 | func Parse(data interface{}) ([]string, error) { 73 | return NewParser(data, Default).Parse() 74 | } 75 | 76 | // ParseArgs is a convenience function to parse command line options with default 77 | // settings. The provided data is a pointer to a struct representing the 78 | // default option group (named "Application Options"). The args argument is 79 | // the list of command line arguments to parse. If you just want to parse the 80 | // default program command line arguments (i.e. os.Args), then use flags.Parse 81 | // instead. For more control, use flags.NewParser. 82 | func ParseArgs(data interface{}, args []string) ([]string, error) { 83 | return NewParser(data, Default).ParseArgs(args) 84 | } 85 | 86 | // NewParser creates a new parser. It uses os.Args[0] as the application 87 | // name and then calls Parser.NewNamedParser (see Parser.NewNamedParser for 88 | // more details). The provided data is a pointer to a struct representing the 89 | // default option group (named "Application Options"), or nil if the default 90 | // group should not be added. The options parameter specifies a set of options 91 | // for the parser. 92 | func NewParser(data interface{}, options Options) *Parser { 93 | p := NewNamedParser(path.Base(os.Args[0]), options) 94 | 95 | if data != nil { 96 | g, err := p.AddGroup("Application Options", "", data) 97 | 98 | if err == nil { 99 | g.parent = p 100 | } 101 | 102 | p.internalError = err 103 | } 104 | 105 | return p 106 | } 107 | 108 | // NewNamedParser creates a new parser. The appname is used to display the 109 | // executable name in the built-in help message. Option groups and commands can 110 | // be added to this parser by using AddGroup and AddCommand. 111 | func NewNamedParser(appname string, options Options) *Parser { 112 | p := &Parser{ 113 | Command: newCommand(appname, "", "", nil), 114 | Options: options, 115 | NamespaceDelimiter: ".", 116 | } 117 | 118 | p.Command.parent = p 119 | 120 | return p 121 | } 122 | 123 | // Parse parses the command line arguments from os.Args using Parser.ParseArgs. 124 | // For more detailed information see ParseArgs. 125 | func (p *Parser) Parse() ([]string, error) { 126 | return p.ParseArgs(os.Args[1:]) 127 | } 128 | 129 | // ParseArgs parses the command line arguments according to the option groups that 130 | // were added to the parser. On successful parsing of the arguments, the 131 | // remaining, non-option, arguments (if any) are returned. The returned error 132 | // indicates a parsing error and can be used with PrintError to display 133 | // contextual information on where the error occurred exactly. 134 | // 135 | // When the common help group has been added (AddHelp) and either -h or --help 136 | // was specified in the command line arguments, a help message will be 137 | // automatically printed. Furthermore, the special error type ErrHelp is returned. 138 | // It is up to the caller to exit the program if so desired. 139 | func (p *Parser) ParseArgs(args []string) ([]string, error) { 140 | if p.internalError != nil { 141 | return nil, p.internalError 142 | } 143 | 144 | p.clearIsSet() 145 | 146 | // Add built-in help group to all commands if necessary 147 | if (p.Options & HelpFlag) != None { 148 | p.addHelpGroups(p.showBuiltinHelp) 149 | } 150 | 151 | compval := os.Getenv("GO_FLAGS_COMPLETION") 152 | 153 | if len(compval) != 0 { 154 | comp := &completion{parser: p} 155 | 156 | if compval == "verbose" { 157 | comp.ShowDescriptions = true 158 | } 159 | 160 | comp.execute(args) 161 | 162 | return nil, nil 163 | } 164 | 165 | s := &parseState{ 166 | args: args, 167 | retargs: make([]string, 0, len(args)), 168 | } 169 | 170 | p.fillParseState(s) 171 | 172 | for !s.eof() { 173 | arg := s.pop() 174 | 175 | // When PassDoubleDash is set and we encounter a --, then 176 | // simply append all the rest as arguments and break out 177 | if (p.Options&PassDoubleDash) != None && arg == "--" { 178 | s.addArgs(s.args...) 179 | break 180 | } 181 | 182 | if !argumentIsOption(arg) { 183 | // Note: this also sets s.err, so we can just check for 184 | // nil here and use s.err later 185 | if p.parseNonOption(s) != nil { 186 | break 187 | } 188 | 189 | continue 190 | } 191 | 192 | var err error 193 | 194 | prefix, optname, islong := stripOptionPrefix(arg) 195 | optname, _, argument := splitOption(prefix, optname, islong) 196 | 197 | if islong { 198 | err = p.parseLong(s, optname, argument) 199 | } else { 200 | err = p.parseShort(s, optname, argument) 201 | } 202 | 203 | if err != nil { 204 | ignoreUnknown := (p.Options & IgnoreUnknown) != None 205 | parseErr := wrapError(err) 206 | 207 | if !(parseErr.Type == ErrUnknownFlag && ignoreUnknown) { 208 | s.err = parseErr 209 | break 210 | } 211 | 212 | if ignoreUnknown { 213 | s.addArgs(arg) 214 | } 215 | } 216 | } 217 | 218 | if s.err == nil { 219 | p.eachCommand(func(c *Command) { 220 | c.eachGroup(func(g *Group) { 221 | for _, option := range g.options { 222 | if option.isSet { 223 | continue 224 | } 225 | 226 | option.clearDefault() 227 | } 228 | }) 229 | }, true) 230 | 231 | s.checkRequired(p) 232 | } 233 | 234 | var reterr error 235 | 236 | if s.err != nil { 237 | reterr = s.err 238 | } else if len(s.command.commands) != 0 && !s.command.SubcommandsOptional { 239 | reterr = s.estimateCommand() 240 | } else if cmd, ok := s.command.data.(Commander); ok { 241 | reterr = cmd.Execute(s.retargs) 242 | } 243 | 244 | if reterr != nil { 245 | return append([]string{s.arg}, s.args...), p.printError(reterr) 246 | } 247 | 248 | return s.retargs, nil 249 | } 250 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jessevdk/go-flags/parser_private.go: -------------------------------------------------------------------------------- 1 | package flags 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "os" 7 | "sort" 8 | "strings" 9 | "unicode/utf8" 10 | ) 11 | 12 | type parseState struct { 13 | arg string 14 | args []string 15 | retargs []string 16 | positional []*Arg 17 | err error 18 | 19 | command *Command 20 | lookup lookup 21 | } 22 | 23 | func (p *parseState) eof() bool { 24 | return len(p.args) == 0 25 | } 26 | 27 | func (p *parseState) pop() string { 28 | if p.eof() { 29 | return "" 30 | } 31 | 32 | p.arg = p.args[0] 33 | p.args = p.args[1:] 34 | 35 | return p.arg 36 | } 37 | 38 | func (p *parseState) peek() string { 39 | if p.eof() { 40 | return "" 41 | } 42 | 43 | return p.args[0] 44 | } 45 | 46 | func (p *parseState) checkRequired(parser *Parser) error { 47 | c := parser.Command 48 | 49 | var required []*Option 50 | 51 | for c != nil { 52 | c.eachGroup(func(g *Group) { 53 | for _, option := range g.options { 54 | if !option.isSet && option.Required { 55 | required = append(required, option) 56 | } 57 | } 58 | }) 59 | 60 | c = c.Active 61 | } 62 | 63 | if len(required) == 0 { 64 | if len(p.positional) > 0 && p.command.ArgsRequired { 65 | var reqnames []string 66 | 67 | for _, arg := range p.positional { 68 | if arg.isRemaining() { 69 | break 70 | } 71 | 72 | reqnames = append(reqnames, "`"+arg.Name+"`") 73 | } 74 | 75 | if len(reqnames) == 0 { 76 | return nil 77 | } 78 | 79 | var msg string 80 | 81 | if len(reqnames) == 1 { 82 | msg = fmt.Sprintf("the required argument %s was not provided", reqnames[0]) 83 | } else { 84 | msg = fmt.Sprintf("the required arguments %s and %s were not provided", 85 | strings.Join(reqnames[:len(reqnames)-1], ", "), reqnames[len(reqnames)-1]) 86 | } 87 | 88 | p.err = newError(ErrRequired, msg) 89 | return p.err 90 | } 91 | 92 | return nil 93 | } 94 | 95 | names := make([]string, 0, len(required)) 96 | 97 | for _, k := range required { 98 | names = append(names, "`"+k.String()+"'") 99 | } 100 | 101 | sort.Strings(names) 102 | 103 | var msg string 104 | 105 | if len(names) == 1 { 106 | msg = fmt.Sprintf("the required flag %s was not specified", names[0]) 107 | } else { 108 | msg = fmt.Sprintf("the required flags %s and %s were not specified", 109 | strings.Join(names[:len(names)-1], ", "), names[len(names)-1]) 110 | } 111 | 112 | p.err = newError(ErrRequired, msg) 113 | return p.err 114 | } 115 | 116 | func (p *parseState) estimateCommand() error { 117 | commands := p.command.sortedCommands() 118 | cmdnames := make([]string, len(commands)) 119 | 120 | for i, v := range commands { 121 | cmdnames[i] = v.Name 122 | } 123 | 124 | var msg string 125 | var errtype ErrorType 126 | 127 | if len(p.retargs) != 0 { 128 | c, l := closestChoice(p.retargs[0], cmdnames) 129 | msg = fmt.Sprintf("Unknown command `%s'", p.retargs[0]) 130 | errtype = ErrUnknownCommand 131 | 132 | if float32(l)/float32(len(c)) < 0.5 { 133 | msg = fmt.Sprintf("%s, did you mean `%s'?", msg, c) 134 | } else if len(cmdnames) == 1 { 135 | msg = fmt.Sprintf("%s. You should use the %s command", 136 | msg, 137 | cmdnames[0]) 138 | } else { 139 | msg = fmt.Sprintf("%s. Please specify one command of: %s or %s", 140 | msg, 141 | strings.Join(cmdnames[:len(cmdnames)-1], ", "), 142 | cmdnames[len(cmdnames)-1]) 143 | } 144 | } else { 145 | errtype = ErrCommandRequired 146 | 147 | if len(cmdnames) == 1 { 148 | msg = fmt.Sprintf("Please specify the %s command", cmdnames[0]) 149 | } else { 150 | msg = fmt.Sprintf("Please specify one command of: %s or %s", 151 | strings.Join(cmdnames[:len(cmdnames)-1], ", "), 152 | cmdnames[len(cmdnames)-1]) 153 | } 154 | } 155 | 156 | return newError(errtype, msg) 157 | } 158 | 159 | func (p *Parser) parseOption(s *parseState, name string, option *Option, canarg bool, argument *string) (err error) { 160 | if !option.canArgument() { 161 | if argument != nil { 162 | return newErrorf(ErrNoArgumentForBool, "bool flag `%s' cannot have an argument", option) 163 | } 164 | 165 | err = option.set(nil) 166 | } else if argument != nil || (canarg && !s.eof()) { 167 | var arg string 168 | 169 | if argument != nil { 170 | arg = *argument 171 | } else { 172 | arg = s.pop() 173 | if argumentIsOption(arg) { 174 | return newErrorf(ErrExpectedArgument, "expected argument for flag `%s', but got option `%s'", option, arg) 175 | } 176 | } 177 | 178 | if option.tag.Get("unquote") != "false" { 179 | arg, err = unquoteIfPossible(arg) 180 | } 181 | 182 | if err == nil { 183 | err = option.set(&arg) 184 | } 185 | } else if option.OptionalArgument { 186 | option.empty() 187 | 188 | for _, v := range option.OptionalValue { 189 | err = option.set(&v) 190 | 191 | if err != nil { 192 | break 193 | } 194 | } 195 | } else { 196 | err = newErrorf(ErrExpectedArgument, "expected argument for flag `%s'", option) 197 | } 198 | 199 | if err != nil { 200 | if _, ok := err.(*Error); !ok { 201 | err = newErrorf(ErrMarshal, "invalid argument for flag `%s' (expected %s): %s", 202 | option, 203 | option.value.Type(), 204 | err.Error()) 205 | } 206 | } 207 | 208 | return err 209 | } 210 | 211 | func (p *Parser) parseLong(s *parseState, name string, argument *string) error { 212 | if option := s.lookup.longNames[name]; option != nil { 213 | // Only long options that are required can consume an argument 214 | // from the argument list 215 | canarg := !option.OptionalArgument 216 | 217 | return p.parseOption(s, name, option, canarg, argument) 218 | } 219 | 220 | return newErrorf(ErrUnknownFlag, "unknown flag `%s'", name) 221 | } 222 | 223 | func (p *Parser) splitShortConcatArg(s *parseState, optname string) (string, *string) { 224 | c, n := utf8.DecodeRuneInString(optname) 225 | 226 | if n == len(optname) { 227 | return optname, nil 228 | } 229 | 230 | first := string(c) 231 | 232 | if option := s.lookup.shortNames[first]; option != nil && option.canArgument() { 233 | arg := optname[n:] 234 | return first, &arg 235 | } 236 | 237 | return optname, nil 238 | } 239 | 240 | func (p *Parser) parseShort(s *parseState, optname string, argument *string) error { 241 | if argument == nil { 242 | optname, argument = p.splitShortConcatArg(s, optname) 243 | } 244 | 245 | for i, c := range optname { 246 | shortname := string(c) 247 | 248 | if option := s.lookup.shortNames[shortname]; option != nil { 249 | // Only the last short argument can consume an argument from 250 | // the arguments list, and only if it's non optional 251 | canarg := (i+utf8.RuneLen(c) == len(optname)) && !option.OptionalArgument 252 | 253 | if err := p.parseOption(s, shortname, option, canarg, argument); err != nil { 254 | return err 255 | } 256 | } else { 257 | return newErrorf(ErrUnknownFlag, "unknown flag `%s'", shortname) 258 | } 259 | 260 | // Only the first option can have a concatted argument, so just 261 | // clear argument here 262 | argument = nil 263 | } 264 | 265 | return nil 266 | } 267 | 268 | func (p *parseState) addArgs(args ...string) error { 269 | for len(p.positional) > 0 && len(args) > 0 { 270 | arg := p.positional[0] 271 | 272 | if err := convert(args[0], arg.value, arg.tag); err != nil { 273 | return err 274 | } 275 | 276 | if !arg.isRemaining() { 277 | p.positional = p.positional[1:] 278 | } 279 | 280 | args = args[1:] 281 | } 282 | 283 | p.retargs = append(p.retargs, args...) 284 | return nil 285 | } 286 | 287 | func (p *Parser) parseNonOption(s *parseState) error { 288 | if len(s.positional) > 0 { 289 | return s.addArgs(s.arg) 290 | } 291 | 292 | if cmd := s.lookup.commands[s.arg]; cmd != nil { 293 | s.command.Active = cmd 294 | cmd.fillParseState(s) 295 | } else if (p.Options & PassAfterNonOption) != None { 296 | // If PassAfterNonOption is set then all remaining arguments 297 | // are considered positional 298 | if err := s.addArgs(s.arg); err != nil { 299 | return err 300 | } 301 | 302 | if err := s.addArgs(s.args...); err != nil { 303 | return err 304 | } 305 | 306 | s.args = []string{} 307 | } else { 308 | return s.addArgs(s.arg) 309 | } 310 | 311 | return nil 312 | } 313 | 314 | func (p *Parser) showBuiltinHelp() error { 315 | var b bytes.Buffer 316 | 317 | p.WriteHelp(&b) 318 | return newError(ErrHelp, b.String()) 319 | } 320 | 321 | func (p *Parser) printError(err error) error { 322 | if err != nil && (p.Options&PrintErrors) != None { 323 | fmt.Fprintln(os.Stderr, err) 324 | } 325 | 326 | return err 327 | } 328 | 329 | func (p *Parser) clearIsSet() { 330 | p.eachCommand(func(c *Command) { 331 | c.eachGroup(func(g *Group) { 332 | for _, option := range g.options { 333 | option.isSet = false 334 | } 335 | }) 336 | }, true) 337 | } 338 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jessevdk/go-flags/parser_test.go: -------------------------------------------------------------------------------- 1 | package flags 2 | 3 | import ( 4 | "os" 5 | "reflect" 6 | "strconv" 7 | "strings" 8 | "testing" 9 | "time" 10 | ) 11 | 12 | type defaultOptions struct { 13 | Int int `long:"i"` 14 | IntDefault int `long:"id" default:"1"` 15 | 16 | String string `long:"str"` 17 | StringDefault string `long:"strd" default:"abc"` 18 | StringNotUnquoted string `long:"strnot" unquote:"false"` 19 | 20 | Time time.Duration `long:"t"` 21 | TimeDefault time.Duration `long:"td" default:"1m"` 22 | 23 | Map map[string]int `long:"m"` 24 | MapDefault map[string]int `long:"md" default:"a:1"` 25 | 26 | Slice []int `long:"s"` 27 | SliceDefault []int `long:"sd" default:"1" default:"2"` 28 | } 29 | 30 | func TestDefaults(t *testing.T) { 31 | var tests = []struct { 32 | msg string 33 | args []string 34 | expected defaultOptions 35 | }{ 36 | { 37 | msg: "no arguments, expecting default values", 38 | args: []string{}, 39 | expected: defaultOptions{ 40 | Int: 0, 41 | IntDefault: 1, 42 | 43 | String: "", 44 | StringDefault: "abc", 45 | 46 | Time: 0, 47 | TimeDefault: time.Minute, 48 | 49 | Map: map[string]int{}, 50 | MapDefault: map[string]int{"a": 1}, 51 | 52 | Slice: []int{}, 53 | SliceDefault: []int{1, 2}, 54 | }, 55 | }, 56 | { 57 | msg: "non-zero value arguments, expecting overwritten arguments", 58 | args: []string{"--i=3", "--id=3", "--str=def", "--strd=def", "--t=3ms", "--td=3ms", "--m=c:3", "--md=c:3", "--s=3", "--sd=3"}, 59 | expected: defaultOptions{ 60 | Int: 3, 61 | IntDefault: 3, 62 | 63 | String: "def", 64 | StringDefault: "def", 65 | 66 | Time: 3 * time.Millisecond, 67 | TimeDefault: 3 * time.Millisecond, 68 | 69 | Map: map[string]int{"c": 3}, 70 | MapDefault: map[string]int{"c": 3}, 71 | 72 | Slice: []int{3}, 73 | SliceDefault: []int{3}, 74 | }, 75 | }, 76 | { 77 | msg: "zero value arguments, expecting overwritten arguments", 78 | args: []string{"--i=0", "--id=0", "--str", "", "--strd=\"\"", "--t=0ms", "--td=0s", "--m=:0", "--md=:0", "--s=0", "--sd=0"}, 79 | expected: defaultOptions{ 80 | Int: 0, 81 | IntDefault: 0, 82 | 83 | String: "", 84 | StringDefault: "", 85 | 86 | Time: 0, 87 | TimeDefault: 0, 88 | 89 | Map: map[string]int{"": 0}, 90 | MapDefault: map[string]int{"": 0}, 91 | 92 | Slice: []int{0}, 93 | SliceDefault: []int{0}, 94 | }, 95 | }, 96 | } 97 | 98 | for _, test := range tests { 99 | var opts defaultOptions 100 | 101 | _, err := ParseArgs(&opts, test.args) 102 | if err != nil { 103 | t.Fatalf("%s:\nUnexpected error: %v", test.msg, err) 104 | } 105 | 106 | if opts.Slice == nil { 107 | opts.Slice = []int{} 108 | } 109 | 110 | if !reflect.DeepEqual(opts, test.expected) { 111 | t.Errorf("%s:\nUnexpected options with arguments %+v\nexpected\n%+v\nbut got\n%+v\n", test.msg, test.args, test.expected, opts) 112 | } 113 | } 114 | } 115 | 116 | func TestUnquoting(t *testing.T) { 117 | var tests = []struct { 118 | arg string 119 | err error 120 | value string 121 | }{ 122 | { 123 | arg: "\"abc", 124 | err: strconv.ErrSyntax, 125 | value: "", 126 | }, 127 | { 128 | arg: "\"\"abc\"", 129 | err: strconv.ErrSyntax, 130 | value: "", 131 | }, 132 | { 133 | arg: "\"abc\"", 134 | err: nil, 135 | value: "abc", 136 | }, 137 | { 138 | arg: "\"\\\"abc\\\"\"", 139 | err: nil, 140 | value: "\"abc\"", 141 | }, 142 | { 143 | arg: "\"\\\"abc\"", 144 | err: nil, 145 | value: "\"abc", 146 | }, 147 | } 148 | 149 | for _, test := range tests { 150 | var opts defaultOptions 151 | 152 | for _, delimiter := range []bool{false, true} { 153 | p := NewParser(&opts, None) 154 | 155 | var err error 156 | if delimiter { 157 | _, err = p.ParseArgs([]string{"--str=" + test.arg, "--strnot=" + test.arg}) 158 | } else { 159 | _, err = p.ParseArgs([]string{"--str", test.arg, "--strnot", test.arg}) 160 | } 161 | 162 | if test.err == nil { 163 | if err != nil { 164 | t.Fatalf("Expected no error but got: %v", err) 165 | } 166 | 167 | if test.value != opts.String { 168 | t.Fatalf("Expected String to be %q but got %q", test.value, opts.String) 169 | } 170 | if q := strconv.Quote(test.value); q != opts.StringNotUnquoted { 171 | t.Fatalf("Expected StringDefault to be %q but got %q", q, opts.StringNotUnquoted) 172 | } 173 | } else { 174 | if err == nil { 175 | t.Fatalf("Expected error") 176 | } else if e, ok := err.(*Error); ok { 177 | if strings.HasPrefix(e.Message, test.err.Error()) { 178 | t.Fatalf("Expected error message to end with %q but got %v", test.err.Error(), e.Message) 179 | } 180 | } 181 | } 182 | } 183 | } 184 | } 185 | 186 | // envRestorer keeps a copy of a set of env variables and can restore the env from them 187 | type envRestorer struct { 188 | env map[string]string 189 | } 190 | 191 | func (r *envRestorer) Restore() { 192 | os.Clearenv() 193 | for k, v := range r.env { 194 | os.Setenv(k, v) 195 | } 196 | } 197 | 198 | // EnvSnapshot returns a snapshot of the currently set env variables 199 | func EnvSnapshot() *envRestorer { 200 | r := envRestorer{make(map[string]string)} 201 | for _, kv := range os.Environ() { 202 | parts := strings.SplitN(kv, "=", 2) 203 | if len(parts) != 2 { 204 | panic("got a weird env variable: " + kv) 205 | } 206 | r.env[parts[0]] = parts[1] 207 | } 208 | return &r 209 | } 210 | 211 | type envDefaultOptions struct { 212 | Int int `long:"i" default:"1" env:"TEST_I"` 213 | Time time.Duration `long:"t" default:"1m" env:"TEST_T"` 214 | Map map[string]int `long:"m" default:"a:1" env:"TEST_M" env-delim:";"` 215 | Slice []int `long:"s" default:"1" default:"2" env:"TEST_S" env-delim:","` 216 | } 217 | 218 | func TestEnvDefaults(t *testing.T) { 219 | var tests = []struct { 220 | msg string 221 | args []string 222 | expected envDefaultOptions 223 | env map[string]string 224 | }{ 225 | { 226 | msg: "no arguments, no env, expecting default values", 227 | args: []string{}, 228 | expected: envDefaultOptions{ 229 | Int: 1, 230 | Time: time.Minute, 231 | Map: map[string]int{"a": 1}, 232 | Slice: []int{1, 2}, 233 | }, 234 | }, 235 | { 236 | msg: "no arguments, env defaults, expecting env default values", 237 | args: []string{}, 238 | expected: envDefaultOptions{ 239 | Int: 2, 240 | Time: 2 * time.Minute, 241 | Map: map[string]int{"a": 2, "b": 3}, 242 | Slice: []int{4, 5, 6}, 243 | }, 244 | env: map[string]string{ 245 | "TEST_I": "2", 246 | "TEST_T": "2m", 247 | "TEST_M": "a:2;b:3", 248 | "TEST_S": "4,5,6", 249 | }, 250 | }, 251 | { 252 | msg: "non-zero value arguments, expecting overwritten arguments", 253 | args: []string{"--i=3", "--t=3ms", "--m=c:3", "--s=3"}, 254 | expected: envDefaultOptions{ 255 | Int: 3, 256 | Time: 3 * time.Millisecond, 257 | Map: map[string]int{"c": 3}, 258 | Slice: []int{3}, 259 | }, 260 | env: map[string]string{ 261 | "TEST_I": "2", 262 | "TEST_T": "2m", 263 | "TEST_M": "a:2;b:3", 264 | "TEST_S": "4,5,6", 265 | }, 266 | }, 267 | { 268 | msg: "zero value arguments, expecting overwritten arguments", 269 | args: []string{"--i=0", "--t=0ms", "--m=:0", "--s=0"}, 270 | expected: envDefaultOptions{ 271 | Int: 0, 272 | Time: 0, 273 | Map: map[string]int{"": 0}, 274 | Slice: []int{0}, 275 | }, 276 | env: map[string]string{ 277 | "TEST_I": "2", 278 | "TEST_T": "2m", 279 | "TEST_M": "a:2;b:3", 280 | "TEST_S": "4,5,6", 281 | }, 282 | }, 283 | } 284 | 285 | oldEnv := EnvSnapshot() 286 | defer oldEnv.Restore() 287 | 288 | for _, test := range tests { 289 | var opts envDefaultOptions 290 | oldEnv.Restore() 291 | for envKey, envValue := range test.env { 292 | os.Setenv(envKey, envValue) 293 | } 294 | _, err := ParseArgs(&opts, test.args) 295 | if err != nil { 296 | t.Fatalf("%s:\nUnexpected error: %v", test.msg, err) 297 | } 298 | 299 | if opts.Slice == nil { 300 | opts.Slice = []int{} 301 | } 302 | 303 | if !reflect.DeepEqual(opts, test.expected) { 304 | t.Errorf("%s:\nUnexpected options with arguments %+v\nexpected\n%+v\nbut got\n%+v\n", test.msg, test.args, test.expected, opts) 305 | } 306 | } 307 | } 308 | 309 | func TestOptionAsArgument(t *testing.T) { 310 | var tests = []struct { 311 | args []string 312 | expectError bool 313 | errType ErrorType 314 | errMsg string 315 | }{ 316 | { 317 | // short option must not be accepted as argument 318 | args: []string{"--string-slice", "foobar", "--string-slice", "-o"}, 319 | expectError: true, 320 | errType: ErrExpectedArgument, 321 | errMsg: "expected argument for flag `--string-slice', but got option `-o'", 322 | }, 323 | { 324 | // long option must not be accepted as argument 325 | args: []string{"--string-slice", "foobar", "--string-slice", "--other-option"}, 326 | expectError: true, 327 | errType: ErrExpectedArgument, 328 | errMsg: "expected argument for flag `--string-slice', but got option `--other-option'", 329 | }, 330 | { 331 | // quoted and appended option should be accepted as argument (even if it looks like an option) 332 | args: []string{"--string-slice", "foobar", "--string-slice=\"--other-option\""}, 333 | }, 334 | } 335 | var opts struct { 336 | StringSlice []string `long:"string-slice"` 337 | OtherOption bool `long:"other-option" short:"o"` 338 | } 339 | 340 | for _, test := range tests { 341 | if test.expectError { 342 | assertParseFail(t, test.errType, test.errMsg, &opts, test.args...) 343 | } else { 344 | assertParseSuccess(t, &opts, test.args...) 345 | } 346 | } 347 | } 348 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jessevdk/go-flags/pointer_test.go: -------------------------------------------------------------------------------- 1 | package flags 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestPointerBool(t *testing.T) { 8 | var opts = struct { 9 | Value *bool `short:"v"` 10 | }{} 11 | 12 | ret := assertParseSuccess(t, &opts, "-v") 13 | 14 | assertStringArray(t, ret, []string{}) 15 | 16 | if !*opts.Value { 17 | t.Errorf("Expected Value to be true") 18 | } 19 | } 20 | 21 | func TestPointerString(t *testing.T) { 22 | var opts = struct { 23 | Value *string `short:"v"` 24 | }{} 25 | 26 | ret := assertParseSuccess(t, &opts, "-v", "value") 27 | 28 | assertStringArray(t, ret, []string{}) 29 | assertString(t, *opts.Value, "value") 30 | } 31 | 32 | func TestPointerSlice(t *testing.T) { 33 | var opts = struct { 34 | Value *[]string `short:"v"` 35 | }{} 36 | 37 | ret := assertParseSuccess(t, &opts, "-v", "value1", "-v", "value2") 38 | 39 | assertStringArray(t, ret, []string{}) 40 | assertStringArray(t, *opts.Value, []string{"value1", "value2"}) 41 | } 42 | 43 | func TestPointerMap(t *testing.T) { 44 | var opts = struct { 45 | Value *map[string]int `short:"v"` 46 | }{} 47 | 48 | ret := assertParseSuccess(t, &opts, "-v", "k1:2", "-v", "k2:-5") 49 | 50 | assertStringArray(t, ret, []string{}) 51 | 52 | if v, ok := (*opts.Value)["k1"]; !ok { 53 | t.Errorf("Expected key \"k1\" to exist") 54 | } else if v != 2 { 55 | t.Errorf("Expected \"k1\" to be 2, but got %#v", v) 56 | } 57 | 58 | if v, ok := (*opts.Value)["k2"]; !ok { 59 | t.Errorf("Expected key \"k2\" to exist") 60 | } else if v != -5 { 61 | t.Errorf("Expected \"k2\" to be -5, but got %#v", v) 62 | } 63 | } 64 | 65 | type PointerGroup struct { 66 | Value bool `short:"v"` 67 | } 68 | 69 | func TestPointerGroup(t *testing.T) { 70 | var opts = struct { 71 | Group *PointerGroup `group:"Group Options"` 72 | }{} 73 | 74 | ret := assertParseSuccess(t, &opts, "-v") 75 | 76 | assertStringArray(t, ret, []string{}) 77 | 78 | if !opts.Group.Value { 79 | t.Errorf("Expected Group.Value to be true") 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jessevdk/go-flags/short_test.go: -------------------------------------------------------------------------------- 1 | package flags 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestShort(t *testing.T) { 9 | var opts = struct { 10 | Value bool `short:"v"` 11 | }{} 12 | 13 | ret := assertParseSuccess(t, &opts, "-v") 14 | 15 | assertStringArray(t, ret, []string{}) 16 | 17 | if !opts.Value { 18 | t.Errorf("Expected Value to be true") 19 | } 20 | } 21 | 22 | func TestShortTooLong(t *testing.T) { 23 | var opts = struct { 24 | Value bool `short:"vv"` 25 | }{} 26 | 27 | assertParseFail(t, ErrShortNameTooLong, "short names can only be 1 character long, not `vv'", &opts) 28 | } 29 | 30 | func TestShortRequired(t *testing.T) { 31 | var opts = struct { 32 | Value bool `short:"v" required:"true"` 33 | }{} 34 | 35 | assertParseFail(t, ErrRequired, fmt.Sprintf("the required flag `%cv' was not specified", defaultShortOptDelimiter), &opts) 36 | } 37 | 38 | func TestShortMultiConcat(t *testing.T) { 39 | var opts = struct { 40 | V bool `short:"v"` 41 | O bool `short:"o"` 42 | F bool `short:"f"` 43 | }{} 44 | 45 | ret := assertParseSuccess(t, &opts, "-vo", "-f") 46 | 47 | assertStringArray(t, ret, []string{}) 48 | 49 | if !opts.V { 50 | t.Errorf("Expected V to be true") 51 | } 52 | 53 | if !opts.O { 54 | t.Errorf("Expected O to be true") 55 | } 56 | 57 | if !opts.F { 58 | t.Errorf("Expected F to be true") 59 | } 60 | } 61 | 62 | func TestShortMultiRequiredConcat(t *testing.T) { 63 | var opts = struct { 64 | V bool `short:"v" required:"true"` 65 | O bool `short:"o" required:"true"` 66 | F bool `short:"f" required:"true"` 67 | }{} 68 | 69 | ret := assertParseSuccess(t, &opts, "-vo", "-f") 70 | 71 | assertStringArray(t, ret, []string{}) 72 | 73 | if !opts.V { 74 | t.Errorf("Expected V to be true") 75 | } 76 | 77 | if !opts.O { 78 | t.Errorf("Expected O to be true") 79 | } 80 | 81 | if !opts.F { 82 | t.Errorf("Expected F to be true") 83 | } 84 | } 85 | 86 | func TestShortMultiSlice(t *testing.T) { 87 | var opts = struct { 88 | Values []bool `short:"v"` 89 | }{} 90 | 91 | ret := assertParseSuccess(t, &opts, "-v", "-v") 92 | 93 | assertStringArray(t, ret, []string{}) 94 | assertBoolArray(t, opts.Values, []bool{true, true}) 95 | } 96 | 97 | func TestShortMultiSliceConcat(t *testing.T) { 98 | var opts = struct { 99 | Values []bool `short:"v"` 100 | }{} 101 | 102 | ret := assertParseSuccess(t, &opts, "-vvv") 103 | 104 | assertStringArray(t, ret, []string{}) 105 | assertBoolArray(t, opts.Values, []bool{true, true, true}) 106 | } 107 | 108 | func TestShortWithEqualArg(t *testing.T) { 109 | var opts = struct { 110 | Value string `short:"v"` 111 | }{} 112 | 113 | ret := assertParseSuccess(t, &opts, "-v=value") 114 | 115 | assertStringArray(t, ret, []string{}) 116 | assertString(t, opts.Value, "value") 117 | } 118 | 119 | func TestShortWithArg(t *testing.T) { 120 | var opts = struct { 121 | Value string `short:"v"` 122 | }{} 123 | 124 | ret := assertParseSuccess(t, &opts, "-vvalue") 125 | 126 | assertStringArray(t, ret, []string{}) 127 | assertString(t, opts.Value, "value") 128 | } 129 | 130 | func TestShortArg(t *testing.T) { 131 | var opts = struct { 132 | Value string `short:"v"` 133 | }{} 134 | 135 | ret := assertParseSuccess(t, &opts, "-v", "value") 136 | 137 | assertStringArray(t, ret, []string{}) 138 | assertString(t, opts.Value, "value") 139 | } 140 | 141 | func TestShortMultiWithEqualArg(t *testing.T) { 142 | var opts = struct { 143 | F []bool `short:"f"` 144 | Value string `short:"v"` 145 | }{} 146 | 147 | assertParseFail(t, ErrExpectedArgument, fmt.Sprintf("expected argument for flag `%cv'", defaultShortOptDelimiter), &opts, "-ffv=value") 148 | } 149 | 150 | func TestShortMultiArg(t *testing.T) { 151 | var opts = struct { 152 | F []bool `short:"f"` 153 | Value string `short:"v"` 154 | }{} 155 | 156 | ret := assertParseSuccess(t, &opts, "-ffv", "value") 157 | 158 | assertStringArray(t, ret, []string{}) 159 | assertBoolArray(t, opts.F, []bool{true, true}) 160 | assertString(t, opts.Value, "value") 161 | } 162 | 163 | func TestShortMultiArgConcatFail(t *testing.T) { 164 | var opts = struct { 165 | F []bool `short:"f"` 166 | Value string `short:"v"` 167 | }{} 168 | 169 | assertParseFail(t, ErrExpectedArgument, fmt.Sprintf("expected argument for flag `%cv'", defaultShortOptDelimiter), &opts, "-ffvvalue") 170 | } 171 | 172 | func TestShortMultiArgConcat(t *testing.T) { 173 | var opts = struct { 174 | F []bool `short:"f"` 175 | Value string `short:"v"` 176 | }{} 177 | 178 | ret := assertParseSuccess(t, &opts, "-vff") 179 | 180 | assertStringArray(t, ret, []string{}) 181 | assertString(t, opts.Value, "ff") 182 | } 183 | 184 | func TestShortOptional(t *testing.T) { 185 | var opts = struct { 186 | F []bool `short:"f"` 187 | Value string `short:"v" optional:"yes" optional-value:"value"` 188 | }{} 189 | 190 | ret := assertParseSuccess(t, &opts, "-fv", "f") 191 | 192 | assertStringArray(t, ret, []string{"f"}) 193 | assertString(t, opts.Value, "value") 194 | } 195 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jessevdk/go-flags/tag_test.go: -------------------------------------------------------------------------------- 1 | package flags 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestTagMissingColon(t *testing.T) { 8 | var opts = struct { 9 | Value bool `short` 10 | }{} 11 | 12 | assertParseFail(t, ErrTag, "expected `:' after key name, but got end of tag (in `short`)", &opts, "") 13 | } 14 | 15 | func TestTagMissingValue(t *testing.T) { 16 | var opts = struct { 17 | Value bool `short:` 18 | }{} 19 | 20 | assertParseFail(t, ErrTag, "expected `\"' to start tag value at end of tag (in `short:`)", &opts, "") 21 | } 22 | 23 | func TestTagMissingQuote(t *testing.T) { 24 | var opts = struct { 25 | Value bool `short:"v` 26 | }{} 27 | 28 | assertParseFail(t, ErrTag, "expected end of tag value `\"' at end of tag (in `short:\"v`)", &opts, "") 29 | } 30 | 31 | func TestTagNewline(t *testing.T) { 32 | var opts = struct { 33 | Value bool `long:"verbose" description:"verbose 34 | something"` 35 | }{} 36 | 37 | assertParseFail(t, ErrTag, "unexpected newline in tag value `description' (in `long:\"verbose\" description:\"verbose\nsomething\"`)", &opts, "") 38 | } 39 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jessevdk/go-flags/termsize.go: -------------------------------------------------------------------------------- 1 | // +build !windows,!plan9,!solaris 2 | 3 | package flags 4 | 5 | import ( 6 | "syscall" 7 | "unsafe" 8 | ) 9 | 10 | type winsize struct { 11 | row, col uint16 12 | xpixel, ypixel uint16 13 | } 14 | 15 | func getTerminalColumns() int { 16 | ws := winsize{} 17 | 18 | if tIOCGWINSZ != 0 { 19 | syscall.Syscall(syscall.SYS_IOCTL, 20 | uintptr(0), 21 | uintptr(tIOCGWINSZ), 22 | uintptr(unsafe.Pointer(&ws))) 23 | 24 | return int(ws.col) 25 | } 26 | 27 | return 80 28 | } 29 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jessevdk/go-flags/termsize_linux.go: -------------------------------------------------------------------------------- 1 | // +build linux 2 | 3 | package flags 4 | 5 | const ( 6 | tIOCGWINSZ = 0x5413 7 | ) 8 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jessevdk/go-flags/termsize_nosysioctl.go: -------------------------------------------------------------------------------- 1 | // +build windows plan9 solaris 2 | 3 | package flags 4 | 5 | func getTerminalColumns() int { 6 | return 80 7 | } 8 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jessevdk/go-flags/termsize_other.go: -------------------------------------------------------------------------------- 1 | // +build !darwin,!freebsd,!netbsd,!openbsd,!linux 2 | 3 | package flags 4 | 5 | const ( 6 | tIOCGWINSZ = 0 7 | ) 8 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jessevdk/go-flags/termsize_unix.go: -------------------------------------------------------------------------------- 1 | // +build darwin freebsd netbsd openbsd 2 | 3 | package flags 4 | 5 | const ( 6 | tIOCGWINSZ = 0x40087468 7 | ) 8 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jessevdk/go-flags/unknown_test.go: -------------------------------------------------------------------------------- 1 | package flags 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestUnknownFlags(t *testing.T) { 8 | var opts = struct { 9 | Verbose []bool `short:"v" long:"verbose" description:"Verbose output"` 10 | }{} 11 | 12 | args := []string{ 13 | "-f", 14 | } 15 | 16 | p := NewParser(&opts, 0) 17 | args, err := p.ParseArgs(args) 18 | 19 | if err == nil { 20 | t.Fatal("Expected error for unknown argument") 21 | } 22 | } 23 | 24 | func TestIgnoreUnknownFlags(t *testing.T) { 25 | var opts = struct { 26 | Verbose []bool `short:"v" long:"verbose" description:"Verbose output"` 27 | }{} 28 | 29 | args := []string{ 30 | "hello", 31 | "world", 32 | "-v", 33 | "--foo=bar", 34 | "--verbose", 35 | "-f", 36 | } 37 | 38 | p := NewParser(&opts, IgnoreUnknown) 39 | args, err := p.ParseArgs(args) 40 | 41 | if err != nil { 42 | t.Fatal(err) 43 | } 44 | 45 | exargs := []string{ 46 | "hello", 47 | "world", 48 | "--foo=bar", 49 | "-f", 50 | } 51 | 52 | issame := (len(args) == len(exargs)) 53 | 54 | if issame { 55 | for i := 0; i < len(args); i++ { 56 | if args[i] != exargs[i] { 57 | issame = false 58 | break 59 | } 60 | } 61 | } 62 | 63 | if !issame { 64 | t.Fatalf("Expected %v but got %v", exargs, args) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2014 Thomas Bishop 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: clean test build 2 | 3 | binaries: clean fmt test 4 | @script/build_binaries.sh 5 | 6 | build: 7 | @echo "==> Compiling source code." 8 | @godep go build -v -o ./bin/github-commit-status ./github-commit-status 9 | 10 | clean: 11 | @echo "==> Cleaning up previous builds." 12 | @rm -rf bin/github-commit-status 13 | 14 | deps: 15 | @echo "==> Downloading dependencies." 16 | @godep save -r ./github-commit-status/... 17 | 18 | fmt: 19 | @echo "==> Formatting source code." 20 | @goimports -w ./github-commit-status 21 | 22 | release: 23 | @echo "==> Releasing" 24 | @script/release 25 | 26 | test: fmt vet 27 | @echo "==> Running tests." 28 | @godep go test -cover ./github-commit-status/... 29 | 30 | vet: 31 | @godep go vet ./github-commit-status/... 32 | 33 | help: 34 | @echo "binaries\tcreate binaries" 35 | @echo "build\t\tbuild the code" 36 | @echo "clean\t\tremove previous builds" 37 | @echo "deps\t\tdownload dependencies" 38 | @echo "fmt\t\tformat the code" 39 | @echo "release\t\tcreate a release" 40 | @echo "test\t\ttest the code" 41 | @echo "vet\t\tvet the code" 42 | @echo "" 43 | @echo "default will test, format, and build the code" 44 | 45 | .PNONY: all clean deps fmt help test 46 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## github-commit-status 2 | 3 | [![Build Status](https://travis-ci.org/thbishop/github-commit-status.svg?branch=master)](https://travis-ci.org/thbishop/github-commit-status) 4 | 5 | This is a simple utility to update the status of a commit on github. The 6 | primary use case is to update the status of a commit in a build environment. 7 | 8 | ## Install 9 | 10 | Download the latest binary or 11 | `brew tap thbishop/github-commit-status && brew install github-commit-status` 12 | if you're on OSX. 13 | 14 | ## Usage 15 | 16 | Create a [github token](https://help.github.com/articles/creating-an-access-token-for-command-line-use/) 17 | with `repo:status` scope and export it as an env var: 18 | ```sh 19 | export GITHUB_TOKEN=1234 20 | ``` 21 | 22 | Update the status with: 23 | ```sh 24 | github-commit-status --user foo --repo bar --commit $SHA --state success 25 | ``` 26 | 27 | You can also optionally include a target url, description, or context to be 28 | included in the status update: 29 | ```sh 30 | github-commit-status --user foo \ 31 | --repo bar \ 32 | --commit $SHA \ 33 | --state success --target-url https://ci.example.com/build/1 \ 34 | --description "It failed because it is busted" \ 35 | --context ci 36 | ``` 37 | 38 | If you're using github enterprise, you can set the API endpoint with an env 39 | var like so: 40 | ```sh 41 | export GITHUB_API=https://github.example.com/api/v3 42 | ``` 43 | 44 | If needed, a proxy can be configured using environment variables: 45 | * `http_proxy` 46 | * `HTTP_PROXY` 47 | 48 | ## Contribute 49 | * Fork the project 50 | * Make your feature addition or bug fix (with tests and docs) in a topic branch 51 | * Make sure tests pass 52 | * Send a pull request and I'll get it integrated 53 | 54 | ## LICENSE 55 | See [LICENSE](LICENSE) 56 | -------------------------------------------------------------------------------- /github-commit-status/cli.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | flags "github.com/thbishop/github-commit-status/Godeps/_workspace/src/github.com/jessevdk/go-flags" 8 | ) 9 | 10 | type options struct { 11 | Commit string `short:"c" long:"commit" description:"Commit (SHA) to update status on" required:"true"` 12 | Context string `long:"context" description:"Context to include in status update"` 13 | Description string `short:"d" long:"description" description:"Text to include with the status update"` 14 | Repo string `short:"r" long:"repo" description:"Github repo name where the commit exists" required:"true"` 15 | State string `short:"s" long:"state" description:"State. Must be one of 'pending', 'success', 'error', or 'failure'" required:"true"` 16 | TargetUrl string `short:"t" long:"target-url" description:"URL to include in status update"` 17 | User string `short:"u" long:"user" description:"Github user that owns the repo" required:"true"` 18 | Version func() `short:"v" long:"version" description:"Display the version github-commit-status"` 19 | } 20 | 21 | func parseCliArgs() *options { 22 | opts := &options{} 23 | 24 | opts.Version = func() { 25 | fmt.Println(version) 26 | os.Exit(0) 27 | } 28 | 29 | parser := flags.NewParser(opts, flags.Default) 30 | 31 | args, err := parser.Parse() 32 | if err != nil { 33 | helpDisplayed := false 34 | 35 | for _, i := range args { 36 | if i == "-h" || i == "--help" { 37 | helpDisplayed = true 38 | break 39 | } 40 | } 41 | 42 | if !helpDisplayed { 43 | parser.WriteHelp(os.Stderr) 44 | } 45 | os.Exit(1) 46 | } 47 | 48 | if !validState(opts.State) { 49 | os.Stderr.Write([]byte(fmt.Sprintf("'%s' is an invalid state\n", opts.State))) 50 | parser.WriteHelp(os.Stderr) 51 | os.Exit(1) 52 | } 53 | 54 | return opts 55 | } 56 | 57 | func states() []string { 58 | return []string{"error", "failure", "pending", "success"} 59 | } 60 | 61 | func validState(state string) bool { 62 | valid := false 63 | for _, s := range states() { 64 | if state == s { 65 | return true 66 | } 67 | } 68 | return valid 69 | } 70 | -------------------------------------------------------------------------------- /github-commit-status/cli_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | var validStateTCs = []struct { 8 | state string 9 | expected bool 10 | }{ 11 | {"error", true}, 12 | {"failure", true}, 13 | {"pending", true}, 14 | {"success", true}, 15 | {"foobar", false}, 16 | } 17 | 18 | func TestValidState(t *testing.T) { 19 | for _, tc := range validStateTCs { 20 | result := validState(tc.state) 21 | if result != tc.expected { 22 | t.Errorf("Error: expected '%t' to be\n'%t'", tc.expected, result) 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /github-commit-status/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "errors" 7 | "fmt" 8 | "io/ioutil" 9 | "net/http" 10 | "os" 11 | "runtime" 12 | "strings" 13 | ) 14 | 15 | func addHeaders(c *config, req *http.Request) { 16 | req.Header.Add("Authorization", "token "+c.apiToken) 17 | req.Header.Add("Content-Type", "application/json") 18 | agent := "github-commit-status/" + version + " (" + runtime.GOOS + ")" 19 | req.Header.Add("User-Agent", agent) 20 | } 21 | 22 | type config struct { 23 | apiUrl string 24 | apiToken string 25 | } 26 | 27 | func newConfig() *config { 28 | c := &config{ 29 | apiToken: os.Getenv("GITHUB_TOKEN"), 30 | apiUrl: getApiUrl(), 31 | } 32 | 33 | if c.apiUrl == "" { 34 | fmt.Printf("Error: Invalid API URL specified '%s'", c.apiUrl) 35 | os.Exit(1) 36 | } 37 | 38 | if c.apiUrl == "" || c.apiToken == "" { 39 | fmt.Printf("Error: GITHUB_TOKEN environment variable not specified") 40 | os.Exit(1) 41 | } 42 | 43 | return c 44 | } 45 | 46 | func getApiUrl() string { 47 | if apiUrl := os.Getenv("GITHUB_API"); apiUrl != "" { 48 | return strings.TrimSuffix(apiUrl, "/") 49 | } 50 | return "https://api.github.com" 51 | } 52 | 53 | type statusBody struct { 54 | Context string `json:"context,omitempty"` 55 | Description string `json:"description,omitempty"` 56 | State string `json:"state"` 57 | TargetUrl string `json:"target_url,omitempty"` 58 | } 59 | 60 | func statusRequestBody(o *options) ([]byte, error) { 61 | body := &statusBody{ 62 | Context: o.Context, 63 | Description: o.Description, 64 | State: o.State, 65 | TargetUrl: o.TargetUrl, 66 | } 67 | 68 | b, err := json.Marshal(body) 69 | if err != nil { 70 | return []byte{}, err 71 | } 72 | 73 | return b, nil 74 | } 75 | 76 | func statusUrl(o *options, c *config) string { 77 | return fmt.Sprintf("%s/repos/%s/%s/statuses/%s", c.apiUrl, o.User, o.Repo, o.Commit) 78 | } 79 | 80 | func updateStatus(opts *options, conf *config) error { 81 | url := statusUrl(opts, conf) 82 | 83 | body, err := statusRequestBody(opts) 84 | if err != nil { 85 | msg := fmt.Sprintf("Error creating API request body: %s", err.Error()) 86 | return errors.New(msg) 87 | } 88 | 89 | client := &http.Client{} 90 | req, err := http.NewRequest("POST", url, bytes.NewBuffer(body)) 91 | addHeaders(conf, req) 92 | 93 | resp, err := client.Do(req) 94 | if err != nil { 95 | msg := fmt.Sprintf("Error making API call: %s", err.Error()) 96 | return errors.New(msg) 97 | } 98 | 99 | return verifyResponse(resp) 100 | } 101 | 102 | func verifyResponse(resp *http.Response) error { 103 | defer resp.Body.Close() 104 | 105 | if resp.StatusCode < 200 || resp.StatusCode >= 300 { 106 | msg := fmt.Sprintf("Did not receive a successful API response (received '%s')\n", resp.Status) 107 | 108 | contents, err := ioutil.ReadAll(resp.Body) 109 | if err != nil { 110 | msg = msg + fmt.Sprintf("Error reading response body: %s", err) 111 | return errors.New(msg) 112 | } 113 | msg = msg + fmt.Sprintf("Response Body:\n%s", contents) 114 | return errors.New(msg) 115 | } 116 | 117 | return nil 118 | } 119 | 120 | func main() { 121 | options := parseCliArgs() 122 | config := newConfig() 123 | 124 | fmt.Printf("Updating status...\n") 125 | 126 | if err := updateStatus(options, config); err != nil { 127 | os.Stderr.Write([]byte(err.Error())) 128 | os.Exit(1) 129 | } 130 | fmt.Printf("Status update complete") 131 | } 132 | -------------------------------------------------------------------------------- /github-commit-status/main_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "net/http" 5 | "net/http/httptest" 6 | "os" 7 | "reflect" 8 | "runtime" 9 | "testing" 10 | ) 11 | 12 | var opts = &options{ 13 | Commit: "1234", 14 | Context: "ci", 15 | Description: "it passed", 16 | Repo: "repo1", 17 | State: "success", 18 | TargetUrl: "http://example.com", 19 | User: "user1", 20 | } 21 | 22 | var addHeaderTCs = []struct { 23 | header string 24 | value []string 25 | }{ 26 | {header: "Authorization", value: []string{"token 1234"}}, 27 | {header: "Content-Type", value: []string{"application/json"}}, 28 | {header: "User-Agent", value: []string{"github-commit-status/" + version + " (" + runtime.GOOS + ")"}}, 29 | } 30 | 31 | func TestAddHeaders(t *testing.T) { 32 | c := &config{apiToken: "1234"} 33 | req, _ := http.NewRequest("GET", "http://example.com", nil) 34 | addHeaders(c, req) 35 | for _, tc := range addHeaderTCs { 36 | result := req.Header[tc.header] 37 | if !reflect.DeepEqual(result, tc.value) { 38 | t.Errorf("Error: expected '%s' to be\n'%s' got\n'%s'", tc.header, tc.value, result) 39 | } 40 | } 41 | } 42 | 43 | var apiUrlTcs = []struct { 44 | envVarValue string 45 | expected string 46 | }{ 47 | {"", "https://api.github.com"}, 48 | {"https://github.example.com", "https://github.example.com"}, 49 | {"https://github.example.com/", "https://github.example.com"}, 50 | } 51 | 52 | func TestGetApiUrl(t *testing.T) { 53 | for _, tc := range apiUrlTcs { 54 | err := os.Setenv("GITHUB_API", tc.envVarValue) 55 | if err != nil { 56 | t.Errorf("Unable to set env var 'GITHUB_API'") 57 | } 58 | 59 | result := getApiUrl() 60 | if result != tc.expected { 61 | t.Errorf("Error: expected\n'%s' got\n'%s'", tc.expected, result) 62 | } 63 | os.Setenv("GITHUB_API", "") 64 | } 65 | } 66 | 67 | func TestStatusRequestBody(t *testing.T) { 68 | expected := `{"context":"ci","description":"it passed","state":"success","target_url":"http://example.com"}` 69 | 70 | result, err := statusRequestBody(opts) 71 | if err != nil { 72 | t.Errorf("Unable to generate status request body") 73 | } 74 | 75 | if string(result) != expected { 76 | t.Errorf("Error: expected\n'%s' got\n'%s'", expected, result) 77 | } 78 | 79 | } 80 | 81 | func TestStatusUrl(t *testing.T) { 82 | os.Setenv("GITHUB_API", "http://api.example.com") 83 | os.Setenv("GITHUB_TOKEN", "foobar") 84 | c := newConfig() 85 | expected := os.Getenv("GITHUB_API") + "/repos/user1/repo1/statuses/1234" 86 | result := statusUrl(opts, c) 87 | 88 | if result != expected { 89 | t.Errorf("Error: expected\n'%s' got \n'%s", expected, result) 90 | } 91 | os.Setenv("GITHUB_API", "") 92 | os.Setenv("GITHUB_TOKEN", "") 93 | } 94 | 95 | var verificationResponseTCs = []struct { 96 | responseCode int 97 | expectedErr bool 98 | }{ 99 | {http.StatusInternalServerError, true}, 100 | {http.StatusBadRequest, true}, 101 | {http.StatusCreated, false}, 102 | } 103 | 104 | func TestVerifyResponse(t *testing.T) { 105 | for _, tc := range verificationResponseTCs { 106 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 107 | http.Error(w, "something failed", tc.responseCode) 108 | })) 109 | 110 | resp, err := http.Get(ts.URL) 111 | if err != nil { 112 | t.Errorf("Error create test request") 113 | } 114 | 115 | result := verifyResponse(resp) 116 | 117 | if tc.expectedErr { 118 | if result == nil { 119 | t.Errorf("Error: expected a http '%d' status code to return an error got '%v'", tc.responseCode, err) 120 | } 121 | } else { 122 | if result != nil { 123 | t.Errorf("Error: expected a http '%d' status code to not return an error got '%v'", tc.responseCode, err) 124 | } 125 | } 126 | 127 | ts.Close() 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /github-commit-status/version.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | const version = "1.0.0" 4 | -------------------------------------------------------------------------------- /script/build_binaries.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -eo pipefail 4 | 5 | OS_TYPES=(darwin linux) 6 | PROJECT_ROOT="$( cd -P "$( dirname "${BASH_SOURCE[0]}" )/.." && pwd )" 7 | PKG_ROOT="$PROJECT_ROOT/pkg" 8 | PROJECT_NAME="github-commit-status" 9 | BIN_NAME="github-commit-status" 10 | 11 | echo -n "Removing existing binaries..." 12 | rm -fr $PKG_ROOT 13 | echo "Done" 14 | 15 | mkdir -p $PKG_ROOT 16 | 17 | version=$(grep version $PROJECT_NAME/version.go | awk '{print $4}' | sed 's/"//g') 18 | 19 | echo "Building packages for distribution for version $version." 20 | 21 | for os in ${OS_TYPES[*]}; do 22 | pkg_name="$BIN_NAME-$version-$os-amd64" 23 | pkg_path="$PKG_ROOT/$pkg_name" 24 | echo -n "Building binary for $os/amd64..." 25 | env GOOS=$os GOARCH=amd64 go build -o $pkg_path/$BIN_NAME ./$PROJECT_NAME 26 | cd $PKG_ROOT 27 | tar zcf $pkg_name.tar.gz -C $pkg_path $BIN_NAME 28 | shasum --algorithm 256 --binary $pkg_name.tar.gz >> $version-sha256-sums 29 | cd $PROJECT_ROOT 30 | echo "Done" 31 | done 32 | 33 | echo "" 34 | echo "Done" 35 | -------------------------------------------------------------------------------- /script/release: -------------------------------------------------------------------------------- 1 | # !/bin/bash 2 | 3 | set -euo pipefail 4 | IFS=$'\n\t' 5 | 6 | GITHUB_USER=thbishop 7 | PROJECT_NAME=github-commit-status 8 | 9 | create_release() { 10 | echo "" 11 | echo "Creating release '$version'." 12 | 13 | env GITHUB_API=https://api.github.com \ 14 | GITHUB_REPO=$PROJECT_NAME \ 15 | github-release release --tag $version --name $version --user $GITHUB_USER 16 | 17 | echo "" 18 | echo "Release '$version' created." 19 | } 20 | 21 | create_tag() { 22 | git tag -m "$version" $version 23 | echo "Created tag '$version'" 24 | } 25 | 26 | ensure_clean_and_committed() { 27 | set +e 28 | if ! git diff --exit-code --quiet || ! git diff --cached --exit-code --quiet; then 29 | echo "There are files that need to be committed or removed first." 30 | echo "" 31 | echo "$(git status)" 32 | exit 1 33 | fi 34 | set -e 35 | } 36 | 37 | ensure_on_master() { 38 | branch=$(git symbolic-ref --short -q HEAD) 39 | if [[ "$branch" != "master" ]]; then 40 | echo "Must be on master to create a release." 41 | echo "Currently on '$branch'." 42 | exit 1 43 | fi 44 | } 45 | 46 | ensure_release_cli_exists() { 47 | if [ ! $(which github-release) ]; then 48 | echo "Unable to find 'github-release' in your \$PATH." 49 | echo "If you're OSX you can 'brew install github-release'." 50 | echo "Otherwise, see https://github.com/aktau/github-release" 51 | exit 1 52 | fi 53 | } 54 | 55 | ensure_token() { 56 | set +u 57 | if [ -z "$GITHUB_TOKEN" ]; then 58 | echo "Env var 'GITHUB_TOKEN' is not set" 59 | exit 1 60 | fi 61 | set -u 62 | } 63 | 64 | push_to_origin() { 65 | echo "Pushing master and tags to origin" 66 | git push 67 | git push --tags 68 | } 69 | 70 | upload_artifacts() { 71 | echo "" 72 | upload_url="$(curl -s -H 'Accept: application/json' \ 73 | https://api.github.com/repos/$GITHUB_USER/$PROJECT_NAME/releases | \ 74 | jq -M .[].upload_url | sed -E 's/{.+$//g' | sed -E 's/"//g')" 75 | 76 | for f in $(ls pkg/*{.rpm,.tar.gz,sums}); do 77 | echo "Uploading '$(basename $f)' for release '$version'" 78 | curl -s \ 79 | -H "Authorization: token $GITHUB_TOKEN" \ 80 | -H "Accept: application/vnd.github.v3+json" \ 81 | -H "Content-Type: application/octet-stream" \ 82 | --data-binary @$f \ 83 | "$upload_url?name=$(basename $f)" > /dev/null 84 | done 85 | } 86 | 87 | ensure_token 88 | ensure_release_cli_exists 89 | ensure_on_master 90 | ensure_clean_and_committed 91 | 92 | version=$(grep version $PROJECT_NAME/version.go | 93 | awk '{print $4}' | 94 | sed 's/"//g') 95 | 96 | create_tag 97 | push_to_origin 98 | create_release 99 | upload_artifacts 100 | 101 | echo "Release complete!" 102 | --------------------------------------------------------------------------------