├── .circleci └── config.yml ├── .github └── workflows │ └── assign.yml ├── .golangci.yml ├── .goreleaser.yml ├── LICENSE ├── README.md ├── example ├── ranges.go ├── ranges.go.ast └── readme.go ├── go.mod ├── go.sum ├── main.go ├── scopelint ├── lint.go ├── lint_test.go ├── option.go ├── option_test.go └── problem.go └── vendor ├── github.com └── alecthomas │ ├── template │ ├── LICENSE │ ├── README.md │ ├── doc.go │ ├── example_test.go │ ├── examplefiles_test.go │ ├── examplefunc_test.go │ ├── exec.go │ ├── exec_test.go │ ├── funcs.go │ ├── helper.go │ ├── multi_test.go │ ├── parse │ │ ├── lex.go │ │ ├── lex_test.go │ │ ├── node.go │ │ ├── parse.go │ │ └── parse_test.go │ ├── template.go │ └── testdata │ │ ├── file1.tmpl │ │ ├── file2.tmpl │ │ ├── tmpl1.tmpl │ │ └── tmpl2.tmpl │ └── units │ ├── COPYING │ ├── README.md │ ├── bytes.go │ ├── bytes_test.go │ ├── doc.go │ ├── si.go │ └── util.go └── gopkg.in └── alecthomas └── kingpin.v2 ├── .travis.yml ├── COPYING ├── README.md ├── _examples ├── chat1 │ └── main.go ├── chat2 │ └── main.go ├── completion │ └── main.go ├── curl │ └── main.go ├── modular │ └── main.go └── ping │ └── main.go ├── actions.go ├── app.go ├── app_test.go ├── args.go ├── args_test.go ├── cmd.go ├── cmd └── genvalues │ └── main.go ├── cmd_test.go ├── completions.go ├── completions_test.go ├── doc.go ├── envar.go ├── examples_test.go ├── flags.go ├── flags_test.go ├── global.go ├── guesswidth.go ├── guesswidth_unix.go ├── model.go ├── parser.go ├── parser_test.go ├── parsers.go ├── parsers_test.go ├── templates.go ├── usage.go ├── usage_test.go ├── values.go ├── values.json ├── values_generated.go └── values_test.go /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | jobs: 3 | build: 4 | working_directory: /home/projects/github.com/kyoh86/scopelint 5 | docker: 6 | - image: kyoh86/gotest:latest 7 | steps: 8 | - checkout 9 | - run: 10 | name: install 11 | command: go install ./... 12 | - run: 13 | name: test and take coverage 14 | command: | 15 | mkdir -p .circleci/test/gotest 16 | go test -v -race -coverprofile=coverage.out ./... | go-junit-report > .circleci/test/gotest/results.xml; 17 | - run: 18 | name: upload coverage 19 | command: bash <(curl -s https://codecov.io/bash) 20 | - run: 21 | name: search lints 22 | command: golangci-lint run 23 | 24 | release: 25 | working_directory: /go/src/github.com/kyoh86/scopelint 26 | docker: 27 | - image: kyoh86/gotest:latest 28 | steps: 29 | - attach_workspace: 30 | at: /go 31 | - run: 32 | name: run goreleaser 33 | command: curl -sL https://git.io/goreleaser | bash 34 | 35 | workflows: 36 | version: 2 37 | commit: 38 | jobs: 39 | - build: 40 | filters: 41 | branches: 42 | only: /.*/ 43 | tags: 44 | only: /.*/ 45 | - release: 46 | requires: 47 | - build 48 | filters: 49 | branches: 50 | ignore: /.*/ 51 | tags: 52 | only: /^v[0-9]+(\.[0-9]+){2}$/ 53 | -------------------------------------------------------------------------------- /.github/workflows/assign.yml: -------------------------------------------------------------------------------- 1 | name: Issue assignment 2 | 3 | on: 4 | issues: 5 | types: [opened] 6 | 7 | jobs: 8 | auto-assign: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: 'Auto-assign issue' 12 | uses: pozil/auto-assign-issue@v1 13 | with: 14 | repo-token: ${{ secrets.GITHUB_TOKEN }} 15 | assignees: kyoh86 16 | -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | run: 2 | skip-dirs: 3 | - example 4 | -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | project_name: scopelint 2 | release: 3 | github: 4 | owner: kyoh86 5 | name: scopelint 6 | brew: 7 | install: bin.install "scopelint" 8 | github: 9 | owner: kyoh86 10 | name: homebrew-tap 11 | folder: Formula 12 | homepage: https://github.com/kyoh86/scopelint 13 | description: Checks for unpinned variables in go programs. 14 | builds: 15 | - goos: 16 | - linux 17 | - darwin 18 | goarch: 19 | - amd64 20 | - "386" 21 | goarm: 22 | - "6" 23 | main: . 24 | ldflags: -s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}} 25 | binary: scopelint 26 | archive: 27 | format: tar.gz 28 | name_template: '{{ .Binary }}_{{.Version}}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ 29 | .Arm }}{{ end }}' 30 | files: 31 | - licence* 32 | - LICENCE* 33 | - license* 34 | - LICENSE* 35 | - readme* 36 | - README* 37 | - changelog* 38 | - CHANGELOG* 39 | snapshot: 40 | name_template: SNAPSHOT-{{ .Commit }} 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019 The Go Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # scopelint 2 | 3 | [![Go Report Card](https://goreportcard.com/badge/github.com/kyoh86/scopelint)](https://goreportcard.com/report/github.com/kyoh86/scopelint) 4 | [![CircleCI](https://img.shields.io/circleci/project/github/kyoh86/scopelint.svg)](https://circleci.com/gh/kyoh86/scopelint) 5 | [![Coverage Status](https://img.shields.io/codecov/c/github/kyoh86/scopelint.svg)](https://codecov.io/gh/kyoh86/scopelint) 6 | 7 | **scopelint** checks for unpinned variables in go programs. 8 | 9 | ## OBSOLETED 10 | 11 | Use [looppointer](https://github.com/kyoh86/looppointer) or [exportloopref](https://github.com/kyoh86/exportloopref) instead. 12 | 13 | If you want to find lints as nervous as possible (with some false-positives), 14 | you should use [looppointer](https://github.com/kyoh86/looppointer). 15 | 16 | If you want to find lints as accurately as possible (with some lints ignored), 17 | you should use [exportloopref](https://github.com/kyoh86/exportloopref). 18 | 19 | ## What's this? 20 | 21 | Sample problem code from: https://github.com/kyoh86/scopelint/blob/master/example/readme.go 22 | 23 | ``` 24 | 6 values := []string{"a", "b", "c"} 25 | 7 var funcs []func() 26 | 8 for _, val := range values { 27 | 9 funcs = append(funcs, func() { 28 | 10 fmt.Println(val) 29 | 11 }) 30 | 12 } 31 | 13 for _, f := range funcs { 32 | 14 f() 33 | 15 } 34 | 16 /*output: 35 | 17 c 36 | 18 c 37 | 19 c 38 | 20 */ 39 | 21 var copies []*string 40 | 22 for _, val := range values { 41 | 23 copies = append(copies, &val) 42 | 24 } 43 | 25 /*(in copies) 44 | 26 &"c" 45 | 27 &"c" 46 | 28 &"c" 47 | 29 */ 48 | ``` 49 | 50 | In Go, the `val` variable in the above loops is actually a single variable. 51 | So in many case (like the above), using it makes for us annoying bugs. 52 | 53 | You can find them with `scopelint`, and fix it. 54 | 55 | ``` 56 | $ scopelint ./example/readme.go 57 | example/readme.go:10:16: Using the variable on range scope "val" in function literal 58 | example/readme.go:23:28: Using a reference for the variable on range scope "val" 59 | Found 2 lint problems; failing. 60 | ``` 61 | 62 | (Fixed sample): 63 | 64 | ```go 65 | values := []string{"a", "b", "c"} 66 | var funcs []func() 67 | for _, val := range values { 68 | val := val // pin! 69 | funcs = append(funcs, func() { 70 | fmt.Println(val) 71 | }) 72 | } 73 | for _, f := range funcs { 74 | f() 75 | } 76 | var copies []*string 77 | for _, val := range values { 78 | val := val // pin! 79 | copies = append(copies, &val) 80 | } 81 | ``` 82 | 83 | ## Install 84 | 85 | go get -u github.com/kyoh86/scopelint 86 | 87 | ## Use 88 | 89 | Give the package paths of interest as arguments: 90 | 91 | ``` 92 | scopelint github.com/kyoh86/scopelint/example 93 | ``` 94 | 95 | To check all packages recursively in the current directory: 96 | 97 | ``` 98 | scopelint ./... 99 | ``` 100 | 101 | And also, scopelint supports the following options: 102 | 103 | * The `--set-exit-status` flag makes it to set exit status to 1 if any problem variables are found (if you DO NOT it, set --no-set-exit-status) 104 | * The `--vendor` flag enables checking in the `vendor` directories (if you DO NOT it, set `--no-vendor` flag) 105 | * The `--test` flag enables checking in the `*_test.go` files" (if you DO NOT it, set `--no-test` flag) 106 | 107 | ### Nolint 108 | 109 | To ignore issues from use an option comment like //scopelint:ignore. 110 | For example, if it's used inline (not from the beginning of the line), it ignores issues only for the line. 111 | 112 | ```go 113 | var copies []*string 114 | for _, val := range values { 115 | copies = append(copies, &val) //scopelint:ignore 116 | } 117 | ``` 118 | 119 | To ignore issues for the block of code put it on the beginning of a line before the block: 120 | 121 | ```go 122 | var copies []*string 123 | //scopelint:ignore 124 | for _, val := range values { 125 | copies = append(copies, &val) 126 | } 127 | ``` 128 | 129 | Also, you can exclude all issues in a file by: 130 | 131 | ```go 132 | //scopelint:ignore 133 | package pkg 134 | ``` 135 | 136 | You may add a comment explaining or justifying why //scopelint:ignore is being used on the same line as the flag itself: 137 | 138 | ```go 139 | //scopelint:ignore // any comment 140 | ``` 141 | 142 | ### Use with gometalinter 143 | 144 | scopelint can be used with [gometalinter](https://github.com/alecthomas/gometalinter) in `--linter` flag. 145 | 146 | `gometalinter --disable-all --linter 'scope:scopelint {path}:^(?P.*?\.go):(?P\d+):(?P\d+):\s*(?P.*)$'` 147 | 148 | ## Exit Codes 149 | 150 | scopelint returns 1 if any problems were found in the checked files. 151 | It returns 2 if there were any other failures. 152 | 153 | ## Known Issues 154 | 155 | - False positive for for table tests [#4](https://github.com/kyoh86/scopelint/issues/4) 156 | - I won't fix it because scopelint supports ignore them all. [=> nolint](#Nolint) 157 | 158 | ## TODO 159 | 160 | - Write tests 161 | - License (Some codes copied from [golint](https://github.com/golang/lint)) 162 | -------------------------------------------------------------------------------- /example/ranges.go: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | // cat ./ranges.go | astdump > ranges.go.ast 4 | func ranges() { 5 | type uniStruct struct{ value string } 6 | m := map[string]uniStruct{ 7 | "foo": uniStruct{"bar"}, 8 | "hoge": uniStruct{"piyo"}, 9 | } 10 | var indices []*int 11 | var keys []*string 12 | var values []*uniStruct 13 | var functions []func() 14 | for key, value := range m { 15 | print(key, value.value) // safe 16 | func() { 17 | print(key, value.value) // safe 18 | }() 19 | functions = append(functions, func() { 20 | print(key, value.value) // unsafe 21 | }) 22 | keys = append(keys, &key) // unsafe 23 | values = append(values, &value) // unsafe 24 | 25 | key, value := key, value // safe 26 | functions = append(functions, func() { 27 | print(key, value.value) // safe 28 | }) 29 | keys = append(keys, &key) // safe 30 | values = append(values, &value) // safe 31 | } 32 | for key := range m { 33 | print(key) // safe 34 | func() { 35 | print(key) // safe 36 | }() 37 | functions = append(functions, func() { 38 | print(key) // unsafe 39 | }) 40 | keys = append(keys, &key) // unsafe 41 | 42 | key := key // safe 43 | functions = append(functions, func() { 44 | print(key) // safe 45 | }) 46 | keys = append(keys, &key) // safe 47 | } 48 | for i, j := 0, 0; i < 1; i++ { 49 | print(i) // safe 50 | print(j) 51 | func() { 52 | print(i) // safe 53 | print(j) // safe 54 | }() 55 | functions = append(functions, func() { 56 | print(i) // unsafe 57 | print(j) // unsafe 58 | }) 59 | indices = append(indices, &i) // unsafe 60 | 61 | i := i // safe 62 | j := j // safe 63 | functions = append(functions, func() { 64 | print(i) // safe 65 | print(j) // safe 66 | }) 67 | indices = append(indices, &i) // safe 68 | indices = append(indices, &j) // safe 69 | j++ 70 | } 71 | } 72 | 73 | var _ = ranges 74 | -------------------------------------------------------------------------------- /example/readme.go: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | import "fmt" 4 | 5 | func readme() { 6 | values := []string{"a", "b", "c"} 7 | var funcs []func() 8 | for _, val := range values { 9 | funcs = append(funcs, func() { 10 | fmt.Println(val) 11 | }) 12 | } 13 | for _, f := range funcs { 14 | f() 15 | } 16 | /*output: 17 | c 18 | c 19 | c 20 | (unstable)*/ 21 | var copies []*string 22 | for _, val := range values { 23 | copies = append(copies, &val) 24 | } 25 | /*(in copies) 26 | &"c" 27 | &"c" 28 | &"c" 29 | */ 30 | } 31 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/kyoh86/scopelint 2 | 3 | go 1.12 4 | 5 | require ( 6 | github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38 // indirect 7 | github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721 // indirect 8 | github.com/alecthomas/repr v0.0.0-20181024024818-d37bc2a10ba1 // indirect 9 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc // indirect 10 | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf // indirect 11 | github.com/mattn/go-isatty v0.0.6 // indirect 12 | github.com/sergi/go-diff v1.0.0 // indirect 13 | github.com/stretchr/testify v1.3.0 14 | gopkg.in/alecthomas/kingpin.v2 v2.2.5 15 | ) 16 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38 h1:smF2tmSOzy2Mm+0dGI2AIUHY+w0BUc+4tn40djz7+6U= 2 | github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38/go.mod h1:r7bzyVFMNntcxPZXK3/+KdruV1H5KSlyVY0gc+NgInI= 3 | github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721 h1:JHZL0hZKJ1VENNfmXvHbgYlbUOvpzYzvy2aZU5gXVeo= 4 | github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721/go.mod h1:QO9JBoKquHd+jz9nshCh40fOfO+JzsoXy8qTHF68zU0= 5 | github.com/alecthomas/repr v0.0.0-20181024024818-d37bc2a10ba1 h1:GDQdwm/gAcJcLAKQQZGOJ4knlw+7rfEQQcmwTbt4p5E= 6 | github.com/alecthomas/repr v0.0.0-20181024024818-d37bc2a10ba1/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ= 7 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc h1:cAKDfWh5VpdgMhJosfJnn5/FoN2SRZ4p7fJNX58YPaU= 8 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 9 | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZqLG4oE62mJzwPIB8+Tee4RNCL9ulrY= 10 | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 11 | github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= 12 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 13 | github.com/mattn/go-isatty v0.0.6 h1:SrwhHcpV4nWrMGdNcC2kXpMfcBVYGDuTArqyhocJgvA= 14 | github.com/mattn/go-isatty v0.0.6/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 15 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 16 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 17 | github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= 18 | github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= 19 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 20 | github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= 21 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 22 | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8= 23 | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 24 | gopkg.in/alecthomas/kingpin.v2 v2.2.5 h1:qskSCq465uEvC3oGocwvZNsO3RF3SpLVLumOAhL0bXo= 25 | gopkg.in/alecthomas/kingpin.v2 v2.2.5/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 26 | -------------------------------------------------------------------------------- /scopelint/lint.go: -------------------------------------------------------------------------------- 1 | // Package scopelint privides a linter for scopes of variable in `for {}`. 2 | package scopelint 3 | 4 | import ( 5 | "fmt" 6 | "go/ast" 7 | "go/parser" 8 | "go/token" 9 | "go/types" 10 | "sort" 11 | "strings" 12 | ) 13 | 14 | // A Linter lints Go source code. 15 | type Linter struct{} 16 | 17 | // Lint lints src. 18 | func (l *Linter) Lint(filename string, src []byte) ([]Problem, error) { 19 | return l.LintFiles(map[string][]byte{filename: src}) 20 | } 21 | 22 | // LintFiles lints a set of files of a single package. 23 | // The argument is a map of filename to source. 24 | func (l *Linter) LintFiles(files map[string][]byte) ([]Problem, error) { 25 | if len(files) == 0 { 26 | return nil, nil 27 | } 28 | 29 | pkg := &Package{ 30 | FileSet: token.NewFileSet(), 31 | Files: make(map[string]*File), 32 | } 33 | 34 | var pkgName string 35 | for filename, src := range files { 36 | astFile, err := parser.ParseFile(pkg.FileSet, filename, src, parser.ParseComments) 37 | if err != nil { 38 | return nil, err 39 | } 40 | if pkgName == "" { 41 | pkgName = astFile.Name.Name 42 | } else if strings.TrimSuffix(astFile.Name.Name, "_test") != strings.TrimSuffix(pkgName, "_test") { 43 | return nil, fmt.Errorf("%s is in package %s, not %s", filename, astFile.Name.Name, pkgName) 44 | } 45 | pkg.Files[filename] = &File{ 46 | Package: pkg, 47 | ASTFile: astFile, 48 | FileSet: pkg.FileSet, 49 | Source: src, 50 | Filename: filename, 51 | CommentMap: ast.NewCommentMap(pkg.FileSet, astFile, astFile.Comments), 52 | } 53 | } 54 | return pkg.lint(), nil 55 | } 56 | 57 | // Package represents a package being linted. 58 | type Package struct { 59 | FileSet *token.FileSet 60 | Files map[string]*File 61 | 62 | TypesPackage *types.Package 63 | TypesInfo *types.Info 64 | 65 | Problems []Problem 66 | } 67 | 68 | func (p *Package) lint() []Problem { 69 | for _, f := range p.Files { 70 | f.lint() 71 | } 72 | 73 | sort.Sort(problemsByPosition(p.Problems)) 74 | 75 | return p.Problems 76 | } 77 | 78 | // File represents a File being linted. 79 | type File struct { 80 | Package *Package 81 | ASTFile *ast.File 82 | FileSet *token.FileSet 83 | Source []byte 84 | Filename string 85 | CommentMap ast.CommentMap 86 | } 87 | 88 | func (f *File) lint() { 89 | ast.Walk(&Node{ 90 | File: *f, 91 | DangerObjects: map[*ast.Object]int{}, 92 | UnsafeObjects: map[*ast.Object]int{}, 93 | SkipFuncs: map[*ast.FuncLit]int{}, 94 | }, f.ASTFile) 95 | } 96 | 97 | // Node represents a Node being linted. 98 | type Node struct { 99 | File 100 | DangerObjects map[*ast.Object]int 101 | UnsafeObjects map[*ast.Object]int 102 | SkipFuncs map[*ast.FuncLit]int 103 | Ignore bool 104 | } 105 | 106 | // Visit method is invoked for each node encountered by Walk. 107 | // If the result visitor w is not nil, Walk visits each of the children 108 | // of node with the visitor w, followed by a call of w.Visit(nil). 109 | func (n *Node) Visit(node ast.Node) ast.Visitor { 110 | next := *n 111 | if node == nil { 112 | return &next 113 | } 114 | CGS_LOOP: 115 | for _, cg := range n.File.CommentMap[node] { 116 | for _, com := range cg.List { 117 | if hasOptionComment(com.Text, "ignore") { 118 | next.Ignore = true 119 | break CGS_LOOP 120 | } 121 | } 122 | } 123 | switch typedNode := node.(type) { 124 | case *ast.ForStmt: 125 | switch init := typedNode.Init.(type) { 126 | case *ast.AssignStmt: 127 | for _, lh := range init.Lhs { 128 | switch tlh := lh.(type) { 129 | case *ast.Ident: 130 | n.UnsafeObjects[tlh.Obj] = 0 131 | } 132 | } 133 | } 134 | 135 | case *ast.RangeStmt: 136 | // Memory variables declarated in range statement 137 | switch k := typedNode.Key.(type) { 138 | case *ast.Ident: 139 | n.UnsafeObjects[k.Obj] = 0 140 | } 141 | switch v := typedNode.Value.(type) { 142 | case *ast.Ident: 143 | n.UnsafeObjects[v.Obj] = 0 144 | } 145 | 146 | case *ast.UnaryExpr: 147 | if typedNode.Op == token.AND { 148 | switch ident := typedNode.X.(type) { 149 | case *ast.Ident: 150 | if _, unsafe := n.UnsafeObjects[ident.Obj]; unsafe { 151 | ref := "" 152 | n.errorf(ident, 1, n.Ignore, link(ref), category("range-scope"), "Using a reference for the variable on range scope %q", ident.Name) 153 | } 154 | } 155 | } 156 | 157 | case *ast.Ident: 158 | if _, obj := n.DangerObjects[typedNode.Obj]; obj { 159 | // It is the naked variable in scope of range statement. 160 | ref := "" 161 | n.errorf(node, 1, n.Ignore, link(ref), category("range-scope"), "Using the variable on range scope %q in function literal", typedNode.Name) 162 | break 163 | } 164 | 165 | case *ast.CallExpr: 166 | // Ignore func literals that'll be called immediately. 167 | switch funcLit := typedNode.Fun.(type) { 168 | case *ast.FuncLit: 169 | n.SkipFuncs[funcLit] = 0 170 | } 171 | 172 | case *ast.FuncLit: 173 | if _, skip := n.SkipFuncs[typedNode]; !skip { 174 | dangers := map[*ast.Object]int{} 175 | for d := range n.DangerObjects { 176 | dangers[d] = 0 177 | } 178 | for u := range n.UnsafeObjects { 179 | dangers[u] = 0 180 | n.UnsafeObjects[u]++ 181 | } 182 | next.DangerObjects = dangers 183 | return &next 184 | } 185 | 186 | case *ast.ReturnStmt: 187 | unsafe := map[*ast.Object]int{} 188 | for u := range n.UnsafeObjects { 189 | if n.UnsafeObjects[u] == 0 { 190 | continue 191 | } 192 | unsafe[u] = n.UnsafeObjects[u] 193 | } 194 | next.UnsafeObjects = unsafe 195 | return &next 196 | } 197 | return &next 198 | } 199 | 200 | type link string 201 | type category string 202 | 203 | // The variadic arguments may start with link and category types, 204 | // and must end with a format string and any arguments. 205 | // It returns the new Problem. 206 | func (f *File) errorf(n ast.Node, confidence float64, ignore bool, args ...interface{}) *Problem { 207 | pos := f.FileSet.Position(n.Pos()) 208 | if pos.Filename == "" { 209 | pos.Filename = f.Filename 210 | } 211 | return f.Package.errorfAt(pos, confidence, ignore, args...) 212 | } 213 | 214 | func (p *Package) errorfAt(pos token.Position, confidence float64, ignore bool, args ...interface{}) *Problem { 215 | problem := Problem{ 216 | Position: pos, 217 | Confidence: confidence, 218 | Ignored: ignore, 219 | } 220 | if pos.Filename != "" { 221 | // The file might not exist in our mapping if a //line directive was encountered. 222 | if f, ok := p.Files[pos.Filename]; ok { 223 | problem.LineText = srcLine(f.Source, pos) 224 | } 225 | } 226 | 227 | argLoop: 228 | for len(args) > 1 { // always leave at least the format string in args 229 | switch v := args[0].(type) { 230 | case link: 231 | problem.Link = string(v) 232 | case category: 233 | problem.Category = string(v) 234 | default: 235 | break argLoop 236 | } 237 | args = args[1:] 238 | } 239 | 240 | problem.Text = fmt.Sprintf(args[0].(string), args[1:]...) 241 | 242 | p.Problems = append(p.Problems, problem) 243 | return &p.Problems[len(p.Problems)-1] 244 | } 245 | 246 | // srcLine returns the complete line at p, including the terminating newline. 247 | func srcLine(src []byte, p token.Position) string { 248 | // Run to end of line in both directions if not at line start/end. 249 | lo, hi := p.Offset, p.Offset+1 250 | for lo > 0 && src[lo-1] != '\n' { 251 | lo-- 252 | } 253 | for hi < len(src) && src[hi-1] != '\n' { 254 | hi++ 255 | } 256 | return string(src[lo:hi]) 257 | } 258 | -------------------------------------------------------------------------------- /scopelint/lint_test.go: -------------------------------------------------------------------------------- 1 | package scopelint 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | "github.com/stretchr/testify/require" 8 | ) 9 | 10 | func TestLintFiles(t *testing.T) { 11 | // This is not more than stopgap for issues. 12 | 13 | t.Run("#2", func(t *testing.T) { 14 | files := map[string][]byte{ 15 | "mypkg/mypkg.go": []byte("package mypkg\n"), 16 | "mypkg/mypkg_test.go": []byte("package mypkg_test"), 17 | } 18 | l := new(Linter) 19 | 20 | promblems, err := l.LintFiles(files) 21 | assert.NoError(t, err, "DO NOT make error for valid test package") 22 | assert.Empty(t, promblems) 23 | }) 24 | } 25 | 26 | func TestLint(t *testing.T) { 27 | // This is not more than stopgap for issues. 28 | 29 | t.Run("#5: true positive", func(t *testing.T) { 30 | l := new(Linter) 31 | problems, err := l.Lint("mypkg/mypkg.go", []byte(`package main 32 | 33 | func factory() (ret func() *int) { 34 | for _, i := range make([]int, 1) { 35 | ret = func() *int { return &i } 36 | } 37 | return 38 | }`)) 39 | require.NoError(t, err) 40 | if assert.Len(t, problems, 2) { 41 | assert.Equal(t, "Using a reference for the variable on range scope \"i\"", problems[0].Text) 42 | assert.Equal(t, "Using the variable on range scope \"i\" in function literal", problems[1].Text) 43 | } 44 | }) 45 | 46 | t.Run("#5: false positive", func(t *testing.T) { 47 | l := new(Linter) 48 | problems, err := l.Lint("mypkg/mypkg.go", []byte(`package main 49 | 50 | func returning() *int { 51 | for _, i := range make([]int, 1) { 52 | return &i 53 | } 54 | return nil 55 | }`)) 56 | require.NoError(t, err) 57 | assert.Empty(t, problems) 58 | }) 59 | 60 | t.Run("issue #4", func(t *testing.T) { 61 | 62 | t.Run("positive", func(t *testing.T) { 63 | l := new(Linter) 64 | problems, err := l.Lint("mypkg/mypkg.go", []byte(`package main 65 | import "testing" 66 | 67 | func TestSomething(t *testing.T) { 68 | for _, tc := range []struct { 69 | expected string 70 | }{} { 71 | t.Run("sub", func(t *testing.T) { // :memo: t.Run runs sub func immediately 72 | if "result" != tc.expected { 73 | t.Fatal("failed") 74 | } 75 | }) 76 | } 77 | }`)) 78 | 79 | require.NoError(t, err) 80 | if assert.Len(t, problems, 1) { 81 | assert.Equal(t, "Using the variable on range scope \"tc\" in function literal", problems[0].Text) 82 | } 83 | }) 84 | 85 | t.Run("ignore line", func(t *testing.T) { 86 | l := new(Linter) 87 | problems, err := l.Lint("mypkg/mypkg.go", []byte(`package main 88 | 89 | import "testing" 90 | 91 | func TestSomething(t *testing.T) { 92 | for _, tc := range []struct { 93 | expected string 94 | }{} { 95 | t.Run("sub", func(t *testing.T) { // :memo: t.Run runs sub func immediately 96 | t.Log(tc.expected) //scopelint:ignore // "result" != tc.expected 97 | if "result" != tc.expected { 98 | t.Fatal("failed") 99 | } 100 | }) 101 | } 102 | }`)) 103 | 104 | require.NoError(t, err) 105 | if assert.Len(t, problems, 2) { 106 | assert.True(t, problems[0].Ignored, "%#v", problems[0]) 107 | assert.False(t, problems[1].Ignored, "%#v", problems[1]) 108 | } 109 | }) 110 | 111 | t.Run("ignore block", func(t *testing.T) { 112 | l := new(Linter) 113 | problems, err := l.Lint("mypkg/mypkg.go", []byte(`package main 114 | import "testing" 115 | 116 | func TestSomething(t *testing.T) { 117 | for _, tc := range []struct { 118 | expected string 119 | }{} { 120 | t.Run("sub", func(t *testing.T) { // :memo: t.Run runs sub func immediately 121 | //scopelint:ignore 122 | if "result" != tc.expected { 123 | t.Fatal("failed") 124 | } 125 | }) 126 | } 127 | }`)) 128 | 129 | require.NoError(t, err) 130 | if assert.Len(t, problems, 1) { 131 | assert.True(t, problems[0].Ignored, "%#v", problems[0]) 132 | } 133 | }) 134 | 135 | t.Run("ignore block with other comments", func(t *testing.T) { 136 | l := new(Linter) 137 | problems, err := l.Lint("mypkg/mypkg.go", []byte(`package main 138 | import "testing" 139 | 140 | func TestSomething(t *testing.T) { 141 | for _, tc := range []struct { 142 | expected string 143 | }{} { 144 | t.Run("sub", func(t *testing.T) { // :memo: t.Run runs sub func immediately 145 | //scopelint:ignore 146 | // compare expected and result 147 | if "result" != tc.expected { 148 | t.Fatal("failed") 149 | } 150 | }) 151 | } 152 | }`)) 153 | 154 | require.NoError(t, err) 155 | if assert.Len(t, problems, 1) { 156 | assert.True(t, problems[0].Ignored, "%#v", problems[0]) 157 | } 158 | }) 159 | 160 | t.Run("ignore ancestor block", func(t *testing.T) { 161 | l := new(Linter) 162 | problems, err := l.Lint("mypkg/mypkg.go", []byte(`package main 163 | import "testing" 164 | 165 | //scopelint:ignore 166 | func TestSomething(t *testing.T) { 167 | for _, tc := range []struct { 168 | expected string 169 | }{} { 170 | t.Run("sub", func(t *testing.T) { // :memo: t.Run runs sub func immediately 171 | if "result" != tc.expected { 172 | t.Fatal("failed") 173 | } 174 | }) 175 | } 176 | }`)) 177 | 178 | require.NoError(t, err) 179 | if assert.Len(t, problems, 1) { 180 | assert.True(t, problems[0].Ignored, "%#v", problems[0]) 181 | } 182 | }) 183 | t.Run("ignore file", func(t *testing.T) { 184 | l := new(Linter) 185 | problems, err := l.Lint("mypkg/mypkg.go", []byte(`//scopelint:ignore 186 | package main 187 | 188 | import "testing" 189 | 190 | func TestSomething(t *testing.T) { 191 | for _, tc := range []struct { 192 | expected string 193 | }{} { 194 | t.Run("sub", func(t *testing.T) { // :memo: t.Run runs sub func immediately 195 | if "result" != tc.expected { 196 | t.Fatal("failed") 197 | } 198 | }) 199 | } 200 | }`)) 201 | 202 | require.NoError(t, err) 203 | if assert.Len(t, problems, 1) { 204 | assert.True(t, problems[0].Ignored, "%#v", problems[0]) 205 | } 206 | }) 207 | 208 | t.Run("positive in next one of ignored line", func(t *testing.T) { 209 | l := new(Linter) 210 | problems, err := l.Lint("mypkg/mypkg.go", []byte(`package main 211 | import "testing" 212 | 213 | func TestSomething(t *testing.T) { 214 | for _, tc := range []struct { 215 | expected string 216 | }{} { 217 | t.Run("sub", func(t *testing.T) { // :memo: t.Run runs sub func immediately 218 | t.Log(tc.expected) //scopelint:ignore 219 | if "result" != tc.expected { 220 | t.Fatalf("failed") 221 | } 222 | }) 223 | } 224 | }`)) 225 | 226 | require.NoError(t, err) 227 | if assert.Len(t, problems, 2) { 228 | assert.True(t, problems[0].Ignored, "%#v", problems[0]) // t.Log ~ 229 | assert.False(t, problems[1].Ignored, "%#v", problems[1]) // if ~ 230 | } 231 | }) 232 | }) 233 | } 234 | -------------------------------------------------------------------------------- /scopelint/option.go: -------------------------------------------------------------------------------- 1 | package scopelint 2 | 3 | import ( 4 | "strings" 5 | ) 6 | 7 | const optionPrefix = "scopelint:" 8 | 9 | func parseOptionComment(comment string) (options []string) { 10 | foreachOptionComment(comment, func(opt string) bool { 11 | options = append(options, opt) 12 | return true 13 | }) 14 | return 15 | } 16 | 17 | func hasOptionComment(comment string, needle string) (having bool) { 18 | foreachOptionComment(comment, func(opt string) bool { 19 | if opt == needle { 20 | having = true 21 | return false 22 | } 23 | return true 24 | }) 25 | return 26 | } 27 | 28 | func foreachOptionComment(comment string, walk func(option string) (_continue bool)) { 29 | for _, sentence := range strings.Split(comment, "//") { 30 | sentence = strings.TrimSpace(sentence) 31 | if !strings.HasPrefix(sentence, optionPrefix) { 32 | continue 33 | } 34 | sentence = strings.TrimSpace(strings.TrimPrefix(sentence, optionPrefix)) 35 | for _, opt := range strings.Split(sentence, ",") { 36 | opt := strings.TrimSpace(opt) 37 | if opt != "" { 38 | if !walk(opt) { 39 | break 40 | } 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /scopelint/option_test.go: -------------------------------------------------------------------------------- 1 | package scopelint 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestParseOptionComment(t *testing.T) { 10 | t.Run("empty comment", func(t *testing.T) { 11 | assert.Nil(t, parseOptionComment("")) 12 | }) 13 | 14 | t.Run("normal comment", func(t *testing.T) { 15 | assert.Nil(t, parseOptionComment("comment")) 16 | }) 17 | 18 | t.Run("only prefix", func(t *testing.T) { 19 | assert.Empty(t, parseOptionComment("scopelint:")) 20 | }) 21 | 22 | t.Run("single option", func(t *testing.T) { 23 | assert.EqualValues(t, []string{"single"}, parseOptionComment("scopelint:single")) 24 | }) 25 | 26 | t.Run("multiple option", func(t *testing.T) { 27 | assert.EqualValues(t, []string{"one", "two"}, parseOptionComment("scopelint:one,two")) 28 | }) 29 | 30 | t.Run("multiple comment", func(t *testing.T) { 31 | assert.EqualValues(t, []string{"one", "two"}, parseOptionComment("scopelint:one//scopelint:two")) 32 | }) 33 | 34 | t.Run("ignore spaces", func(t *testing.T) { 35 | assert.EqualValues(t, []string{"one", "two"}, parseOptionComment(" scopelint: one , two ")) 36 | }) 37 | } 38 | 39 | func TestHasOptionComment(t *testing.T) { 40 | assert.True(t, hasOptionComment("scopelint:one,two,three", "one"), "first one") 41 | assert.True(t, hasOptionComment("scopelint:one,two,three", "two"), "second one") 42 | assert.True(t, hasOptionComment("scopelint:one,two,three", "three"), "third one") 43 | assert.False(t, hasOptionComment("scopelint:one,two,three", "four"), "none") 44 | } 45 | -------------------------------------------------------------------------------- /scopelint/problem.go: -------------------------------------------------------------------------------- 1 | package scopelint 2 | 3 | import "go/token" 4 | 5 | // Problem represents a problem in some source code. 6 | type Problem struct { 7 | Position token.Position // position in source file 8 | Text string // the prose that describes the problem 9 | Link string // (optional) the link to the style guide for the problem 10 | Confidence float64 // a value in (0,1] estimating the confidence in this problem's correctness 11 | LineText string // the source line 12 | Category string // a short name for the general category of the problem 13 | 14 | // If the problem has a suggested fix (the minority case), 15 | // ReplacementLine is a full replacement for the relevant line of the source file. 16 | ReplacementLine string 17 | 18 | Ignored bool // marks ignored issue by nolint directive 19 | } 20 | 21 | func (p *Problem) String() string { 22 | if p.Link != "" { 23 | return p.Text + "\n\n" + p.Link 24 | } 25 | return p.Text 26 | } 27 | 28 | type problemsByPosition []Problem 29 | 30 | func (p problemsByPosition) Len() int { return len(p) } 31 | func (p problemsByPosition) Swap(i, j int) { p[i], p[j] = p[j], p[i] } 32 | 33 | func (p problemsByPosition) Less(i, j int) bool { 34 | pi, pj := p[i].Position, p[j].Position 35 | 36 | if pi.Filename != pj.Filename { 37 | return pi.Filename < pj.Filename 38 | } 39 | if pi.Line != pj.Line { 40 | return pi.Line < pj.Line 41 | } 42 | if pi.Column != pj.Column { 43 | return pi.Column < pj.Column 44 | } 45 | 46 | return p[i].Text < p[j].Text 47 | } 48 | -------------------------------------------------------------------------------- /vendor/github.com/alecthomas/template/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 The Go Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /vendor/github.com/alecthomas/template/README.md: -------------------------------------------------------------------------------- 1 | # Go's `text/template` package with newline elision 2 | 3 | This is a fork of Go 1.4's [text/template](http://golang.org/pkg/text/template/) package with one addition: a backslash immediately after a closing delimiter will delete all subsequent newlines until a non-newline. 4 | 5 | eg. 6 | 7 | ``` 8 | {{if true}}\ 9 | hello 10 | {{end}}\ 11 | ``` 12 | 13 | Will result in: 14 | 15 | ``` 16 | hello\n 17 | ``` 18 | 19 | Rather than: 20 | 21 | ``` 22 | \n 23 | hello\n 24 | \n 25 | ``` 26 | -------------------------------------------------------------------------------- /vendor/github.com/alecthomas/template/example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. 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 template_test 6 | 7 | import ( 8 | "log" 9 | "os" 10 | 11 | "github.com/alecthomas/template" 12 | ) 13 | 14 | func ExampleTemplate() { 15 | // Define a template. 16 | const letter = ` 17 | Dear {{.Name}}, 18 | {{if .Attended}} 19 | It was a pleasure to see you at the wedding.{{else}} 20 | It is a shame you couldn't make it to the wedding.{{end}} 21 | {{with .Gift}}Thank you for the lovely {{.}}. 22 | {{end}} 23 | Best wishes, 24 | Josie 25 | ` 26 | 27 | // Prepare some data to insert into the template. 28 | type Recipient struct { 29 | Name, Gift string 30 | Attended bool 31 | } 32 | var recipients = []Recipient{ 33 | {"Aunt Mildred", "bone china tea set", true}, 34 | {"Uncle John", "moleskin pants", false}, 35 | {"Cousin Rodney", "", false}, 36 | } 37 | 38 | // Create a new template and parse the letter into it. 39 | t := template.Must(template.New("letter").Parse(letter)) 40 | 41 | // Execute the template for each recipient. 42 | for _, r := range recipients { 43 | err := t.Execute(os.Stdout, r) 44 | if err != nil { 45 | log.Println("executing template:", err) 46 | } 47 | } 48 | 49 | // Output: 50 | // Dear Aunt Mildred, 51 | // 52 | // It was a pleasure to see you at the wedding. 53 | // Thank you for the lovely bone china tea set. 54 | // 55 | // Best wishes, 56 | // Josie 57 | // 58 | // Dear Uncle John, 59 | // 60 | // It is a shame you couldn't make it to the wedding. 61 | // Thank you for the lovely moleskin pants. 62 | // 63 | // Best wishes, 64 | // Josie 65 | // 66 | // Dear Cousin Rodney, 67 | // 68 | // It is a shame you couldn't make it to the wedding. 69 | // 70 | // Best wishes, 71 | // Josie 72 | } 73 | -------------------------------------------------------------------------------- /vendor/github.com/alecthomas/template/examplefiles_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. 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 template_test 6 | 7 | import ( 8 | "io" 9 | "io/ioutil" 10 | "log" 11 | "os" 12 | "path/filepath" 13 | 14 | "github.com/alecthomas/template" 15 | ) 16 | 17 | // templateFile defines the contents of a template to be stored in a file, for testing. 18 | type templateFile struct { 19 | name string 20 | contents string 21 | } 22 | 23 | func createTestDir(files []templateFile) string { 24 | dir, err := ioutil.TempDir("", "template") 25 | if err != nil { 26 | log.Fatal(err) 27 | } 28 | for _, file := range files { 29 | f, err := os.Create(filepath.Join(dir, file.name)) 30 | if err != nil { 31 | log.Fatal(err) 32 | } 33 | defer f.Close() 34 | _, err = io.WriteString(f, file.contents) 35 | if err != nil { 36 | log.Fatal(err) 37 | } 38 | } 39 | return dir 40 | } 41 | 42 | // Here we demonstrate loading a set of templates from a directory. 43 | func ExampleTemplate_glob() { 44 | // Here we create a temporary directory and populate it with our sample 45 | // template definition files; usually the template files would already 46 | // exist in some location known to the program. 47 | dir := createTestDir([]templateFile{ 48 | // T0.tmpl is a plain template file that just invokes T1. 49 | {"T0.tmpl", `T0 invokes T1: ({{template "T1"}})`}, 50 | // T1.tmpl defines a template, T1 that invokes T2. 51 | {"T1.tmpl", `{{define "T1"}}T1 invokes T2: ({{template "T2"}}){{end}}`}, 52 | // T2.tmpl defines a template T2. 53 | {"T2.tmpl", `{{define "T2"}}This is T2{{end}}`}, 54 | }) 55 | // Clean up after the test; another quirk of running as an example. 56 | defer os.RemoveAll(dir) 57 | 58 | // pattern is the glob pattern used to find all the template files. 59 | pattern := filepath.Join(dir, "*.tmpl") 60 | 61 | // Here starts the example proper. 62 | // T0.tmpl is the first name matched, so it becomes the starting template, 63 | // the value returned by ParseGlob. 64 | tmpl := template.Must(template.ParseGlob(pattern)) 65 | 66 | err := tmpl.Execute(os.Stdout, nil) 67 | if err != nil { 68 | log.Fatalf("template execution: %s", err) 69 | } 70 | // Output: 71 | // T0 invokes T1: (T1 invokes T2: (This is T2)) 72 | } 73 | 74 | // This example demonstrates one way to share some templates 75 | // and use them in different contexts. In this variant we add multiple driver 76 | // templates by hand to an existing bundle of templates. 77 | func ExampleTemplate_helpers() { 78 | // Here we create a temporary directory and populate it with our sample 79 | // template definition files; usually the template files would already 80 | // exist in some location known to the program. 81 | dir := createTestDir([]templateFile{ 82 | // T1.tmpl defines a template, T1 that invokes T2. 83 | {"T1.tmpl", `{{define "T1"}}T1 invokes T2: ({{template "T2"}}){{end}}`}, 84 | // T2.tmpl defines a template T2. 85 | {"T2.tmpl", `{{define "T2"}}This is T2{{end}}`}, 86 | }) 87 | // Clean up after the test; another quirk of running as an example. 88 | defer os.RemoveAll(dir) 89 | 90 | // pattern is the glob pattern used to find all the template files. 91 | pattern := filepath.Join(dir, "*.tmpl") 92 | 93 | // Here starts the example proper. 94 | // Load the helpers. 95 | templates := template.Must(template.ParseGlob(pattern)) 96 | // Add one driver template to the bunch; we do this with an explicit template definition. 97 | _, err := templates.Parse("{{define `driver1`}}Driver 1 calls T1: ({{template `T1`}})\n{{end}}") 98 | if err != nil { 99 | log.Fatal("parsing driver1: ", err) 100 | } 101 | // Add another driver template. 102 | _, err = templates.Parse("{{define `driver2`}}Driver 2 calls T2: ({{template `T2`}})\n{{end}}") 103 | if err != nil { 104 | log.Fatal("parsing driver2: ", err) 105 | } 106 | // We load all the templates before execution. This package does not require 107 | // that behavior but html/template's escaping does, so it's a good habit. 108 | err = templates.ExecuteTemplate(os.Stdout, "driver1", nil) 109 | if err != nil { 110 | log.Fatalf("driver1 execution: %s", err) 111 | } 112 | err = templates.ExecuteTemplate(os.Stdout, "driver2", nil) 113 | if err != nil { 114 | log.Fatalf("driver2 execution: %s", err) 115 | } 116 | // Output: 117 | // Driver 1 calls T1: (T1 invokes T2: (This is T2)) 118 | // Driver 2 calls T2: (This is T2) 119 | } 120 | 121 | // This example demonstrates how to use one group of driver 122 | // templates with distinct sets of helper templates. 123 | func ExampleTemplate_share() { 124 | // Here we create a temporary directory and populate it with our sample 125 | // template definition files; usually the template files would already 126 | // exist in some location known to the program. 127 | dir := createTestDir([]templateFile{ 128 | // T0.tmpl is a plain template file that just invokes T1. 129 | {"T0.tmpl", "T0 ({{.}} version) invokes T1: ({{template `T1`}})\n"}, 130 | // T1.tmpl defines a template, T1 that invokes T2. Note T2 is not defined 131 | {"T1.tmpl", `{{define "T1"}}T1 invokes T2: ({{template "T2"}}){{end}}`}, 132 | }) 133 | // Clean up after the test; another quirk of running as an example. 134 | defer os.RemoveAll(dir) 135 | 136 | // pattern is the glob pattern used to find all the template files. 137 | pattern := filepath.Join(dir, "*.tmpl") 138 | 139 | // Here starts the example proper. 140 | // Load the drivers. 141 | drivers := template.Must(template.ParseGlob(pattern)) 142 | 143 | // We must define an implementation of the T2 template. First we clone 144 | // the drivers, then add a definition of T2 to the template name space. 145 | 146 | // 1. Clone the helper set to create a new name space from which to run them. 147 | first, err := drivers.Clone() 148 | if err != nil { 149 | log.Fatal("cloning helpers: ", err) 150 | } 151 | // 2. Define T2, version A, and parse it. 152 | _, err = first.Parse("{{define `T2`}}T2, version A{{end}}") 153 | if err != nil { 154 | log.Fatal("parsing T2: ", err) 155 | } 156 | 157 | // Now repeat the whole thing, using a different version of T2. 158 | // 1. Clone the drivers. 159 | second, err := drivers.Clone() 160 | if err != nil { 161 | log.Fatal("cloning drivers: ", err) 162 | } 163 | // 2. Define T2, version B, and parse it. 164 | _, err = second.Parse("{{define `T2`}}T2, version B{{end}}") 165 | if err != nil { 166 | log.Fatal("parsing T2: ", err) 167 | } 168 | 169 | // Execute the templates in the reverse order to verify the 170 | // first is unaffected by the second. 171 | err = second.ExecuteTemplate(os.Stdout, "T0.tmpl", "second") 172 | if err != nil { 173 | log.Fatalf("second execution: %s", err) 174 | } 175 | err = first.ExecuteTemplate(os.Stdout, "T0.tmpl", "first") 176 | if err != nil { 177 | log.Fatalf("first: execution: %s", err) 178 | } 179 | 180 | // Output: 181 | // T0 (second version) invokes T1: (T1 invokes T2: (T2, version B)) 182 | // T0 (first version) invokes T1: (T1 invokes T2: (T2, version A)) 183 | } 184 | -------------------------------------------------------------------------------- /vendor/github.com/alecthomas/template/examplefunc_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. 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 template_test 6 | 7 | import ( 8 | "log" 9 | "os" 10 | "strings" 11 | 12 | "github.com/alecthomas/template" 13 | ) 14 | 15 | // This example demonstrates a custom function to process template text. 16 | // It installs the strings.Title function and uses it to 17 | // Make Title Text Look Good In Our Template's Output. 18 | func ExampleTemplate_func() { 19 | // First we create a FuncMap with which to register the function. 20 | funcMap := template.FuncMap{ 21 | // The name "title" is what the function will be called in the template text. 22 | "title": strings.Title, 23 | } 24 | 25 | // A simple template definition to test our function. 26 | // We print the input text several ways: 27 | // - the original 28 | // - title-cased 29 | // - title-cased and then printed with %q 30 | // - printed with %q and then title-cased. 31 | const templateText = ` 32 | Input: {{printf "%q" .}} 33 | Output 0: {{title .}} 34 | Output 1: {{title . | printf "%q"}} 35 | Output 2: {{printf "%q" . | title}} 36 | ` 37 | 38 | // Create a template, add the function map, and parse the text. 39 | tmpl, err := template.New("titleTest").Funcs(funcMap).Parse(templateText) 40 | if err != nil { 41 | log.Fatalf("parsing: %s", err) 42 | } 43 | 44 | // Run the template to verify the output. 45 | err = tmpl.Execute(os.Stdout, "the go programming language") 46 | if err != nil { 47 | log.Fatalf("execution: %s", err) 48 | } 49 | 50 | // Output: 51 | // Input: "the go programming language" 52 | // Output 0: The Go Programming Language 53 | // Output 1: "The Go Programming Language" 54 | // Output 2: "The Go Programming Language" 55 | } 56 | -------------------------------------------------------------------------------- /vendor/github.com/alecthomas/template/helper.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. 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 | // Helper functions to make constructing templates easier. 6 | 7 | package template 8 | 9 | import ( 10 | "fmt" 11 | "io/ioutil" 12 | "path/filepath" 13 | ) 14 | 15 | // Functions and methods to parse templates. 16 | 17 | // Must is a helper that wraps a call to a function returning (*Template, error) 18 | // and panics if the error is non-nil. It is intended for use in variable 19 | // initializations such as 20 | // var t = template.Must(template.New("name").Parse("text")) 21 | func Must(t *Template, err error) *Template { 22 | if err != nil { 23 | panic(err) 24 | } 25 | return t 26 | } 27 | 28 | // ParseFiles creates a new Template and parses the template definitions from 29 | // the named files. The returned template's name will have the (base) name and 30 | // (parsed) contents of the first file. There must be at least one file. 31 | // If an error occurs, parsing stops and the returned *Template is nil. 32 | func ParseFiles(filenames ...string) (*Template, error) { 33 | return parseFiles(nil, filenames...) 34 | } 35 | 36 | // ParseFiles parses the named files and associates the resulting templates with 37 | // t. If an error occurs, parsing stops and the returned template is nil; 38 | // otherwise it is t. There must be at least one file. 39 | func (t *Template) ParseFiles(filenames ...string) (*Template, error) { 40 | return parseFiles(t, filenames...) 41 | } 42 | 43 | // parseFiles is the helper for the method and function. If the argument 44 | // template is nil, it is created from the first file. 45 | func parseFiles(t *Template, filenames ...string) (*Template, error) { 46 | if len(filenames) == 0 { 47 | // Not really a problem, but be consistent. 48 | return nil, fmt.Errorf("template: no files named in call to ParseFiles") 49 | } 50 | for _, filename := range filenames { 51 | b, err := ioutil.ReadFile(filename) 52 | if err != nil { 53 | return nil, err 54 | } 55 | s := string(b) 56 | name := filepath.Base(filename) 57 | // First template becomes return value if not already defined, 58 | // and we use that one for subsequent New calls to associate 59 | // all the templates together. Also, if this file has the same name 60 | // as t, this file becomes the contents of t, so 61 | // t, err := New(name).Funcs(xxx).ParseFiles(name) 62 | // works. Otherwise we create a new template associated with t. 63 | var tmpl *Template 64 | if t == nil { 65 | t = New(name) 66 | } 67 | if name == t.Name() { 68 | tmpl = t 69 | } else { 70 | tmpl = t.New(name) 71 | } 72 | _, err = tmpl.Parse(s) 73 | if err != nil { 74 | return nil, err 75 | } 76 | } 77 | return t, nil 78 | } 79 | 80 | // ParseGlob creates a new Template and parses the template definitions from the 81 | // files identified by the pattern, which must match at least one file. The 82 | // returned template will have the (base) name and (parsed) contents of the 83 | // first file matched by the pattern. ParseGlob is equivalent to calling 84 | // ParseFiles with the list of files matched by the pattern. 85 | func ParseGlob(pattern string) (*Template, error) { 86 | return parseGlob(nil, pattern) 87 | } 88 | 89 | // ParseGlob parses the template definitions in the files identified by the 90 | // pattern and associates the resulting templates with t. The pattern is 91 | // processed by filepath.Glob and must match at least one file. ParseGlob is 92 | // equivalent to calling t.ParseFiles with the list of files matched by the 93 | // pattern. 94 | func (t *Template) ParseGlob(pattern string) (*Template, error) { 95 | return parseGlob(t, pattern) 96 | } 97 | 98 | // parseGlob is the implementation of the function and method ParseGlob. 99 | func parseGlob(t *Template, pattern string) (*Template, error) { 100 | filenames, err := filepath.Glob(pattern) 101 | if err != nil { 102 | return nil, err 103 | } 104 | if len(filenames) == 0 { 105 | return nil, fmt.Errorf("template: pattern matches no files: %#q", pattern) 106 | } 107 | return parseFiles(t, filenames...) 108 | } 109 | -------------------------------------------------------------------------------- /vendor/github.com/alecthomas/template/multi_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. 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 template 6 | 7 | // Tests for mulitple-template parsing and execution. 8 | 9 | import ( 10 | "bytes" 11 | "fmt" 12 | "strings" 13 | "testing" 14 | 15 | "github.com/alecthomas/template/parse" 16 | ) 17 | 18 | const ( 19 | noError = true 20 | hasError = false 21 | ) 22 | 23 | type multiParseTest struct { 24 | name string 25 | input string 26 | ok bool 27 | names []string 28 | results []string 29 | } 30 | 31 | var multiParseTests = []multiParseTest{ 32 | {"empty", "", noError, 33 | nil, 34 | nil}, 35 | {"one", `{{define "foo"}} FOO {{end}}`, noError, 36 | []string{"foo"}, 37 | []string{" FOO "}}, 38 | {"two", `{{define "foo"}} FOO {{end}}{{define "bar"}} BAR {{end}}`, noError, 39 | []string{"foo", "bar"}, 40 | []string{" FOO ", " BAR "}}, 41 | // errors 42 | {"missing end", `{{define "foo"}} FOO `, hasError, 43 | nil, 44 | nil}, 45 | {"malformed name", `{{define "foo}} FOO `, hasError, 46 | nil, 47 | nil}, 48 | } 49 | 50 | func TestMultiParse(t *testing.T) { 51 | for _, test := range multiParseTests { 52 | template, err := New("root").Parse(test.input) 53 | switch { 54 | case err == nil && !test.ok: 55 | t.Errorf("%q: expected error; got none", test.name) 56 | continue 57 | case err != nil && test.ok: 58 | t.Errorf("%q: unexpected error: %v", test.name, err) 59 | continue 60 | case err != nil && !test.ok: 61 | // expected error, got one 62 | if *debug { 63 | fmt.Printf("%s: %s\n\t%s\n", test.name, test.input, err) 64 | } 65 | continue 66 | } 67 | if template == nil { 68 | continue 69 | } 70 | if len(template.tmpl) != len(test.names)+1 { // +1 for root 71 | t.Errorf("%s: wrong number of templates; wanted %d got %d", test.name, len(test.names), len(template.tmpl)) 72 | continue 73 | } 74 | for i, name := range test.names { 75 | tmpl, ok := template.tmpl[name] 76 | if !ok { 77 | t.Errorf("%s: can't find template %q", test.name, name) 78 | continue 79 | } 80 | result := tmpl.Root.String() 81 | if result != test.results[i] { 82 | t.Errorf("%s=(%q): got\n\t%v\nexpected\n\t%v", test.name, test.input, result, test.results[i]) 83 | } 84 | } 85 | } 86 | } 87 | 88 | var multiExecTests = []execTest{ 89 | {"empty", "", "", nil, true}, 90 | {"text", "some text", "some text", nil, true}, 91 | {"invoke x", `{{template "x" .SI}}`, "TEXT", tVal, true}, 92 | {"invoke x no args", `{{template "x"}}`, "TEXT", tVal, true}, 93 | {"invoke dot int", `{{template "dot" .I}}`, "17", tVal, true}, 94 | {"invoke dot []int", `{{template "dot" .SI}}`, "[3 4 5]", tVal, true}, 95 | {"invoke dotV", `{{template "dotV" .U}}`, "v", tVal, true}, 96 | {"invoke nested int", `{{template "nested" .I}}`, "17", tVal, true}, 97 | {"variable declared by template", `{{template "nested" $x:=.SI}},{{index $x 1}}`, "[3 4 5],4", tVal, true}, 98 | 99 | // User-defined function: test argument evaluator. 100 | {"testFunc literal", `{{oneArg "joe"}}`, "oneArg=joe", tVal, true}, 101 | {"testFunc .", `{{oneArg .}}`, "oneArg=joe", "joe", true}, 102 | } 103 | 104 | // These strings are also in testdata/*. 105 | const multiText1 = ` 106 | {{define "x"}}TEXT{{end}} 107 | {{define "dotV"}}{{.V}}{{end}} 108 | ` 109 | 110 | const multiText2 = ` 111 | {{define "dot"}}{{.}}{{end}} 112 | {{define "nested"}}{{template "dot" .}}{{end}} 113 | ` 114 | 115 | func TestMultiExecute(t *testing.T) { 116 | // Declare a couple of templates first. 117 | template, err := New("root").Parse(multiText1) 118 | if err != nil { 119 | t.Fatalf("parse error for 1: %s", err) 120 | } 121 | _, err = template.Parse(multiText2) 122 | if err != nil { 123 | t.Fatalf("parse error for 2: %s", err) 124 | } 125 | testExecute(multiExecTests, template, t) 126 | } 127 | 128 | func TestParseFiles(t *testing.T) { 129 | _, err := ParseFiles("DOES NOT EXIST") 130 | if err == nil { 131 | t.Error("expected error for non-existent file; got none") 132 | } 133 | template := New("root") 134 | _, err = template.ParseFiles("testdata/file1.tmpl", "testdata/file2.tmpl") 135 | if err != nil { 136 | t.Fatalf("error parsing files: %v", err) 137 | } 138 | testExecute(multiExecTests, template, t) 139 | } 140 | 141 | func TestParseGlob(t *testing.T) { 142 | _, err := ParseGlob("DOES NOT EXIST") 143 | if err == nil { 144 | t.Error("expected error for non-existent file; got none") 145 | } 146 | _, err = New("error").ParseGlob("[x") 147 | if err == nil { 148 | t.Error("expected error for bad pattern; got none") 149 | } 150 | template := New("root") 151 | _, err = template.ParseGlob("testdata/file*.tmpl") 152 | if err != nil { 153 | t.Fatalf("error parsing files: %v", err) 154 | } 155 | testExecute(multiExecTests, template, t) 156 | } 157 | 158 | // In these tests, actual content (not just template definitions) comes from the parsed files. 159 | 160 | var templateFileExecTests = []execTest{ 161 | {"test", `{{template "tmpl1.tmpl"}}{{template "tmpl2.tmpl"}}`, "template1\n\ny\ntemplate2\n\nx\n", 0, true}, 162 | } 163 | 164 | func TestParseFilesWithData(t *testing.T) { 165 | template, err := New("root").ParseFiles("testdata/tmpl1.tmpl", "testdata/tmpl2.tmpl") 166 | if err != nil { 167 | t.Fatalf("error parsing files: %v", err) 168 | } 169 | testExecute(templateFileExecTests, template, t) 170 | } 171 | 172 | func TestParseGlobWithData(t *testing.T) { 173 | template, err := New("root").ParseGlob("testdata/tmpl*.tmpl") 174 | if err != nil { 175 | t.Fatalf("error parsing files: %v", err) 176 | } 177 | testExecute(templateFileExecTests, template, t) 178 | } 179 | 180 | const ( 181 | cloneText1 = `{{define "a"}}{{template "b"}}{{template "c"}}{{end}}` 182 | cloneText2 = `{{define "b"}}b{{end}}` 183 | cloneText3 = `{{define "c"}}root{{end}}` 184 | cloneText4 = `{{define "c"}}clone{{end}}` 185 | ) 186 | 187 | func TestClone(t *testing.T) { 188 | // Create some templates and clone the root. 189 | root, err := New("root").Parse(cloneText1) 190 | if err != nil { 191 | t.Fatal(err) 192 | } 193 | _, err = root.Parse(cloneText2) 194 | if err != nil { 195 | t.Fatal(err) 196 | } 197 | clone := Must(root.Clone()) 198 | // Add variants to both. 199 | _, err = root.Parse(cloneText3) 200 | if err != nil { 201 | t.Fatal(err) 202 | } 203 | _, err = clone.Parse(cloneText4) 204 | if err != nil { 205 | t.Fatal(err) 206 | } 207 | // Verify that the clone is self-consistent. 208 | for k, v := range clone.tmpl { 209 | if k == clone.name && v.tmpl[k] != clone { 210 | t.Error("clone does not contain root") 211 | } 212 | if v != v.tmpl[v.name] { 213 | t.Errorf("clone does not contain self for %q", k) 214 | } 215 | } 216 | // Execute root. 217 | var b bytes.Buffer 218 | err = root.ExecuteTemplate(&b, "a", 0) 219 | if err != nil { 220 | t.Fatal(err) 221 | } 222 | if b.String() != "broot" { 223 | t.Errorf("expected %q got %q", "broot", b.String()) 224 | } 225 | // Execute copy. 226 | b.Reset() 227 | err = clone.ExecuteTemplate(&b, "a", 0) 228 | if err != nil { 229 | t.Fatal(err) 230 | } 231 | if b.String() != "bclone" { 232 | t.Errorf("expected %q got %q", "bclone", b.String()) 233 | } 234 | } 235 | 236 | func TestAddParseTree(t *testing.T) { 237 | // Create some templates. 238 | root, err := New("root").Parse(cloneText1) 239 | if err != nil { 240 | t.Fatal(err) 241 | } 242 | _, err = root.Parse(cloneText2) 243 | if err != nil { 244 | t.Fatal(err) 245 | } 246 | // Add a new parse tree. 247 | tree, err := parse.Parse("cloneText3", cloneText3, "", "", nil, builtins) 248 | if err != nil { 249 | t.Fatal(err) 250 | } 251 | added, err := root.AddParseTree("c", tree["c"]) 252 | // Execute. 253 | var b bytes.Buffer 254 | err = added.ExecuteTemplate(&b, "a", 0) 255 | if err != nil { 256 | t.Fatal(err) 257 | } 258 | if b.String() != "broot" { 259 | t.Errorf("expected %q got %q", "broot", b.String()) 260 | } 261 | } 262 | 263 | // Issue 7032 264 | func TestAddParseTreeToUnparsedTemplate(t *testing.T) { 265 | master := "{{define \"master\"}}{{end}}" 266 | tmpl := New("master") 267 | tree, err := parse.Parse("master", master, "", "", nil) 268 | if err != nil { 269 | t.Fatalf("unexpected parse err: %v", err) 270 | } 271 | masterTree := tree["master"] 272 | tmpl.AddParseTree("master", masterTree) // used to panic 273 | } 274 | 275 | func TestRedefinition(t *testing.T) { 276 | var tmpl *Template 277 | var err error 278 | if tmpl, err = New("tmpl1").Parse(`{{define "test"}}foo{{end}}`); err != nil { 279 | t.Fatalf("parse 1: %v", err) 280 | } 281 | if _, err = tmpl.Parse(`{{define "test"}}bar{{end}}`); err == nil { 282 | t.Fatal("expected error") 283 | } 284 | if !strings.Contains(err.Error(), "redefinition") { 285 | t.Fatalf("expected redefinition error; got %v", err) 286 | } 287 | if _, err = tmpl.New("tmpl2").Parse(`{{define "test"}}bar{{end}}`); err == nil { 288 | t.Fatal("expected error") 289 | } 290 | if !strings.Contains(err.Error(), "redefinition") { 291 | t.Fatalf("expected redefinition error; got %v", err) 292 | } 293 | } 294 | -------------------------------------------------------------------------------- /vendor/github.com/alecthomas/template/template.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. 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 template 6 | 7 | import ( 8 | "fmt" 9 | "reflect" 10 | 11 | "github.com/alecthomas/template/parse" 12 | ) 13 | 14 | // common holds the information shared by related templates. 15 | type common struct { 16 | tmpl map[string]*Template 17 | // We use two maps, one for parsing and one for execution. 18 | // This separation makes the API cleaner since it doesn't 19 | // expose reflection to the client. 20 | parseFuncs FuncMap 21 | execFuncs map[string]reflect.Value 22 | } 23 | 24 | // Template is the representation of a parsed template. The *parse.Tree 25 | // field is exported only for use by html/template and should be treated 26 | // as unexported by all other clients. 27 | type Template struct { 28 | name string 29 | *parse.Tree 30 | *common 31 | leftDelim string 32 | rightDelim string 33 | } 34 | 35 | // New allocates a new template with the given name. 36 | func New(name string) *Template { 37 | return &Template{ 38 | name: name, 39 | } 40 | } 41 | 42 | // Name returns the name of the template. 43 | func (t *Template) Name() string { 44 | return t.name 45 | } 46 | 47 | // New allocates a new template associated with the given one and with the same 48 | // delimiters. The association, which is transitive, allows one template to 49 | // invoke another with a {{template}} action. 50 | func (t *Template) New(name string) *Template { 51 | t.init() 52 | return &Template{ 53 | name: name, 54 | common: t.common, 55 | leftDelim: t.leftDelim, 56 | rightDelim: t.rightDelim, 57 | } 58 | } 59 | 60 | func (t *Template) init() { 61 | if t.common == nil { 62 | t.common = new(common) 63 | t.tmpl = make(map[string]*Template) 64 | t.parseFuncs = make(FuncMap) 65 | t.execFuncs = make(map[string]reflect.Value) 66 | } 67 | } 68 | 69 | // Clone returns a duplicate of the template, including all associated 70 | // templates. The actual representation is not copied, but the name space of 71 | // associated templates is, so further calls to Parse in the copy will add 72 | // templates to the copy but not to the original. Clone can be used to prepare 73 | // common templates and use them with variant definitions for other templates 74 | // by adding the variants after the clone is made. 75 | func (t *Template) Clone() (*Template, error) { 76 | nt := t.copy(nil) 77 | nt.init() 78 | nt.tmpl[t.name] = nt 79 | for k, v := range t.tmpl { 80 | if k == t.name { // Already installed. 81 | continue 82 | } 83 | // The associated templates share nt's common structure. 84 | tmpl := v.copy(nt.common) 85 | nt.tmpl[k] = tmpl 86 | } 87 | for k, v := range t.parseFuncs { 88 | nt.parseFuncs[k] = v 89 | } 90 | for k, v := range t.execFuncs { 91 | nt.execFuncs[k] = v 92 | } 93 | return nt, nil 94 | } 95 | 96 | // copy returns a shallow copy of t, with common set to the argument. 97 | func (t *Template) copy(c *common) *Template { 98 | nt := New(t.name) 99 | nt.Tree = t.Tree 100 | nt.common = c 101 | nt.leftDelim = t.leftDelim 102 | nt.rightDelim = t.rightDelim 103 | return nt 104 | } 105 | 106 | // AddParseTree creates a new template with the name and parse tree 107 | // and associates it with t. 108 | func (t *Template) AddParseTree(name string, tree *parse.Tree) (*Template, error) { 109 | if t.common != nil && t.tmpl[name] != nil { 110 | return nil, fmt.Errorf("template: redefinition of template %q", name) 111 | } 112 | nt := t.New(name) 113 | nt.Tree = tree 114 | t.tmpl[name] = nt 115 | return nt, nil 116 | } 117 | 118 | // Templates returns a slice of the templates associated with t, including t 119 | // itself. 120 | func (t *Template) Templates() []*Template { 121 | if t.common == nil { 122 | return nil 123 | } 124 | // Return a slice so we don't expose the map. 125 | m := make([]*Template, 0, len(t.tmpl)) 126 | for _, v := range t.tmpl { 127 | m = append(m, v) 128 | } 129 | return m 130 | } 131 | 132 | // Delims sets the action delimiters to the specified strings, to be used in 133 | // subsequent calls to Parse, ParseFiles, or ParseGlob. Nested template 134 | // definitions will inherit the settings. An empty delimiter stands for the 135 | // corresponding default: {{ or }}. 136 | // The return value is the template, so calls can be chained. 137 | func (t *Template) Delims(left, right string) *Template { 138 | t.leftDelim = left 139 | t.rightDelim = right 140 | return t 141 | } 142 | 143 | // Funcs adds the elements of the argument map to the template's function map. 144 | // It panics if a value in the map is not a function with appropriate return 145 | // type. However, it is legal to overwrite elements of the map. The return 146 | // value is the template, so calls can be chained. 147 | func (t *Template) Funcs(funcMap FuncMap) *Template { 148 | t.init() 149 | addValueFuncs(t.execFuncs, funcMap) 150 | addFuncs(t.parseFuncs, funcMap) 151 | return t 152 | } 153 | 154 | // Lookup returns the template with the given name that is associated with t, 155 | // or nil if there is no such template. 156 | func (t *Template) Lookup(name string) *Template { 157 | if t.common == nil { 158 | return nil 159 | } 160 | return t.tmpl[name] 161 | } 162 | 163 | // Parse parses a string into a template. Nested template definitions will be 164 | // associated with the top-level template t. Parse may be called multiple times 165 | // to parse definitions of templates to associate with t. It is an error if a 166 | // resulting template is non-empty (contains content other than template 167 | // definitions) and would replace a non-empty template with the same name. 168 | // (In multiple calls to Parse with the same receiver template, only one call 169 | // can contain text other than space, comments, and template definitions.) 170 | func (t *Template) Parse(text string) (*Template, error) { 171 | t.init() 172 | trees, err := parse.Parse(t.name, text, t.leftDelim, t.rightDelim, t.parseFuncs, builtins) 173 | if err != nil { 174 | return nil, err 175 | } 176 | // Add the newly parsed trees, including the one for t, into our common structure. 177 | for name, tree := range trees { 178 | // If the name we parsed is the name of this template, overwrite this template. 179 | // The associate method checks it's not a redefinition. 180 | tmpl := t 181 | if name != t.name { 182 | tmpl = t.New(name) 183 | } 184 | // Even if t == tmpl, we need to install it in the common.tmpl map. 185 | if replace, err := t.associate(tmpl, tree); err != nil { 186 | return nil, err 187 | } else if replace { 188 | tmpl.Tree = tree 189 | } 190 | tmpl.leftDelim = t.leftDelim 191 | tmpl.rightDelim = t.rightDelim 192 | } 193 | return t, nil 194 | } 195 | 196 | // associate installs the new template into the group of templates associated 197 | // with t. It is an error to reuse a name except to overwrite an empty 198 | // template. The two are already known to share the common structure. 199 | // The boolean return value reports wither to store this tree as t.Tree. 200 | func (t *Template) associate(new *Template, tree *parse.Tree) (bool, error) { 201 | if new.common != t.common { 202 | panic("internal error: associate not common") 203 | } 204 | name := new.name 205 | if old := t.tmpl[name]; old != nil { 206 | oldIsEmpty := parse.IsEmptyTree(old.Root) 207 | newIsEmpty := parse.IsEmptyTree(tree.Root) 208 | if newIsEmpty { 209 | // Whether old is empty or not, new is empty; no reason to replace old. 210 | return false, nil 211 | } 212 | if !oldIsEmpty { 213 | return false, fmt.Errorf("template: redefinition of template %q", name) 214 | } 215 | } 216 | t.tmpl[name] = new 217 | return true, nil 218 | } 219 | -------------------------------------------------------------------------------- /vendor/github.com/alecthomas/template/testdata/file1.tmpl: -------------------------------------------------------------------------------- 1 | {{define "x"}}TEXT{{end}} 2 | {{define "dotV"}}{{.V}}{{end}} 3 | -------------------------------------------------------------------------------- /vendor/github.com/alecthomas/template/testdata/file2.tmpl: -------------------------------------------------------------------------------- 1 | {{define "dot"}}{{.}}{{end}} 2 | {{define "nested"}}{{template "dot" .}}{{end}} 3 | -------------------------------------------------------------------------------- /vendor/github.com/alecthomas/template/testdata/tmpl1.tmpl: -------------------------------------------------------------------------------- 1 | template1 2 | {{define "x"}}x{{end}} 3 | {{template "y"}} 4 | -------------------------------------------------------------------------------- /vendor/github.com/alecthomas/template/testdata/tmpl2.tmpl: -------------------------------------------------------------------------------- 1 | template2 2 | {{define "y"}}y{{end}} 3 | {{template "x"}} 4 | -------------------------------------------------------------------------------- /vendor/github.com/alecthomas/units/COPYING: -------------------------------------------------------------------------------- 1 | Copyright (C) 2014 Alec Thomas 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /vendor/github.com/alecthomas/units/README.md: -------------------------------------------------------------------------------- 1 | # Units - Helpful unit multipliers and functions for Go 2 | 3 | The goal of this package is to have functionality similar to the [time](http://golang.org/pkg/time/) package. 4 | 5 | It allows for code like this: 6 | 7 | ```go 8 | n, err := ParseBase2Bytes("1KB") 9 | // n == 1024 10 | n = units.Mebibyte * 512 11 | ``` 12 | -------------------------------------------------------------------------------- /vendor/github.com/alecthomas/units/bytes.go: -------------------------------------------------------------------------------- 1 | package units 2 | 3 | // Base2Bytes is the old non-SI power-of-2 byte scale (1024 bytes in a kilobyte, 4 | // etc.). 5 | type Base2Bytes int64 6 | 7 | // Base-2 byte units. 8 | const ( 9 | Kibibyte Base2Bytes = 1024 10 | KiB = Kibibyte 11 | Mebibyte = Kibibyte * 1024 12 | MiB = Mebibyte 13 | Gibibyte = Mebibyte * 1024 14 | GiB = Gibibyte 15 | Tebibyte = Gibibyte * 1024 16 | TiB = Tebibyte 17 | Pebibyte = Tebibyte * 1024 18 | PiB = Pebibyte 19 | Exbibyte = Pebibyte * 1024 20 | EiB = Exbibyte 21 | ) 22 | 23 | var ( 24 | bytesUnitMap = MakeUnitMap("iB", "B", 1024) 25 | oldBytesUnitMap = MakeUnitMap("B", "B", 1024) 26 | ) 27 | 28 | // ParseBase2Bytes supports both iB and B in base-2 multipliers. That is, KB 29 | // and KiB are both 1024. 30 | func ParseBase2Bytes(s string) (Base2Bytes, error) { 31 | n, err := ParseUnit(s, bytesUnitMap) 32 | if err != nil { 33 | n, err = ParseUnit(s, oldBytesUnitMap) 34 | } 35 | return Base2Bytes(n), err 36 | } 37 | 38 | func (b Base2Bytes) String() string { 39 | return ToString(int64(b), 1024, "iB", "B") 40 | } 41 | 42 | var ( 43 | metricBytesUnitMap = MakeUnitMap("B", "B", 1000) 44 | ) 45 | 46 | // MetricBytes are SI byte units (1000 bytes in a kilobyte). 47 | type MetricBytes SI 48 | 49 | // SI base-10 byte units. 50 | const ( 51 | Kilobyte MetricBytes = 1000 52 | KB = Kilobyte 53 | Megabyte = Kilobyte * 1000 54 | MB = Megabyte 55 | Gigabyte = Megabyte * 1000 56 | GB = Gigabyte 57 | Terabyte = Gigabyte * 1000 58 | TB = Terabyte 59 | Petabyte = Terabyte * 1000 60 | PB = Petabyte 61 | Exabyte = Petabyte * 1000 62 | EB = Exabyte 63 | ) 64 | 65 | // ParseMetricBytes parses base-10 metric byte units. That is, KB is 1000 bytes. 66 | func ParseMetricBytes(s string) (MetricBytes, error) { 67 | n, err := ParseUnit(s, metricBytesUnitMap) 68 | return MetricBytes(n), err 69 | } 70 | 71 | func (m MetricBytes) String() string { 72 | return ToString(int64(m), 1000, "B", "B") 73 | } 74 | 75 | // ParseStrictBytes supports both iB and B suffixes for base 2 and metric, 76 | // respectively. That is, KiB represents 1024 and KB represents 1000. 77 | func ParseStrictBytes(s string) (int64, error) { 78 | n, err := ParseUnit(s, bytesUnitMap) 79 | if err != nil { 80 | n, err = ParseUnit(s, metricBytesUnitMap) 81 | } 82 | return int64(n), err 83 | } 84 | -------------------------------------------------------------------------------- /vendor/github.com/alecthomas/units/bytes_test.go: -------------------------------------------------------------------------------- 1 | package units 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestBase2BytesString(t *testing.T) { 10 | assert.Equal(t, Base2Bytes(0).String(), "0B") 11 | assert.Equal(t, Base2Bytes(1025).String(), "1KiB1B") 12 | assert.Equal(t, Base2Bytes(1048577).String(), "1MiB1B") 13 | } 14 | 15 | func TestParseBase2Bytes(t *testing.T) { 16 | n, err := ParseBase2Bytes("0B") 17 | assert.NoError(t, err) 18 | assert.Equal(t, 0, int(n)) 19 | n, err = ParseBase2Bytes("1KB") 20 | assert.NoError(t, err) 21 | assert.Equal(t, 1024, int(n)) 22 | n, err = ParseBase2Bytes("1MB1KB25B") 23 | assert.NoError(t, err) 24 | assert.Equal(t, 1049625, int(n)) 25 | n, err = ParseBase2Bytes("1.5MB") 26 | assert.NoError(t, err) 27 | assert.Equal(t, 1572864, int(n)) 28 | } 29 | 30 | func TestMetricBytesString(t *testing.T) { 31 | assert.Equal(t, MetricBytes(0).String(), "0B") 32 | assert.Equal(t, MetricBytes(1001).String(), "1KB1B") 33 | assert.Equal(t, MetricBytes(1001025).String(), "1MB1KB25B") 34 | } 35 | 36 | func TestParseMetricBytes(t *testing.T) { 37 | n, err := ParseMetricBytes("0B") 38 | assert.NoError(t, err) 39 | assert.Equal(t, 0, int(n)) 40 | n, err = ParseMetricBytes("1KB1B") 41 | assert.NoError(t, err) 42 | assert.Equal(t, 1001, int(n)) 43 | n, err = ParseMetricBytes("1MB1KB25B") 44 | assert.NoError(t, err) 45 | assert.Equal(t, 1001025, int(n)) 46 | n, err = ParseMetricBytes("1.5MB") 47 | assert.NoError(t, err) 48 | assert.Equal(t, 1500000, int(n)) 49 | } 50 | -------------------------------------------------------------------------------- /vendor/github.com/alecthomas/units/doc.go: -------------------------------------------------------------------------------- 1 | // Package units provides helpful unit multipliers and functions for Go. 2 | // 3 | // The goal of this package is to have functionality similar to the time [1] package. 4 | // 5 | // 6 | // [1] http://golang.org/pkg/time/ 7 | // 8 | // It allows for code like this: 9 | // 10 | // n, err := ParseBase2Bytes("1KB") 11 | // // n == 1024 12 | // n = units.Mebibyte * 512 13 | package units 14 | -------------------------------------------------------------------------------- /vendor/github.com/alecthomas/units/si.go: -------------------------------------------------------------------------------- 1 | package units 2 | 3 | // SI units. 4 | type SI int64 5 | 6 | // SI unit multiples. 7 | const ( 8 | Kilo SI = 1000 9 | Mega = Kilo * 1000 10 | Giga = Mega * 1000 11 | Tera = Giga * 1000 12 | Peta = Tera * 1000 13 | Exa = Peta * 1000 14 | ) 15 | 16 | func MakeUnitMap(suffix, shortSuffix string, scale int64) map[string]float64 { 17 | return map[string]float64{ 18 | shortSuffix: 1, 19 | "K" + suffix: float64(scale), 20 | "M" + suffix: float64(scale * scale), 21 | "G" + suffix: float64(scale * scale * scale), 22 | "T" + suffix: float64(scale * scale * scale * scale), 23 | "P" + suffix: float64(scale * scale * scale * scale * scale), 24 | "E" + suffix: float64(scale * scale * scale * scale * scale * scale), 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /vendor/github.com/alecthomas/units/util.go: -------------------------------------------------------------------------------- 1 | package units 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "strings" 7 | ) 8 | 9 | var ( 10 | siUnits = []string{"", "K", "M", "G", "T", "P", "E"} 11 | ) 12 | 13 | func ToString(n int64, scale int64, suffix, baseSuffix string) string { 14 | mn := len(siUnits) 15 | out := make([]string, mn) 16 | for i, m := range siUnits { 17 | if n%scale != 0 || i == 0 && n == 0 { 18 | s := suffix 19 | if i == 0 { 20 | s = baseSuffix 21 | } 22 | out[mn-1-i] = fmt.Sprintf("%d%s%s", n%scale, m, s) 23 | } 24 | n /= scale 25 | if n == 0 { 26 | break 27 | } 28 | } 29 | return strings.Join(out, "") 30 | } 31 | 32 | // Below code ripped straight from http://golang.org/src/pkg/time/format.go?s=33392:33438#L1123 33 | var errLeadingInt = errors.New("units: bad [0-9]*") // never printed 34 | 35 | // leadingInt consumes the leading [0-9]* from s. 36 | func leadingInt(s string) (x int64, rem string, err error) { 37 | i := 0 38 | for ; i < len(s); i++ { 39 | c := s[i] 40 | if c < '0' || c > '9' { 41 | break 42 | } 43 | if x >= (1<<63-10)/10 { 44 | // overflow 45 | return 0, "", errLeadingInt 46 | } 47 | x = x*10 + int64(c) - '0' 48 | } 49 | return x, s[i:], nil 50 | } 51 | 52 | func ParseUnit(s string, unitMap map[string]float64) (int64, error) { 53 | // [-+]?([0-9]*(\.[0-9]*)?[a-z]+)+ 54 | orig := s 55 | f := float64(0) 56 | neg := false 57 | 58 | // Consume [-+]? 59 | if s != "" { 60 | c := s[0] 61 | if c == '-' || c == '+' { 62 | neg = c == '-' 63 | s = s[1:] 64 | } 65 | } 66 | // Special case: if all that is left is "0", this is zero. 67 | if s == "0" { 68 | return 0, nil 69 | } 70 | if s == "" { 71 | return 0, errors.New("units: invalid " + orig) 72 | } 73 | for s != "" { 74 | g := float64(0) // this element of the sequence 75 | 76 | var x int64 77 | var err error 78 | 79 | // The next character must be [0-9.] 80 | if !(s[0] == '.' || ('0' <= s[0] && s[0] <= '9')) { 81 | return 0, errors.New("units: invalid " + orig) 82 | } 83 | // Consume [0-9]* 84 | pl := len(s) 85 | x, s, err = leadingInt(s) 86 | if err != nil { 87 | return 0, errors.New("units: invalid " + orig) 88 | } 89 | g = float64(x) 90 | pre := pl != len(s) // whether we consumed anything before a period 91 | 92 | // Consume (\.[0-9]*)? 93 | post := false 94 | if s != "" && s[0] == '.' { 95 | s = s[1:] 96 | pl := len(s) 97 | x, s, err = leadingInt(s) 98 | if err != nil { 99 | return 0, errors.New("units: invalid " + orig) 100 | } 101 | scale := 1.0 102 | for n := pl - len(s); n > 0; n-- { 103 | scale *= 10 104 | } 105 | g += float64(x) / scale 106 | post = pl != len(s) 107 | } 108 | if !pre && !post { 109 | // no digits (e.g. ".s" or "-.s") 110 | return 0, errors.New("units: invalid " + orig) 111 | } 112 | 113 | // Consume unit. 114 | i := 0 115 | for ; i < len(s); i++ { 116 | c := s[i] 117 | if c == '.' || ('0' <= c && c <= '9') { 118 | break 119 | } 120 | } 121 | u := s[:i] 122 | s = s[i:] 123 | unit, ok := unitMap[u] 124 | if !ok { 125 | return 0, errors.New("units: unknown unit " + u + " in " + orig) 126 | } 127 | 128 | f += g * unit 129 | } 130 | 131 | if neg { 132 | f = -f 133 | } 134 | if f < float64(-1<<63) || f > float64(1<<63-1) { 135 | return 0, errors.New("units: overflow parsing unit") 136 | } 137 | return int64(f), nil 138 | } 139 | -------------------------------------------------------------------------------- /vendor/gopkg.in/alecthomas/kingpin.v2/.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: go 3 | install: go get -t -v ./... 4 | go: 1.2 5 | -------------------------------------------------------------------------------- /vendor/gopkg.in/alecthomas/kingpin.v2/COPYING: -------------------------------------------------------------------------------- 1 | Copyright (C) 2014 Alec Thomas 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /vendor/gopkg.in/alecthomas/kingpin.v2/_examples/chat1/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "gopkg.in/alecthomas/kingpin.v2" 7 | ) 8 | 9 | var ( 10 | debug = kingpin.Flag("debug", "Enable debug mode.").Bool() 11 | timeout = kingpin.Flag("timeout", "Timeout waiting for ping.").Default("5s").OverrideDefaultFromEnvar("PING_TIMEOUT").Short('t').Duration() 12 | ip = kingpin.Arg("ip", "IP address to ping.").Required().IP() 13 | count = kingpin.Arg("count", "Number of packets to send").Int() 14 | ) 15 | 16 | func main() { 17 | kingpin.Version("0.0.1") 18 | kingpin.Parse() 19 | fmt.Printf("Would ping: %s with timeout %s and count %d", *ip, *timeout, *count) 20 | } 21 | -------------------------------------------------------------------------------- /vendor/gopkg.in/alecthomas/kingpin.v2/_examples/chat2/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | "strings" 6 | 7 | "gopkg.in/alecthomas/kingpin.v2" 8 | ) 9 | 10 | var ( 11 | app = kingpin.New("chat", "A command-line chat application.") 12 | debug = app.Flag("debug", "Enable debug mode.").Bool() 13 | serverIP = app.Flag("server", "Server address.").Default("127.0.0.1").IP() 14 | 15 | register = app.Command("register", "Register a new user.") 16 | registerNick = register.Arg("nick", "Nickname for user.").Required().String() 17 | registerName = register.Arg("name", "Name of user.").Required().String() 18 | 19 | post = app.Command("post", "Post a message to a channel.") 20 | postImage = post.Flag("image", "Image to post.").File() 21 | postChannel = post.Arg("channel", "Channel to post to.").Required().String() 22 | postText = post.Arg("text", "Text to post.").Strings() 23 | ) 24 | 25 | func main() { 26 | switch kingpin.MustParse(app.Parse(os.Args[1:])) { 27 | // Register user 28 | case register.FullCommand(): 29 | println(*registerNick) 30 | 31 | // Post message 32 | case post.FullCommand(): 33 | if *postImage != nil { 34 | } 35 | text := strings.Join(*postText, " ") 36 | println("Post:", text) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /vendor/gopkg.in/alecthomas/kingpin.v2/_examples/completion/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "github.com/alecthomas/kingpin" 8 | ) 9 | 10 | func listHosts() []string { 11 | // Provide a dynamic list of hosts from a hosts file or otherwise 12 | // for bash completion. In this example we simply return static slice. 13 | 14 | // You could use this functionality to reach into a hosts file to provide 15 | // completion for a list of known hosts. 16 | return []string{"sshhost.example", "webhost.example", "ftphost.example"} 17 | } 18 | 19 | type NetcatCommand struct { 20 | hostName string 21 | port int 22 | format string 23 | } 24 | 25 | func (n *NetcatCommand) run(c *kingpin.ParseContext) error { 26 | fmt.Printf("Would have run netcat to hostname %v, port %d, and output format %v\n", n.hostName, n.port, n.format) 27 | return nil 28 | } 29 | 30 | func configureNetcatCommand(app *kingpin.Application) { 31 | c := &NetcatCommand{} 32 | nc := app.Command("nc", "Connect to a Host").Action(c.run) 33 | nc.Flag("nop-flag", "Example of a flag with no options").Bool() 34 | 35 | // You can provide hint options using a function to generate them 36 | nc.Flag("host", "Provide a hostname to nc"). 37 | Required(). 38 | HintAction(listHosts). 39 | StringVar(&c.hostName) 40 | 41 | // You can provide hint options statically 42 | nc.Flag("port", "Provide a port to connect to"). 43 | Required(). 44 | HintOptions("80", "443", "8080"). 45 | IntVar(&c.port) 46 | 47 | // Enum/EnumVar options will be turned into completion options automatically 48 | nc.Flag("format", "Define the output format"). 49 | Default("raw"). 50 | EnumVar(&c.format, "raw", "json") 51 | 52 | // You can combine HintOptions with HintAction too 53 | nc.Flag("host-with-multi", "Define a hostname"). 54 | HintAction(listHosts). 55 | HintOptions("myhost.com"). 56 | String() 57 | 58 | // And combine with themselves 59 | nc.Flag("host-with-multi-options", "Define a hostname"). 60 | HintOptions("myhost.com"). 61 | HintOptions("myhost2.com"). 62 | String() 63 | 64 | // If you specify HintOptions/HintActions for Enum/EnumVar, the options 65 | // provided for Enum/EnumVar will be overridden. 66 | nc.Flag("format-with-override-1", "Define a format"). 67 | HintAction(listHosts). 68 | Enum("option1", "option2") 69 | 70 | nc.Flag("format-with-override-2", "Define a format"). 71 | HintOptions("myhost.com", "myhost2.com"). 72 | Enum("option1", "option2") 73 | } 74 | 75 | func addSubCommand(app *kingpin.Application, name string, description string) { 76 | c := app.Command(name, description).Action(func(c *kingpin.ParseContext) error { 77 | fmt.Printf("Would have run command %s.\n", name) 78 | return nil 79 | }) 80 | c.Flag("nop-flag", "Example of a flag with no options").Bool() 81 | } 82 | 83 | func main() { 84 | app := kingpin.New("completion", "My application with bash completion.") 85 | app.Flag("flag-1", "").String() 86 | app.Flag("flag-2", "").HintOptions("opt1", "opt2").String() 87 | 88 | configureNetcatCommand(app) 89 | 90 | // Add some additional top level commands 91 | addSubCommand(app, "ls", "Additional top level command to show command completion") 92 | addSubCommand(app, "ping", "Additional top level command to show command completion") 93 | addSubCommand(app, "nmap", "Additional top level command to show command completion") 94 | 95 | kingpin.MustParse(app.Parse(os.Args[1:])) 96 | } 97 | -------------------------------------------------------------------------------- /vendor/gopkg.in/alecthomas/kingpin.v2/_examples/curl/main.go: -------------------------------------------------------------------------------- 1 | // A curl-like HTTP command-line client. 2 | package main 3 | 4 | import ( 5 | "errors" 6 | "fmt" 7 | "io" 8 | "net/http" 9 | "os" 10 | "strings" 11 | 12 | "gopkg.in/alecthomas/kingpin.v2" 13 | ) 14 | 15 | var ( 16 | timeout = kingpin.Flag("timeout", "Set connection timeout.").Short('t').Default("5s").Duration() 17 | headers = HTTPHeader(kingpin.Flag("headers", "Add HTTP headers to the request.").Short('H').PlaceHolder("HEADER=VALUE")) 18 | 19 | get = kingpin.Command("get", "GET a resource.").Default() 20 | getFlag = get.Flag("test", "Test flag").Bool() 21 | getURL = get.Command("url", "Retrieve a URL.").Default() 22 | getURLURL = getURL.Arg("url", "URL to GET.").Required().URL() 23 | getFile = get.Command("file", "Retrieve a file.") 24 | getFileFile = getFile.Arg("file", "File to retrieve.").Required().ExistingFile() 25 | 26 | post = kingpin.Command("post", "POST a resource.") 27 | postData = post.Flag("data", "Key-value data to POST").Short('d').PlaceHolder("KEY:VALUE").StringMap() 28 | postBinaryFile = post.Flag("data-binary", "File with binary data to POST.").File() 29 | postURL = post.Arg("url", "URL to POST to.").Required().URL() 30 | ) 31 | 32 | type HTTPHeaderValue http.Header 33 | 34 | func (h HTTPHeaderValue) Set(value string) error { 35 | parts := strings.SplitN(value, "=", 2) 36 | if len(parts) != 2 { 37 | return fmt.Errorf("expected HEADER=VALUE got '%s'", value) 38 | } 39 | (http.Header)(h).Add(parts[0], parts[1]) 40 | return nil 41 | } 42 | 43 | func (h HTTPHeaderValue) String() string { 44 | return "" 45 | } 46 | 47 | func HTTPHeader(s kingpin.Settings) (target *http.Header) { 48 | target = &http.Header{} 49 | s.SetValue((*HTTPHeaderValue)(target)) 50 | return 51 | } 52 | 53 | func applyRequest(req *http.Request) error { 54 | req.Header = *headers 55 | resp, err := http.DefaultClient.Do(req) 56 | if err != nil { 57 | return err 58 | } 59 | defer resp.Body.Close() 60 | if resp.StatusCode < 200 || resp.StatusCode > 299 { 61 | return fmt.Errorf("HTTP request failed: %s", resp.Status) 62 | } 63 | _, err = io.Copy(os.Stdout, resp.Body) 64 | return err 65 | } 66 | 67 | func apply(method string, url string) error { 68 | req, err := http.NewRequest(method, url, nil) 69 | if err != nil { 70 | return err 71 | } 72 | return applyRequest(req) 73 | } 74 | 75 | func applyPOST() error { 76 | req, err := http.NewRequest("POST", (*postURL).String(), nil) 77 | if err != nil { 78 | return err 79 | } 80 | if len(*postData) > 0 { 81 | for key, value := range *postData { 82 | req.Form.Set(key, value) 83 | } 84 | } else if postBinaryFile != nil { 85 | if headers.Get("Content-Type") != "" { 86 | headers.Set("Content-Type", "application/octet-stream") 87 | } 88 | req.Body = *postBinaryFile 89 | } else { 90 | return errors.New("--data or --data-binary must be provided to POST") 91 | } 92 | return applyRequest(req) 93 | } 94 | 95 | func main() { 96 | kingpin.UsageTemplate(kingpin.CompactUsageTemplate).Version("1.0").Author("Alec Thomas") 97 | kingpin.CommandLine.Help = "An example implementation of curl." 98 | switch kingpin.Parse() { 99 | case "get url": 100 | kingpin.FatalIfError(apply("GET", (*getURLURL).String()), "GET failed") 101 | 102 | case "post": 103 | kingpin.FatalIfError(applyPOST(), "POST failed") 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /vendor/gopkg.in/alecthomas/kingpin.v2/_examples/modular/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "gopkg.in/alecthomas/kingpin.v2" 8 | ) 9 | 10 | // Context for "ls" command 11 | type LsCommand struct { 12 | All bool 13 | } 14 | 15 | func (l *LsCommand) run(c *kingpin.ParseContext) error { 16 | fmt.Printf("all=%v\n", l.All) 17 | return nil 18 | } 19 | 20 | func configureLsCommand(app *kingpin.Application) { 21 | c := &LsCommand{} 22 | ls := app.Command("ls", "List files.").Action(c.run) 23 | ls.Flag("all", "List all files.").Short('a').BoolVar(&c.All) 24 | } 25 | 26 | func main() { 27 | app := kingpin.New("modular", "My modular application.") 28 | configureLsCommand(app) 29 | kingpin.MustParse(app.Parse(os.Args[1:])) 30 | } 31 | -------------------------------------------------------------------------------- /vendor/gopkg.in/alecthomas/kingpin.v2/_examples/ping/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "gopkg.in/alecthomas/kingpin.v2" 7 | ) 8 | 9 | var ( 10 | debug = kingpin.Flag("debug", "Enable debug mode.").Bool() 11 | timeout = kingpin.Flag("timeout", "Timeout waiting for ping.").OverrideDefaultFromEnvar("PING_TIMEOUT").Required().Short('t').Duration() 12 | ip = kingpin.Arg("ip", "IP address to ping.").Required().IP() 13 | count = kingpin.Arg("count", "Number of packets to send").Int() 14 | ) 15 | 16 | func main() { 17 | kingpin.Version("0.0.1") 18 | kingpin.Parse() 19 | fmt.Printf("Would ping: %s with timeout %s and count %d", *ip, *timeout, *count) 20 | } 21 | -------------------------------------------------------------------------------- /vendor/gopkg.in/alecthomas/kingpin.v2/actions.go: -------------------------------------------------------------------------------- 1 | package kingpin 2 | 3 | // Action callback executed at various stages after all values are populated. 4 | // The application, commands, arguments and flags all have corresponding 5 | // actions. 6 | type Action func(*ParseContext) error 7 | 8 | type actionMixin struct { 9 | actions []Action 10 | preActions []Action 11 | } 12 | 13 | type actionApplier interface { 14 | applyActions(*ParseContext) error 15 | applyPreActions(*ParseContext) error 16 | } 17 | 18 | func (a *actionMixin) addAction(action Action) { 19 | a.actions = append(a.actions, action) 20 | } 21 | 22 | func (a *actionMixin) addPreAction(action Action) { 23 | a.preActions = append(a.preActions, action) 24 | } 25 | 26 | func (a *actionMixin) applyActions(context *ParseContext) error { 27 | for _, action := range a.actions { 28 | if err := action(context); err != nil { 29 | return err 30 | } 31 | } 32 | return nil 33 | } 34 | 35 | func (a *actionMixin) applyPreActions(context *ParseContext) error { 36 | for _, preAction := range a.preActions { 37 | if err := preAction(context); err != nil { 38 | return err 39 | } 40 | } 41 | return nil 42 | } 43 | -------------------------------------------------------------------------------- /vendor/gopkg.in/alecthomas/kingpin.v2/args.go: -------------------------------------------------------------------------------- 1 | package kingpin 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type argGroup struct { 8 | args []*ArgClause 9 | } 10 | 11 | func newArgGroup() *argGroup { 12 | return &argGroup{} 13 | } 14 | 15 | func (a *argGroup) have() bool { 16 | return len(a.args) > 0 17 | } 18 | 19 | // GetArg gets an argument definition. 20 | // 21 | // This allows existing arguments to be modified after definition but before parsing. Useful for 22 | // modular applications. 23 | func (a *argGroup) GetArg(name string) *ArgClause { 24 | for _, arg := range a.args { 25 | if arg.name == name { 26 | return arg 27 | } 28 | } 29 | return nil 30 | } 31 | 32 | func (a *argGroup) Arg(name, help string) *ArgClause { 33 | arg := newArg(name, help) 34 | a.args = append(a.args, arg) 35 | return arg 36 | } 37 | 38 | func (a *argGroup) init() error { 39 | required := 0 40 | seen := map[string]struct{}{} 41 | previousArgMustBeLast := false 42 | for i, arg := range a.args { 43 | if previousArgMustBeLast { 44 | return fmt.Errorf("Args() can't be followed by another argument '%s'", arg.name) 45 | } 46 | if arg.consumesRemainder() { 47 | previousArgMustBeLast = true 48 | } 49 | if _, ok := seen[arg.name]; ok { 50 | return fmt.Errorf("duplicate argument '%s'", arg.name) 51 | } 52 | seen[arg.name] = struct{}{} 53 | if arg.required && required != i { 54 | return fmt.Errorf("required arguments found after non-required") 55 | } 56 | if arg.required { 57 | required++ 58 | } 59 | if err := arg.init(); err != nil { 60 | return err 61 | } 62 | } 63 | return nil 64 | } 65 | 66 | type ArgClause struct { 67 | actionMixin 68 | parserMixin 69 | completionsMixin 70 | envarMixin 71 | name string 72 | help string 73 | defaultValues []string 74 | required bool 75 | } 76 | 77 | func newArg(name, help string) *ArgClause { 78 | a := &ArgClause{ 79 | name: name, 80 | help: help, 81 | } 82 | return a 83 | } 84 | 85 | func (a *ArgClause) setDefault() error { 86 | if a.HasEnvarValue() { 87 | if v, ok := a.value.(remainderArg); !ok || !v.IsCumulative() { 88 | // Use the value as-is 89 | return a.value.Set(a.GetEnvarValue()) 90 | } 91 | for _, value := range a.GetSplitEnvarValue() { 92 | if err := a.value.Set(value); err != nil { 93 | return err 94 | } 95 | } 96 | return nil 97 | } 98 | 99 | if len(a.defaultValues) > 0 { 100 | for _, defaultValue := range a.defaultValues { 101 | if err := a.value.Set(defaultValue); err != nil { 102 | return err 103 | } 104 | } 105 | return nil 106 | } 107 | 108 | return nil 109 | } 110 | 111 | func (a *ArgClause) needsValue() bool { 112 | haveDefault := len(a.defaultValues) > 0 113 | return a.required && !(haveDefault || a.HasEnvarValue()) 114 | } 115 | 116 | func (a *ArgClause) consumesRemainder() bool { 117 | if r, ok := a.value.(remainderArg); ok { 118 | return r.IsCumulative() 119 | } 120 | return false 121 | } 122 | 123 | // Required arguments must be input by the user. They can not have a Default() value provided. 124 | func (a *ArgClause) Required() *ArgClause { 125 | a.required = true 126 | return a 127 | } 128 | 129 | // Default values for this argument. They *must* be parseable by the value of the argument. 130 | func (a *ArgClause) Default(values ...string) *ArgClause { 131 | a.defaultValues = values 132 | return a 133 | } 134 | 135 | // Envar overrides the default value(s) for a flag from an environment variable, 136 | // if it is set. Several default values can be provided by using new lines to 137 | // separate them. 138 | func (a *ArgClause) Envar(name string) *ArgClause { 139 | a.envar = name 140 | a.noEnvar = false 141 | return a 142 | } 143 | 144 | // NoEnvar forces environment variable defaults to be disabled for this flag. 145 | // Most useful in conjunction with app.DefaultEnvars(). 146 | func (a *ArgClause) NoEnvar() *ArgClause { 147 | a.envar = "" 148 | a.noEnvar = true 149 | return a 150 | } 151 | 152 | func (a *ArgClause) Action(action Action) *ArgClause { 153 | a.addAction(action) 154 | return a 155 | } 156 | 157 | func (a *ArgClause) PreAction(action Action) *ArgClause { 158 | a.addPreAction(action) 159 | return a 160 | } 161 | 162 | // HintAction registers a HintAction (function) for the arg to provide completions 163 | func (a *ArgClause) HintAction(action HintAction) *ArgClause { 164 | a.addHintAction(action) 165 | return a 166 | } 167 | 168 | // HintOptions registers any number of options for the flag to provide completions 169 | func (a *ArgClause) HintOptions(options ...string) *ArgClause { 170 | a.addHintAction(func() []string { 171 | return options 172 | }) 173 | return a 174 | } 175 | 176 | func (a *ArgClause) init() error { 177 | if a.required && len(a.defaultValues) > 0 { 178 | return fmt.Errorf("required argument '%s' with unusable default value", a.name) 179 | } 180 | if a.value == nil { 181 | return fmt.Errorf("no parser defined for arg '%s'", a.name) 182 | } 183 | return nil 184 | } 185 | -------------------------------------------------------------------------------- /vendor/gopkg.in/alecthomas/kingpin.v2/args_test.go: -------------------------------------------------------------------------------- 1 | package kingpin 2 | 3 | import ( 4 | "io/ioutil" 5 | "os" 6 | "testing" 7 | 8 | "github.com/alecthomas/assert" 9 | ) 10 | 11 | func TestArgRemainder(t *testing.T) { 12 | app := New("test", "") 13 | v := app.Arg("test", "").Strings() 14 | args := []string{"hello", "world"} 15 | _, err := app.Parse(args) 16 | assert.NoError(t, err) 17 | assert.Equal(t, args, *v) 18 | } 19 | 20 | func TestArgRemainderErrorsWhenNotLast(t *testing.T) { 21 | a := newArgGroup() 22 | a.Arg("test", "").Strings() 23 | a.Arg("test2", "").String() 24 | assert.Error(t, a.init()) 25 | } 26 | 27 | func TestArgMultipleRequired(t *testing.T) { 28 | terminated := false 29 | app := New("test", "") 30 | app.Version("0.0.0").Writer(ioutil.Discard) 31 | app.Arg("a", "").Required().String() 32 | app.Arg("b", "").Required().String() 33 | app.Terminate(func(int) { terminated = true }) 34 | 35 | _, err := app.Parse([]string{}) 36 | assert.Error(t, err) 37 | _, err = app.Parse([]string{"A"}) 38 | assert.Error(t, err) 39 | _, err = app.Parse([]string{"A", "B"}) 40 | assert.NoError(t, err) 41 | _, err = app.Parse([]string{"--version"}) 42 | assert.True(t, terminated) 43 | } 44 | 45 | func TestInvalidArgsDefaultCanBeOverridden(t *testing.T) { 46 | app := New("test", "") 47 | app.Arg("a", "").Default("invalid").Bool() 48 | _, err := app.Parse([]string{}) 49 | assert.Error(t, err) 50 | } 51 | 52 | func TestArgMultipleValuesDefault(t *testing.T) { 53 | app := New("test", "") 54 | a := app.Arg("a", "").Default("default1", "default2").Strings() 55 | _, err := app.Parse([]string{}) 56 | assert.NoError(t, err) 57 | assert.Equal(t, []string{"default1", "default2"}, *a) 58 | } 59 | 60 | func TestRequiredArgWithEnvarMissingErrors(t *testing.T) { 61 | app := newTestApp() 62 | app.Arg("t", "").Envar("TEST_ARG_ENVAR").Required().Int() 63 | _, err := app.Parse([]string{}) 64 | assert.Error(t, err) 65 | } 66 | 67 | func TestArgRequiredWithEnvar(t *testing.T) { 68 | os.Setenv("TEST_ARG_ENVAR", "123") 69 | app := newTestApp() 70 | flag := app.Arg("t", "").Envar("TEST_ARG_ENVAR").Required().Int() 71 | _, err := app.Parse([]string{}) 72 | assert.NoError(t, err) 73 | assert.Equal(t, 123, *flag) 74 | } 75 | 76 | func TestSubcommandArgRequiredWithEnvar(t *testing.T) { 77 | os.Setenv("TEST_ARG_ENVAR", "123") 78 | app := newTestApp() 79 | cmd := app.Command("command", "") 80 | flag := cmd.Arg("t", "").Envar("TEST_ARG_ENVAR").Required().Int() 81 | _, err := app.Parse([]string{"command"}) 82 | assert.NoError(t, err) 83 | assert.Equal(t, 123, *flag) 84 | } 85 | -------------------------------------------------------------------------------- /vendor/gopkg.in/alecthomas/kingpin.v2/cmd.go: -------------------------------------------------------------------------------- 1 | package kingpin 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | type cmdMixin struct { 9 | *flagGroup 10 | *argGroup 11 | *cmdGroup 12 | actionMixin 13 | } 14 | 15 | // CmdCompletion returns completion options for arguments, if that's where 16 | // parsing left off, or commands if there aren't any unsatisfied args. 17 | func (c *cmdMixin) CmdCompletion(context *ParseContext) []string { 18 | var options []string 19 | 20 | // Count args already satisfied - we won't complete those, and add any 21 | // default commands' alternatives, since they weren't listed explicitly 22 | // and the user may want to explicitly list something else. 23 | argsSatisfied := 0 24 | for _, el := range context.Elements { 25 | switch clause := el.Clause.(type) { 26 | case *ArgClause: 27 | if el.Value != nil && *el.Value != "" { 28 | argsSatisfied++ 29 | } 30 | case *CmdClause: 31 | options = append(options, clause.completionAlts...) 32 | default: 33 | } 34 | } 35 | 36 | if argsSatisfied < len(c.argGroup.args) { 37 | // Since not all args have been satisfied, show options for the current one 38 | options = append(options, c.argGroup.args[argsSatisfied].resolveCompletions()...) 39 | } else { 40 | // If all args are satisfied, then go back to completing commands 41 | for _, cmd := range c.cmdGroup.commandOrder { 42 | if !cmd.hidden { 43 | options = append(options, cmd.name) 44 | } 45 | } 46 | } 47 | 48 | return options 49 | } 50 | 51 | func (c *cmdMixin) FlagCompletion(flagName string, flagValue string) (choices []string, flagMatch bool, optionMatch bool) { 52 | // Check if flagName matches a known flag. 53 | // If it does, show the options for the flag 54 | // Otherwise, show all flags 55 | 56 | options := []string{} 57 | 58 | for _, flag := range c.flagGroup.flagOrder { 59 | // Loop through each flag and determine if a match exists 60 | if flag.name == flagName { 61 | // User typed entire flag. Need to look for flag options. 62 | options = flag.resolveCompletions() 63 | if len(options) == 0 { 64 | // No Options to Choose From, Assume Match. 65 | return options, true, true 66 | } 67 | 68 | // Loop options to find if the user specified value matches 69 | isPrefix := false 70 | matched := false 71 | 72 | for _, opt := range options { 73 | if flagValue == opt { 74 | matched = true 75 | } else if strings.HasPrefix(opt, flagValue) { 76 | isPrefix = true 77 | } 78 | } 79 | 80 | // Matched Flag Directly 81 | // Flag Value Not Prefixed, and Matched Directly 82 | return options, true, !isPrefix && matched 83 | } 84 | 85 | if !flag.hidden { 86 | options = append(options, "--"+flag.name) 87 | } 88 | } 89 | // No Flag directly matched. 90 | return options, false, false 91 | 92 | } 93 | 94 | type cmdGroup struct { 95 | app *Application 96 | parent *CmdClause 97 | commands map[string]*CmdClause 98 | commandOrder []*CmdClause 99 | } 100 | 101 | func (c *cmdGroup) defaultSubcommand() *CmdClause { 102 | for _, cmd := range c.commandOrder { 103 | if cmd.isDefault { 104 | return cmd 105 | } 106 | } 107 | return nil 108 | } 109 | 110 | func (c *cmdGroup) cmdNames() []string { 111 | names := make([]string, 0, len(c.commandOrder)) 112 | for _, cmd := range c.commandOrder { 113 | names = append(names, cmd.name) 114 | } 115 | return names 116 | } 117 | 118 | // GetArg gets a command definition. 119 | // 120 | // This allows existing commands to be modified after definition but before parsing. Useful for 121 | // modular applications. 122 | func (c *cmdGroup) GetCommand(name string) *CmdClause { 123 | return c.commands[name] 124 | } 125 | 126 | func newCmdGroup(app *Application) *cmdGroup { 127 | return &cmdGroup{ 128 | app: app, 129 | commands: make(map[string]*CmdClause), 130 | } 131 | } 132 | 133 | func (c *cmdGroup) flattenedCommands() (out []*CmdClause) { 134 | for _, cmd := range c.commandOrder { 135 | if len(cmd.commands) == 0 { 136 | out = append(out, cmd) 137 | } 138 | out = append(out, cmd.flattenedCommands()...) 139 | } 140 | return 141 | } 142 | 143 | func (c *cmdGroup) addCommand(name, help string) *CmdClause { 144 | cmd := newCommand(c.app, name, help) 145 | c.commands[name] = cmd 146 | c.commandOrder = append(c.commandOrder, cmd) 147 | return cmd 148 | } 149 | 150 | func (c *cmdGroup) init() error { 151 | seen := map[string]bool{} 152 | if c.defaultSubcommand() != nil && !c.have() { 153 | return fmt.Errorf("default subcommand %q provided but no subcommands defined", c.defaultSubcommand().name) 154 | } 155 | defaults := []string{} 156 | for _, cmd := range c.commandOrder { 157 | if cmd.isDefault { 158 | defaults = append(defaults, cmd.name) 159 | } 160 | if seen[cmd.name] { 161 | return fmt.Errorf("duplicate command %q", cmd.name) 162 | } 163 | seen[cmd.name] = true 164 | for _, alias := range cmd.aliases { 165 | if seen[alias] { 166 | return fmt.Errorf("alias duplicates existing command %q", alias) 167 | } 168 | c.commands[alias] = cmd 169 | } 170 | if err := cmd.init(); err != nil { 171 | return err 172 | } 173 | } 174 | if len(defaults) > 1 { 175 | return fmt.Errorf("more than one default subcommand exists: %s", strings.Join(defaults, ", ")) 176 | } 177 | return nil 178 | } 179 | 180 | func (c *cmdGroup) have() bool { 181 | return len(c.commands) > 0 182 | } 183 | 184 | type CmdClauseValidator func(*CmdClause) error 185 | 186 | // A CmdClause is a single top-level command. It encapsulates a set of flags 187 | // and either subcommands or positional arguments. 188 | type CmdClause struct { 189 | cmdMixin 190 | app *Application 191 | name string 192 | aliases []string 193 | help string 194 | isDefault bool 195 | validator CmdClauseValidator 196 | hidden bool 197 | completionAlts []string 198 | } 199 | 200 | func newCommand(app *Application, name, help string) *CmdClause { 201 | c := &CmdClause{ 202 | app: app, 203 | name: name, 204 | help: help, 205 | } 206 | c.flagGroup = newFlagGroup() 207 | c.argGroup = newArgGroup() 208 | c.cmdGroup = newCmdGroup(app) 209 | return c 210 | } 211 | 212 | // Add an Alias for this command. 213 | func (c *CmdClause) Alias(name string) *CmdClause { 214 | c.aliases = append(c.aliases, name) 215 | return c 216 | } 217 | 218 | // Validate sets a validation function to run when parsing. 219 | func (c *CmdClause) Validate(validator CmdClauseValidator) *CmdClause { 220 | c.validator = validator 221 | return c 222 | } 223 | 224 | func (c *CmdClause) FullCommand() string { 225 | out := []string{c.name} 226 | for p := c.parent; p != nil; p = p.parent { 227 | out = append([]string{p.name}, out...) 228 | } 229 | return strings.Join(out, " ") 230 | } 231 | 232 | // Command adds a new sub-command. 233 | func (c *CmdClause) Command(name, help string) *CmdClause { 234 | cmd := c.addCommand(name, help) 235 | cmd.parent = c 236 | return cmd 237 | } 238 | 239 | // Default makes this command the default if commands don't match. 240 | func (c *CmdClause) Default() *CmdClause { 241 | c.isDefault = true 242 | return c 243 | } 244 | 245 | func (c *CmdClause) Action(action Action) *CmdClause { 246 | c.addAction(action) 247 | return c 248 | } 249 | 250 | func (c *CmdClause) PreAction(action Action) *CmdClause { 251 | c.addPreAction(action) 252 | return c 253 | } 254 | 255 | func (c *CmdClause) init() error { 256 | if err := c.flagGroup.init(c.app.defaultEnvarPrefix()); err != nil { 257 | return err 258 | } 259 | if c.argGroup.have() && c.cmdGroup.have() { 260 | return fmt.Errorf("can't mix Arg()s with Command()s") 261 | } 262 | if err := c.argGroup.init(); err != nil { 263 | return err 264 | } 265 | if err := c.cmdGroup.init(); err != nil { 266 | return err 267 | } 268 | return nil 269 | } 270 | 271 | func (c *CmdClause) Hidden() *CmdClause { 272 | c.hidden = true 273 | return c 274 | } 275 | -------------------------------------------------------------------------------- /vendor/gopkg.in/alecthomas/kingpin.v2/cmd/genvalues/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "os" 6 | "os/exec" 7 | "strings" 8 | 9 | "github.com/alecthomas/template" 10 | ) 11 | 12 | const ( 13 | tmpl = `package kingpin 14 | 15 | // This file is autogenerated by "go generate .". Do not modify. 16 | 17 | {{range .}} 18 | {{if not .NoValueParser}} 19 | // -- {{.Type}} Value 20 | type {{.|ValueName}} struct { v *{{.Type}} } 21 | 22 | func new{{.|Name}}Value(p *{{.Type}}) *{{.|ValueName}} { 23 | return &{{.|ValueName}}{p} 24 | } 25 | 26 | func (f *{{.|ValueName}}) Set(s string) error { 27 | v, err := {{.Parser}} 28 | if err == nil { 29 | *f.v = ({{.Type}})(v) 30 | } 31 | return err 32 | } 33 | 34 | func (f *{{.|ValueName}}) Get() interface{} { return ({{.Type}})(*f.v) } 35 | 36 | func (f *{{.|ValueName}}) String() string { return {{.|Format}} } 37 | 38 | {{if .Help}} 39 | // {{.Help}} 40 | {{else}}\ 41 | // {{.|Name}} parses the next command-line value as {{.Type}}. 42 | {{end}}\ 43 | func (p *parserMixin) {{.|Name}}() (target *{{.Type}}) { 44 | target = new({{.Type}}) 45 | p.{{.|Name}}Var(target) 46 | return 47 | } 48 | 49 | func (p *parserMixin) {{.|Name}}Var(target *{{.Type}}) { 50 | p.SetValue(new{{.|Name}}Value(target)) 51 | } 52 | 53 | {{end}} 54 | // {{.|Plural}} accumulates {{.Type}} values into a slice. 55 | func (p *parserMixin) {{.|Plural}}() (target *[]{{.Type}}) { 56 | target = new([]{{.Type}}) 57 | p.{{.|Plural}}Var(target) 58 | return 59 | } 60 | 61 | func (p *parserMixin) {{.|Plural}}Var(target *[]{{.Type}}) { 62 | p.SetValue(newAccumulator(target, func(v interface{}) Value { 63 | return new{{.|Name}}Value(v.(*{{.Type}})) 64 | })) 65 | } 66 | 67 | {{end}} 68 | ` 69 | ) 70 | 71 | type Value struct { 72 | Name string `json:"name"` 73 | NoValueParser bool `json:"no_value_parser"` 74 | Type string `json:"type"` 75 | Parser string `json:"parser"` 76 | Format string `json:"format"` 77 | Plural string `json:"plural"` 78 | Help string `json:"help"` 79 | } 80 | 81 | func fatalIfError(err error) { 82 | if err != nil { 83 | panic(err) 84 | } 85 | } 86 | 87 | func main() { 88 | r, err := os.Open("values.json") 89 | fatalIfError(err) 90 | defer r.Close() 91 | 92 | v := []Value{} 93 | err = json.NewDecoder(r).Decode(&v) 94 | fatalIfError(err) 95 | 96 | valueName := func(v *Value) string { 97 | if v.Name != "" { 98 | return v.Name 99 | } 100 | return strings.Title(v.Type) 101 | } 102 | 103 | t, err := template.New("genvalues").Funcs(template.FuncMap{ 104 | "Lower": strings.ToLower, 105 | "Format": func(v *Value) string { 106 | if v.Format != "" { 107 | return v.Format 108 | } 109 | return "fmt.Sprintf(\"%v\", *f.v)" 110 | }, 111 | "ValueName": func(v *Value) string { 112 | name := valueName(v) 113 | return strings.ToLower(name[0:1]) + name[1:] + "Value" 114 | }, 115 | "Name": valueName, 116 | "Plural": func(v *Value) string { 117 | if v.Plural != "" { 118 | return v.Plural 119 | } 120 | return valueName(v) + "List" 121 | }, 122 | }).Parse(tmpl) 123 | fatalIfError(err) 124 | 125 | w, err := os.Create("values_generated.go") 126 | fatalIfError(err) 127 | defer w.Close() 128 | 129 | err = t.Execute(w, v) 130 | fatalIfError(err) 131 | 132 | err = exec.Command("goimports", "-w", "values_generated.go").Run() 133 | fatalIfError(err) 134 | } 135 | -------------------------------------------------------------------------------- /vendor/gopkg.in/alecthomas/kingpin.v2/cmd_test.go: -------------------------------------------------------------------------------- 1 | package kingpin 2 | 3 | import ( 4 | "sort" 5 | "strings" 6 | 7 | "github.com/alecthomas/assert" 8 | 9 | "testing" 10 | ) 11 | 12 | func parseAndExecute(app *Application, context *ParseContext) (string, error) { 13 | if err := parse(context, app); err != nil { 14 | return "", err 15 | } 16 | 17 | selected, err := app.setValues(context) 18 | if err != nil { 19 | return "", err 20 | } 21 | 22 | return app.execute(context, selected) 23 | } 24 | 25 | func complete(t *testing.T, app *Application, args ...string) []string { 26 | context, err := app.ParseContext(args) 27 | assert.NoError(t, err) 28 | if err != nil { 29 | return nil 30 | } 31 | 32 | completions := app.completionOptions(context) 33 | sort.Strings(completions) 34 | 35 | return completions 36 | } 37 | 38 | func TestNestedCommands(t *testing.T) { 39 | app := New("app", "") 40 | sub1 := app.Command("sub1", "") 41 | sub1.Flag("sub1", "") 42 | subsub1 := sub1.Command("sub1sub1", "") 43 | subsub1.Command("sub1sub1end", "") 44 | 45 | sub2 := app.Command("sub2", "") 46 | sub2.Flag("sub2", "") 47 | sub2.Command("sub2sub1", "") 48 | 49 | context := tokenize([]string{"sub1", "sub1sub1", "sub1sub1end"}, false) 50 | selected, err := parseAndExecute(app, context) 51 | assert.NoError(t, err) 52 | assert.True(t, context.EOL()) 53 | assert.Equal(t, "sub1 sub1sub1 sub1sub1end", selected) 54 | } 55 | 56 | func TestNestedCommandsWithArgs(t *testing.T) { 57 | app := New("app", "") 58 | cmd := app.Command("a", "").Command("b", "") 59 | a := cmd.Arg("a", "").String() 60 | b := cmd.Arg("b", "").String() 61 | context := tokenize([]string{"a", "b", "c", "d"}, false) 62 | selected, err := parseAndExecute(app, context) 63 | assert.NoError(t, err) 64 | assert.True(t, context.EOL()) 65 | assert.Equal(t, "a b", selected) 66 | assert.Equal(t, "c", *a) 67 | assert.Equal(t, "d", *b) 68 | } 69 | 70 | func TestNestedCommandsWithFlags(t *testing.T) { 71 | app := New("app", "") 72 | cmd := app.Command("a", "").Command("b", "") 73 | a := cmd.Flag("aaa", "").Short('a').String() 74 | b := cmd.Flag("bbb", "").Short('b').String() 75 | err := app.init() 76 | assert.NoError(t, err) 77 | context := tokenize(strings.Split("a b --aaa x -b x", " "), false) 78 | selected, err := parseAndExecute(app, context) 79 | assert.NoError(t, err) 80 | assert.True(t, context.EOL()) 81 | assert.Equal(t, "a b", selected) 82 | assert.Equal(t, "x", *a) 83 | assert.Equal(t, "x", *b) 84 | } 85 | 86 | func TestNestedCommandWithMergedFlags(t *testing.T) { 87 | app := New("app", "") 88 | cmd0 := app.Command("a", "") 89 | cmd0f0 := cmd0.Flag("aflag", "").Bool() 90 | // cmd1 := app.Command("b", "") 91 | // cmd1f0 := cmd0.Flag("bflag", "").Bool() 92 | cmd00 := cmd0.Command("aa", "") 93 | cmd00f0 := cmd00.Flag("aaflag", "").Bool() 94 | err := app.init() 95 | assert.NoError(t, err) 96 | context := tokenize(strings.Split("a aa --aflag --aaflag", " "), false) 97 | selected, err := parseAndExecute(app, context) 98 | assert.NoError(t, err) 99 | assert.True(t, *cmd0f0) 100 | assert.True(t, *cmd00f0) 101 | assert.Equal(t, "a aa", selected) 102 | } 103 | 104 | func TestNestedCommandWithDuplicateFlagErrors(t *testing.T) { 105 | app := New("app", "") 106 | app.Flag("test", "").Bool() 107 | app.Command("cmd0", "").Flag("test", "").Bool() 108 | err := app.init() 109 | assert.Error(t, err) 110 | } 111 | 112 | func TestNestedCommandWithArgAndMergedFlags(t *testing.T) { 113 | app := New("app", "") 114 | cmd0 := app.Command("a", "") 115 | cmd0f0 := cmd0.Flag("aflag", "").Bool() 116 | // cmd1 := app.Command("b", "") 117 | // cmd1f0 := cmd0.Flag("bflag", "").Bool() 118 | cmd00 := cmd0.Command("aa", "") 119 | cmd00a0 := cmd00.Arg("arg", "").String() 120 | cmd00f0 := cmd00.Flag("aaflag", "").Bool() 121 | err := app.init() 122 | assert.NoError(t, err) 123 | context := tokenize(strings.Split("a aa hello --aflag --aaflag", " "), false) 124 | selected, err := parseAndExecute(app, context) 125 | assert.NoError(t, err) 126 | assert.True(t, *cmd0f0) 127 | assert.True(t, *cmd00f0) 128 | assert.Equal(t, "a aa", selected) 129 | assert.Equal(t, "hello", *cmd00a0) 130 | } 131 | 132 | func TestDefaultSubcommandEOL(t *testing.T) { 133 | app := newTestApp() 134 | c0 := app.Command("c0", "").Default() 135 | c0.Command("c01", "").Default() 136 | c0.Command("c02", "") 137 | 138 | cmd, err := app.Parse([]string{"c0"}) 139 | assert.NoError(t, err) 140 | assert.Equal(t, "c0 c01", cmd) 141 | } 142 | 143 | func TestDefaultSubcommandWithArg(t *testing.T) { 144 | app := newTestApp() 145 | c0 := app.Command("c0", "").Default() 146 | c01 := c0.Command("c01", "").Default() 147 | c012 := c01.Command("c012", "").Default() 148 | a0 := c012.Arg("a0", "").String() 149 | c0.Command("c02", "") 150 | 151 | cmd, err := app.Parse([]string{"c0", "hello"}) 152 | assert.NoError(t, err) 153 | assert.Equal(t, "c0 c01 c012", cmd) 154 | assert.Equal(t, "hello", *a0) 155 | } 156 | 157 | func TestDefaultSubcommandWithFlags(t *testing.T) { 158 | app := newTestApp() 159 | c0 := app.Command("c0", "").Default() 160 | _ = c0.Flag("f0", "").Int() 161 | c0c1 := c0.Command("c1", "").Default() 162 | c0c1f1 := c0c1.Flag("f1", "").Int() 163 | selected, err := app.Parse([]string{"--f1=2"}) 164 | assert.NoError(t, err) 165 | assert.Equal(t, "c0 c1", selected) 166 | assert.Equal(t, 2, *c0c1f1) 167 | _, err = app.Parse([]string{"--f2"}) 168 | assert.Error(t, err) 169 | } 170 | 171 | func TestMultipleDefaultCommands(t *testing.T) { 172 | app := newTestApp() 173 | app.Command("c0", "").Default() 174 | app.Command("c1", "").Default() 175 | _, err := app.Parse([]string{}) 176 | assert.Error(t, err) 177 | } 178 | 179 | func TestAliasedCommand(t *testing.T) { 180 | app := newTestApp() 181 | app.Command("one", "").Alias("two") 182 | selected, _ := app.Parse([]string{"one"}) 183 | assert.Equal(t, "one", selected) 184 | selected, _ = app.Parse([]string{"two"}) 185 | assert.Equal(t, "one", selected) 186 | // 2 due to "help" and "one" 187 | assert.Equal(t, 2, len(app.Model().FlattenedCommands())) 188 | } 189 | 190 | func TestDuplicateAlias(t *testing.T) { 191 | app := newTestApp() 192 | app.Command("one", "") 193 | app.Command("two", "").Alias("one") 194 | _, err := app.Parse([]string{"one"}) 195 | assert.Error(t, err) 196 | } 197 | 198 | func TestFlagCompletion(t *testing.T) { 199 | app := newTestApp() 200 | app.Command("one", "") 201 | two := app.Command("two", "") 202 | two.Flag("flag-1", "") 203 | two.Flag("flag-2", "").HintOptions("opt1", "opt2", "opt3") 204 | two.Flag("flag-3", "") 205 | 206 | cases := []struct { 207 | target cmdMixin 208 | flagName string 209 | flagValue string 210 | expectedFlagMatch bool 211 | expectedOptionMatch bool 212 | expectedFlags []string 213 | }{ 214 | { 215 | // Test top level flags 216 | target: app.cmdMixin, 217 | flagName: "", 218 | flagValue: "", 219 | expectedFlagMatch: false, 220 | expectedOptionMatch: false, 221 | expectedFlags: []string{"--help"}, 222 | }, 223 | { 224 | // Test no flag passed 225 | target: two.cmdMixin, 226 | flagName: "", 227 | flagValue: "", 228 | expectedFlagMatch: false, 229 | expectedOptionMatch: false, 230 | expectedFlags: []string{"--flag-1", "--flag-2", "--flag-3"}, 231 | }, 232 | { 233 | // Test an incomplete flag. Should still give all options as if the flag wasn't given at all. 234 | target: two.cmdMixin, 235 | flagName: "flag-", 236 | flagValue: "", 237 | expectedFlagMatch: false, 238 | expectedOptionMatch: false, 239 | expectedFlags: []string{"--flag-1", "--flag-2", "--flag-3"}, 240 | }, 241 | { 242 | // Test with a complete flag. Should show available choices for the flag 243 | // This flag has no options. No options should be produced. 244 | // Should also report an option was matched 245 | target: two.cmdMixin, 246 | flagName: "flag-1", 247 | flagValue: "", 248 | expectedFlagMatch: true, 249 | expectedOptionMatch: true, 250 | expectedFlags: []string(nil), 251 | }, 252 | { 253 | // Test with a complete flag. Should show available choices for the flag 254 | target: two.cmdMixin, 255 | flagName: "flag-2", 256 | flagValue: "", 257 | expectedFlagMatch: true, 258 | expectedOptionMatch: false, 259 | expectedFlags: []string{"opt1", "opt2", "opt3"}, 260 | }, 261 | { 262 | // Test with a complete flag and complete option for that flag. 263 | target: two.cmdMixin, 264 | flagName: "flag-2", 265 | flagValue: "opt1", 266 | expectedFlagMatch: true, 267 | expectedOptionMatch: true, 268 | expectedFlags: []string{"opt1", "opt2", "opt3"}, 269 | }, 270 | } 271 | 272 | for i, c := range cases { 273 | choices, flagMatch, optionMatch := c.target.FlagCompletion(c.flagName, c.flagValue) 274 | assert.Equal(t, c.expectedFlags, choices, "Test case %d: expectedFlags != actual flags", i+1) 275 | assert.Equal(t, c.expectedFlagMatch, flagMatch, "Test case %d: expectedFlagMatch != flagMatch", i+1) 276 | assert.Equal(t, c.expectedOptionMatch, optionMatch, "Test case %d: expectedOptionMatch != optionMatch", i+1) 277 | } 278 | 279 | } 280 | 281 | func TestCmdCompletion(t *testing.T) { 282 | app := newTestApp() 283 | app.Command("one", "") 284 | two := app.Command("two", "") 285 | two.Command("sub1", "") 286 | two.Command("sub2", "") 287 | 288 | assert.Equal(t, []string{"help", "one", "two"}, complete(t, app)) 289 | assert.Equal(t, []string{"sub1", "sub2"}, complete(t, app, "two")) 290 | } 291 | 292 | func TestHiddenCmdCompletion(t *testing.T) { 293 | app := newTestApp() 294 | 295 | // top level visible & hidden cmds, with no sub-cmds 296 | app.Command("visible1", "") 297 | app.Command("hidden1", "").Hidden() 298 | 299 | // visible cmd with visible & hidden sub-cmds 300 | visible2 := app.Command("visible2", "") 301 | visible2.Command("visible2-visible", "") 302 | visible2.Command("visible2-hidden", "").Hidden() 303 | 304 | // hidden cmd with visible & hidden sub-cmds 305 | hidden2 := app.Command("hidden2", "").Hidden() 306 | hidden2.Command("hidden2-visible", "") 307 | hidden2.Command("hidden2-hidden", "").Hidden() 308 | 309 | // Only top level visible cmds should show 310 | assert.Equal(t, []string{"help", "visible1", "visible2"}, complete(t, app)) 311 | 312 | // Only visible sub-cmds should show 313 | assert.Equal(t, []string{"visible2-visible"}, complete(t, app, "visible2")) 314 | 315 | // Hidden commands should still complete visible sub-cmds 316 | assert.Equal(t, []string{"hidden2-visible"}, complete(t, app, "hidden2")) 317 | } 318 | 319 | func TestDefaultCmdCompletion(t *testing.T) { 320 | app := newTestApp() 321 | 322 | cmd1 := app.Command("cmd1", "") 323 | 324 | cmd1Sub1 := cmd1.Command("cmd1-sub1", "") 325 | cmd1Sub1.Arg("cmd1-sub1-arg1", "").HintOptions("cmd1-arg1").String() 326 | 327 | cmd2 := app.Command("cmd2", "").Default() 328 | 329 | cmd2.Command("cmd2-sub1", "") 330 | 331 | cmd2Sub2 := cmd2.Command("cmd2-sub2", "").Default() 332 | 333 | cmd2Sub2Sub1 := cmd2Sub2.Command("cmd2-sub2-sub1", "").Default() 334 | cmd2Sub2Sub1.Arg("cmd2-sub2-sub1-arg1", "").HintOptions("cmd2-sub2-sub1-arg1").String() 335 | cmd2Sub2Sub1.Arg("cmd2-sub2-sub1-arg2", "").HintOptions("cmd2-sub2-sub1-arg2").String() 336 | 337 | // Without args, should get: 338 | // - root cmds (including implicit "help") 339 | // - thread of default cmds 340 | // - first arg hints for the final default cmd 341 | assert.Equal(t, []string{"cmd1", "cmd2", "cmd2-sub1", "cmd2-sub2", "cmd2-sub2-sub1", "cmd2-sub2-sub1-arg1", "help"}, complete(t, app)) 342 | 343 | // With a non-default cmd already listed, should get: 344 | // - sub cmds of that arg 345 | assert.Equal(t, []string{"cmd1-sub1"}, complete(t, app, "cmd1")) 346 | 347 | // With an explicit default cmd listed, should get: 348 | // - default child-cmds 349 | // - first arg hints for the final default cmd 350 | assert.Equal(t, []string{"cmd2-sub1", "cmd2-sub2", "cmd2-sub2-sub1", "cmd2-sub2-sub1-arg1"}, complete(t, app, "cmd2")) 351 | 352 | // Args should be completed when all preceding cmds are explicit, and when 353 | // any of them are implicit (not listed). Check this by trying all possible 354 | // combinations of choosing/excluding the three levels of cmds. This tests 355 | // root-level default, middle default, and end default. 356 | for i := 0; i < 8; i++ { 357 | var cmdline []string 358 | 359 | if i&1 != 0 { 360 | cmdline = append(cmdline, "cmd2") 361 | } 362 | if i&2 != 0 { 363 | cmdline = append(cmdline, "cmd2-sub2") 364 | } 365 | if i&4 != 0 { 366 | cmdline = append(cmdline, "cmd2-sub2-sub1") 367 | } 368 | 369 | assert.Contains(t, complete(t, app, cmdline...), "cmd2-sub2-sub1-arg1", "with cmdline: %v", cmdline) 370 | } 371 | 372 | // With both args of a default sub cmd, should get no completions 373 | assert.Empty(t, complete(t, app, "arg1", "arg2")) 374 | } 375 | -------------------------------------------------------------------------------- /vendor/gopkg.in/alecthomas/kingpin.v2/completions.go: -------------------------------------------------------------------------------- 1 | package kingpin 2 | 3 | // HintAction is a function type who is expected to return a slice of possible 4 | // command line arguments. 5 | type HintAction func() []string 6 | type completionsMixin struct { 7 | hintActions []HintAction 8 | builtinHintActions []HintAction 9 | } 10 | 11 | func (a *completionsMixin) addHintAction(action HintAction) { 12 | a.hintActions = append(a.hintActions, action) 13 | } 14 | 15 | // Allow adding of HintActions which are added internally, ie, EnumVar 16 | func (a *completionsMixin) addHintActionBuiltin(action HintAction) { 17 | a.builtinHintActions = append(a.builtinHintActions, action) 18 | } 19 | 20 | func (a *completionsMixin) resolveCompletions() []string { 21 | var hints []string 22 | 23 | options := a.builtinHintActions 24 | if len(a.hintActions) > 0 { 25 | // User specified their own hintActions. Use those instead. 26 | options = a.hintActions 27 | } 28 | 29 | for _, hintAction := range options { 30 | hints = append(hints, hintAction()...) 31 | } 32 | return hints 33 | } 34 | -------------------------------------------------------------------------------- /vendor/gopkg.in/alecthomas/kingpin.v2/completions_test.go: -------------------------------------------------------------------------------- 1 | package kingpin 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/alecthomas/assert" 7 | ) 8 | 9 | func TestResolveWithBuiltin(t *testing.T) { 10 | a := completionsMixin{} 11 | 12 | hintAction1 := func() []string { 13 | return []string{"opt1", "opt2"} 14 | } 15 | hintAction2 := func() []string { 16 | return []string{"opt3", "opt4"} 17 | } 18 | 19 | a.builtinHintActions = []HintAction{hintAction1, hintAction2} 20 | 21 | args := a.resolveCompletions() 22 | assert.Equal(t, []string{"opt1", "opt2", "opt3", "opt4"}, args) 23 | } 24 | 25 | func TestResolveWithUser(t *testing.T) { 26 | a := completionsMixin{} 27 | hintAction1 := func() []string { 28 | return []string{"opt1", "opt2"} 29 | } 30 | hintAction2 := func() []string { 31 | return []string{"opt3", "opt4"} 32 | } 33 | 34 | a.hintActions = []HintAction{hintAction1, hintAction2} 35 | 36 | args := a.resolveCompletions() 37 | assert.Equal(t, []string{"opt1", "opt2", "opt3", "opt4"}, args) 38 | } 39 | 40 | func TestResolveWithCombination(t *testing.T) { 41 | a := completionsMixin{} 42 | builtin := func() []string { 43 | return []string{"opt1", "opt2"} 44 | } 45 | user := func() []string { 46 | return []string{"opt3", "opt4"} 47 | } 48 | 49 | a.builtinHintActions = []HintAction{builtin} 50 | a.hintActions = []HintAction{user} 51 | 52 | args := a.resolveCompletions() 53 | // User provided args take preference over builtin (enum-defined) args. 54 | assert.Equal(t, []string{"opt3", "opt4"}, args) 55 | } 56 | 57 | func TestAddHintAction(t *testing.T) { 58 | a := completionsMixin{} 59 | hintFunc := func() []string { 60 | return []string{"opt1", "opt2"} 61 | } 62 | a.addHintAction(hintFunc) 63 | 64 | args := a.resolveCompletions() 65 | assert.Equal(t, []string{"opt1", "opt2"}, args) 66 | } 67 | 68 | func TestAddHintActionBuiltin(t *testing.T) { 69 | a := completionsMixin{} 70 | hintFunc := func() []string { 71 | return []string{"opt1", "opt2"} 72 | } 73 | 74 | a.addHintActionBuiltin(hintFunc) 75 | 76 | args := a.resolveCompletions() 77 | assert.Equal(t, []string{"opt1", "opt2"}, args) 78 | } 79 | -------------------------------------------------------------------------------- /vendor/gopkg.in/alecthomas/kingpin.v2/doc.go: -------------------------------------------------------------------------------- 1 | // Package kingpin provides command line interfaces like this: 2 | // 3 | // $ chat 4 | // usage: chat [] [] [ ...] 5 | // 6 | // Flags: 7 | // --debug enable debug mode 8 | // --help Show help. 9 | // --server=127.0.0.1 server address 10 | // 11 | // Commands: 12 | // help 13 | // Show help for a command. 14 | // 15 | // post [] 16 | // Post a message to a channel. 17 | // 18 | // register 19 | // Register a new user. 20 | // 21 | // $ chat help post 22 | // usage: chat [] post [] [] 23 | // 24 | // Post a message to a channel. 25 | // 26 | // Flags: 27 | // --image=IMAGE image to post 28 | // 29 | // Args: 30 | // channel to post to 31 | // [] text to post 32 | // $ chat post --image=~/Downloads/owls.jpg pics 33 | // 34 | // From code like this: 35 | // 36 | // package main 37 | // 38 | // import "gopkg.in/alecthomas/kingpin.v2" 39 | // 40 | // var ( 41 | // debug = kingpin.Flag("debug", "enable debug mode").Default("false").Bool() 42 | // serverIP = kingpin.Flag("server", "server address").Default("127.0.0.1").IP() 43 | // 44 | // register = kingpin.Command("register", "Register a new user.") 45 | // registerNick = register.Arg("nick", "nickname for user").Required().String() 46 | // registerName = register.Arg("name", "name of user").Required().String() 47 | // 48 | // post = kingpin.Command("post", "Post a message to a channel.") 49 | // postImage = post.Flag("image", "image to post").ExistingFile() 50 | // postChannel = post.Arg("channel", "channel to post to").Required().String() 51 | // postText = post.Arg("text", "text to post").String() 52 | // ) 53 | // 54 | // func main() { 55 | // switch kingpin.Parse() { 56 | // // Register user 57 | // case "register": 58 | // println(*registerNick) 59 | // 60 | // // Post message 61 | // case "post": 62 | // if *postImage != nil { 63 | // } 64 | // if *postText != "" { 65 | // } 66 | // } 67 | // } 68 | package kingpin 69 | -------------------------------------------------------------------------------- /vendor/gopkg.in/alecthomas/kingpin.v2/envar.go: -------------------------------------------------------------------------------- 1 | package kingpin 2 | 3 | import ( 4 | "os" 5 | "regexp" 6 | ) 7 | 8 | var ( 9 | envVarValuesSeparator = "\r?\n" 10 | envVarValuesTrimmer = regexp.MustCompile(envVarValuesSeparator + "$") 11 | envVarValuesSplitter = regexp.MustCompile(envVarValuesSeparator) 12 | ) 13 | 14 | type envarMixin struct { 15 | envar string 16 | noEnvar bool 17 | } 18 | 19 | func (e *envarMixin) HasEnvarValue() bool { 20 | return e.GetEnvarValue() != "" 21 | } 22 | 23 | func (e *envarMixin) GetEnvarValue() string { 24 | if e.noEnvar || e.envar == "" { 25 | return "" 26 | } 27 | return os.Getenv(e.envar) 28 | } 29 | 30 | func (e *envarMixin) GetSplitEnvarValue() []string { 31 | values := make([]string, 0) 32 | 33 | envarValue := e.GetEnvarValue() 34 | if envarValue == "" { 35 | return values 36 | } 37 | 38 | // Split by new line to extract multiple values, if any. 39 | trimmed := envVarValuesTrimmer.ReplaceAllString(envarValue, "") 40 | for _, value := range envVarValuesSplitter.Split(trimmed, -1) { 41 | values = append(values, value) 42 | } 43 | 44 | return values 45 | } 46 | -------------------------------------------------------------------------------- /vendor/gopkg.in/alecthomas/kingpin.v2/examples_test.go: -------------------------------------------------------------------------------- 1 | package kingpin 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "strings" 7 | ) 8 | 9 | type HTTPHeaderValue http.Header 10 | 11 | func (h *HTTPHeaderValue) Set(value string) error { 12 | parts := strings.SplitN(value, ":", 2) 13 | if len(parts) != 2 { 14 | return fmt.Errorf("expected HEADER:VALUE got '%s'", value) 15 | } 16 | (*http.Header)(h).Add(parts[0], parts[1]) 17 | return nil 18 | } 19 | 20 | func (h *HTTPHeaderValue) Get() interface{} { 21 | return (http.Header)(*h) 22 | } 23 | 24 | func (h *HTTPHeaderValue) String() string { 25 | return "" 26 | } 27 | 28 | func HTTPHeader(s Settings) (target *http.Header) { 29 | target = new(http.Header) 30 | s.SetValue((*HTTPHeaderValue)(target)) 31 | return 32 | } 33 | 34 | // This example ilustrates how to define custom parsers. HTTPHeader 35 | // cumulatively parses each encountered --header flag into a http.Header struct. 36 | func ExampleValue() { 37 | var ( 38 | curl = New("curl", "transfer a URL") 39 | headers = HTTPHeader(curl.Flag("headers", "Add HTTP headers to the request.").Short('H').PlaceHolder("HEADER:VALUE")) 40 | ) 41 | 42 | curl.Parse([]string{"-H Content-Type:application/octet-stream"}) 43 | for key, value := range *headers { 44 | fmt.Printf("%s = %s\n", key, value) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /vendor/gopkg.in/alecthomas/kingpin.v2/flags.go: -------------------------------------------------------------------------------- 1 | package kingpin 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | type flagGroup struct { 9 | short map[string]*FlagClause 10 | long map[string]*FlagClause 11 | flagOrder []*FlagClause 12 | } 13 | 14 | func newFlagGroup() *flagGroup { 15 | return &flagGroup{ 16 | short: map[string]*FlagClause{}, 17 | long: map[string]*FlagClause{}, 18 | } 19 | } 20 | 21 | // GetFlag gets a flag definition. 22 | // 23 | // This allows existing flags to be modified after definition but before parsing. Useful for 24 | // modular applications. 25 | func (f *flagGroup) GetFlag(name string) *FlagClause { 26 | return f.long[name] 27 | } 28 | 29 | // Flag defines a new flag with the given long name and help. 30 | func (f *flagGroup) Flag(name, help string) *FlagClause { 31 | flag := newFlag(name, help) 32 | f.long[name] = flag 33 | f.flagOrder = append(f.flagOrder, flag) 34 | return flag 35 | } 36 | 37 | func (f *flagGroup) init(defaultEnvarPrefix string) error { 38 | if err := f.checkDuplicates(); err != nil { 39 | return err 40 | } 41 | for _, flag := range f.long { 42 | if defaultEnvarPrefix != "" && !flag.noEnvar && flag.envar == "" { 43 | flag.envar = envarTransform(defaultEnvarPrefix + "_" + flag.name) 44 | } 45 | if err := flag.init(); err != nil { 46 | return err 47 | } 48 | if flag.shorthand != 0 { 49 | f.short[string(flag.shorthand)] = flag 50 | } 51 | } 52 | return nil 53 | } 54 | 55 | func (f *flagGroup) checkDuplicates() error { 56 | seenShort := map[rune]bool{} 57 | seenLong := map[string]bool{} 58 | for _, flag := range f.flagOrder { 59 | if flag.shorthand != 0 { 60 | if _, ok := seenShort[flag.shorthand]; ok { 61 | return fmt.Errorf("duplicate short flag -%c", flag.shorthand) 62 | } 63 | seenShort[flag.shorthand] = true 64 | } 65 | if _, ok := seenLong[flag.name]; ok { 66 | return fmt.Errorf("duplicate long flag --%s", flag.name) 67 | } 68 | seenLong[flag.name] = true 69 | } 70 | return nil 71 | } 72 | 73 | func (f *flagGroup) parse(context *ParseContext) (*FlagClause, error) { 74 | var token *Token 75 | 76 | loop: 77 | for { 78 | token = context.Peek() 79 | switch token.Type { 80 | case TokenEOL: 81 | break loop 82 | 83 | case TokenLong, TokenShort: 84 | flagToken := token 85 | defaultValue := "" 86 | var flag *FlagClause 87 | var ok bool 88 | invert := false 89 | 90 | name := token.Value 91 | if token.Type == TokenLong { 92 | flag, ok = f.long[name] 93 | if !ok { 94 | if strings.HasPrefix(name, "no-") { 95 | name = name[3:] 96 | invert = true 97 | } 98 | flag, ok = f.long[name] 99 | } 100 | if !ok { 101 | return nil, fmt.Errorf("unknown long flag '%s'", flagToken) 102 | } 103 | } else { 104 | flag, ok = f.short[name] 105 | if !ok { 106 | return nil, fmt.Errorf("unknown short flag '%s'", flagToken) 107 | } 108 | } 109 | 110 | context.Next() 111 | 112 | fb, ok := flag.value.(boolFlag) 113 | if ok && fb.IsBoolFlag() { 114 | if invert { 115 | defaultValue = "false" 116 | } else { 117 | defaultValue = "true" 118 | } 119 | } else { 120 | if invert { 121 | context.Push(token) 122 | return nil, fmt.Errorf("unknown long flag '%s'", flagToken) 123 | } 124 | token = context.Peek() 125 | if token.Type != TokenArg { 126 | context.Push(token) 127 | return nil, fmt.Errorf("expected argument for flag '%s'", flagToken) 128 | } 129 | context.Next() 130 | defaultValue = token.Value 131 | } 132 | 133 | context.matchedFlag(flag, defaultValue) 134 | return flag, nil 135 | 136 | default: 137 | break loop 138 | } 139 | } 140 | return nil, nil 141 | } 142 | 143 | // FlagClause is a fluid interface used to build flags. 144 | type FlagClause struct { 145 | parserMixin 146 | actionMixin 147 | completionsMixin 148 | envarMixin 149 | name string 150 | shorthand rune 151 | help string 152 | defaultValues []string 153 | placeholder string 154 | hidden bool 155 | } 156 | 157 | func newFlag(name, help string) *FlagClause { 158 | f := &FlagClause{ 159 | name: name, 160 | help: help, 161 | } 162 | return f 163 | } 164 | 165 | func (f *FlagClause) setDefault() error { 166 | if f.HasEnvarValue() { 167 | if v, ok := f.value.(repeatableFlag); !ok || !v.IsCumulative() { 168 | // Use the value as-is 169 | return f.value.Set(f.GetEnvarValue()) 170 | } else { 171 | for _, value := range f.GetSplitEnvarValue() { 172 | if err := f.value.Set(value); err != nil { 173 | return err 174 | } 175 | } 176 | return nil 177 | } 178 | } 179 | 180 | if len(f.defaultValues) > 0 { 181 | for _, defaultValue := range f.defaultValues { 182 | if err := f.value.Set(defaultValue); err != nil { 183 | return err 184 | } 185 | } 186 | return nil 187 | } 188 | 189 | return nil 190 | } 191 | 192 | func (f *FlagClause) needsValue() bool { 193 | haveDefault := len(f.defaultValues) > 0 194 | return f.required && !(haveDefault || f.HasEnvarValue()) 195 | } 196 | 197 | func (f *FlagClause) init() error { 198 | if f.required && len(f.defaultValues) > 0 { 199 | return fmt.Errorf("required flag '--%s' with default value that will never be used", f.name) 200 | } 201 | if f.value == nil { 202 | return fmt.Errorf("no type defined for --%s (eg. .String())", f.name) 203 | } 204 | if v, ok := f.value.(repeatableFlag); (!ok || !v.IsCumulative()) && len(f.defaultValues) > 1 { 205 | return fmt.Errorf("invalid default for '--%s', expecting single value", f.name) 206 | } 207 | return nil 208 | } 209 | 210 | // Dispatch to the given function after the flag is parsed and validated. 211 | func (f *FlagClause) Action(action Action) *FlagClause { 212 | f.addAction(action) 213 | return f 214 | } 215 | 216 | func (f *FlagClause) PreAction(action Action) *FlagClause { 217 | f.addPreAction(action) 218 | return f 219 | } 220 | 221 | // HintAction registers a HintAction (function) for the flag to provide completions 222 | func (a *FlagClause) HintAction(action HintAction) *FlagClause { 223 | a.addHintAction(action) 224 | return a 225 | } 226 | 227 | // HintOptions registers any number of options for the flag to provide completions 228 | func (a *FlagClause) HintOptions(options ...string) *FlagClause { 229 | a.addHintAction(func() []string { 230 | return options 231 | }) 232 | return a 233 | } 234 | 235 | func (a *FlagClause) EnumVar(target *string, options ...string) { 236 | a.parserMixin.EnumVar(target, options...) 237 | a.addHintActionBuiltin(func() []string { 238 | return options 239 | }) 240 | } 241 | 242 | func (a *FlagClause) Enum(options ...string) (target *string) { 243 | a.addHintActionBuiltin(func() []string { 244 | return options 245 | }) 246 | return a.parserMixin.Enum(options...) 247 | } 248 | 249 | // Default values for this flag. They *must* be parseable by the value of the flag. 250 | func (f *FlagClause) Default(values ...string) *FlagClause { 251 | f.defaultValues = values 252 | return f 253 | } 254 | 255 | // DEPRECATED: Use Envar(name) instead. 256 | func (f *FlagClause) OverrideDefaultFromEnvar(envar string) *FlagClause { 257 | return f.Envar(envar) 258 | } 259 | 260 | // Envar overrides the default value(s) for a flag from an environment variable, 261 | // if it is set. Several default values can be provided by using new lines to 262 | // separate them. 263 | func (f *FlagClause) Envar(name string) *FlagClause { 264 | f.envar = name 265 | f.noEnvar = false 266 | return f 267 | } 268 | 269 | // NoEnvar forces environment variable defaults to be disabled for this flag. 270 | // Most useful in conjunction with app.DefaultEnvars(). 271 | func (f *FlagClause) NoEnvar() *FlagClause { 272 | f.envar = "" 273 | f.noEnvar = true 274 | return f 275 | } 276 | 277 | // PlaceHolder sets the place-holder string used for flag values in the help. The 278 | // default behaviour is to use the value provided by Default() if provided, 279 | // then fall back on the capitalized flag name. 280 | func (f *FlagClause) PlaceHolder(placeholder string) *FlagClause { 281 | f.placeholder = placeholder 282 | return f 283 | } 284 | 285 | // Hidden hides a flag from usage but still allows it to be used. 286 | func (f *FlagClause) Hidden() *FlagClause { 287 | f.hidden = true 288 | return f 289 | } 290 | 291 | // Required makes the flag required. You can not provide a Default() value to a Required() flag. 292 | func (f *FlagClause) Required() *FlagClause { 293 | f.required = true 294 | return f 295 | } 296 | 297 | // Short sets the short flag name. 298 | func (f *FlagClause) Short(name rune) *FlagClause { 299 | f.shorthand = name 300 | return f 301 | } 302 | 303 | // Bool makes this flag a boolean flag. 304 | func (f *FlagClause) Bool() (target *bool) { 305 | target = new(bool) 306 | f.SetValue(newBoolValue(target)) 307 | return 308 | } 309 | -------------------------------------------------------------------------------- /vendor/gopkg.in/alecthomas/kingpin.v2/flags_test.go: -------------------------------------------------------------------------------- 1 | package kingpin 2 | 3 | import ( 4 | "io/ioutil" 5 | "os" 6 | 7 | "github.com/alecthomas/assert" 8 | 9 | "testing" 10 | ) 11 | 12 | func TestBool(t *testing.T) { 13 | app := newTestApp() 14 | b := app.Flag("b", "").Bool() 15 | _, err := app.Parse([]string{"--b"}) 16 | assert.NoError(t, err) 17 | assert.True(t, *b) 18 | } 19 | 20 | func TestNoBool(t *testing.T) { 21 | fg := newFlagGroup() 22 | f := fg.Flag("b", "").Default("true") 23 | b := f.Bool() 24 | fg.init("") 25 | tokens := tokenize([]string{"--no-b"}, false) 26 | _, err := fg.parse(tokens) 27 | assert.NoError(t, err) 28 | assert.False(t, *b) 29 | } 30 | 31 | func TestNegateNonBool(t *testing.T) { 32 | fg := newFlagGroup() 33 | f := fg.Flag("b", "") 34 | f.Int() 35 | fg.init("") 36 | tokens := tokenize([]string{"--no-b"}, false) 37 | _, err := fg.parse(tokens) 38 | assert.Error(t, err) 39 | } 40 | 41 | func TestNegativePrefixLongFlag(t *testing.T) { 42 | fg := newFlagGroup() 43 | f := fg.Flag("no-comment", "") 44 | b := f.Bool() 45 | fg.init("") 46 | tokens := tokenize([]string{"--no-comment"}, false) 47 | _, err := fg.parse(tokens) 48 | assert.NoError(t, err) 49 | assert.False(t, *b) 50 | } 51 | 52 | func TestInvalidFlagDefaultCanBeOverridden(t *testing.T) { 53 | app := newTestApp() 54 | app.Flag("a", "").Default("invalid").Bool() 55 | _, err := app.Parse([]string{}) 56 | assert.Error(t, err) 57 | } 58 | 59 | func TestRequiredFlag(t *testing.T) { 60 | app := newTestApp() 61 | app.Version("0.0.0").Writer(ioutil.Discard) 62 | exits := 0 63 | app.Terminate(func(int) { exits++ }) 64 | app.Flag("a", "").Required().Bool() 65 | _, err := app.Parse([]string{"--a"}) 66 | assert.NoError(t, err) 67 | _, err = app.Parse([]string{}) 68 | assert.Error(t, err) 69 | _, err = app.Parse([]string{"--version"}) 70 | assert.Equal(t, 1, exits) 71 | } 72 | 73 | func TestShortFlag(t *testing.T) { 74 | app := newTestApp() 75 | f := app.Flag("long", "").Short('s').Bool() 76 | _, err := app.Parse([]string{"-s"}) 77 | assert.NoError(t, err) 78 | assert.True(t, *f) 79 | } 80 | 81 | func TestUnicodeShortFlag(t *testing.T) { 82 | app := newTestApp() 83 | f := app.Flag("aaa", "").Short('ä').Bool() 84 | _, err := app.Parse([]string{"-ä"}) 85 | assert.NoError(t, err) 86 | assert.True(t, *f) 87 | } 88 | 89 | func TestCombinedShortFlags(t *testing.T) { 90 | app := newTestApp() 91 | a := app.Flag("short0", "").Short('0').Bool() 92 | b := app.Flag("short1", "").Short('1').Bool() 93 | c := app.Flag("short2", "").Short('2').Bool() 94 | _, err := app.Parse([]string{"-01"}) 95 | assert.NoError(t, err) 96 | assert.True(t, *a) 97 | assert.True(t, *b) 98 | assert.False(t, *c) 99 | } 100 | 101 | func TestCombinedUnicodeShortFlags(t *testing.T) { 102 | app := newTestApp() 103 | a := app.Flag("short0", "").Short('0').Bool() 104 | b := app.Flag("short1", "").Short('1').Bool() 105 | c := app.Flag("short2", "").Short('ä').Bool() 106 | d := app.Flag("short3", "").Short('2').Bool() 107 | _, err := app.Parse([]string{"-0ä1"}) 108 | assert.NoError(t, err) 109 | assert.True(t, *a) 110 | assert.True(t, *b) 111 | assert.True(t, *c) 112 | assert.False(t, *d) 113 | } 114 | 115 | func TestCombinedShortFlagArg(t *testing.T) { 116 | a := newTestApp() 117 | n := a.Flag("short", "").Short('s').Int() 118 | _, err := a.Parse([]string{"-s10"}) 119 | assert.NoError(t, err) 120 | assert.Equal(t, 10, *n) 121 | } 122 | 123 | func TestCombinedUnicodeShortFlagArg(t *testing.T) { 124 | app := newTestApp() 125 | a := app.Flag("short", "").Short('ä').Int() 126 | _, err := app.Parse([]string{"-ä10"}) 127 | assert.NoError(t, err) 128 | assert.Equal(t, 10, *a) 129 | } 130 | 131 | func TestCombinedUnicodeShortFlagUnicodeArg(t *testing.T) { 132 | app := newTestApp() 133 | a := app.Flag("short", "").Short('ä').String() 134 | _, err := app.Parse([]string{"-äöö"}) 135 | assert.NoError(t, err) 136 | assert.Equal(t, "öö", *a) 137 | } 138 | 139 | func TestEmptyShortFlagIsAnError(t *testing.T) { 140 | _, err := newTestApp().Parse([]string{"-"}) 141 | assert.Error(t, err) 142 | } 143 | 144 | func TestRequiredWithEnvarMissingErrors(t *testing.T) { 145 | app := newTestApp() 146 | app.Flag("t", "").OverrideDefaultFromEnvar("TEST_ENVAR").Required().Int() 147 | _, err := app.Parse([]string{}) 148 | assert.Error(t, err) 149 | } 150 | 151 | func TestRequiredWithEnvar(t *testing.T) { 152 | os.Setenv("TEST_ENVAR", "123") 153 | app := newTestApp() 154 | flag := app.Flag("t", "").Envar("TEST_ENVAR").Required().Int() 155 | _, err := app.Parse([]string{}) 156 | assert.NoError(t, err) 157 | assert.Equal(t, 123, *flag) 158 | } 159 | 160 | func TestSubcommandFlagRequiredWithEnvar(t *testing.T) { 161 | os.Setenv("TEST_ENVAR", "123") 162 | app := newTestApp() 163 | cmd := app.Command("command", "") 164 | flag := cmd.Flag("t", "").Envar("TEST_ENVAR").Required().Int() 165 | _, err := app.Parse([]string{"command"}) 166 | assert.NoError(t, err) 167 | assert.Equal(t, 123, *flag) 168 | } 169 | 170 | func TestRegexp(t *testing.T) { 171 | app := newTestApp() 172 | flag := app.Flag("reg", "").Regexp() 173 | _, err := app.Parse([]string{"--reg", "^abc$"}) 174 | assert.NoError(t, err) 175 | assert.NotNil(t, *flag) 176 | assert.Equal(t, "^abc$", (*flag).String()) 177 | assert.Regexp(t, *flag, "abc") 178 | assert.NotRegexp(t, *flag, "abcd") 179 | } 180 | 181 | func TestDuplicateShortFlag(t *testing.T) { 182 | app := newTestApp() 183 | app.Flag("a", "").Short('a').String() 184 | app.Flag("b", "").Short('a').String() 185 | _, err := app.Parse([]string{}) 186 | assert.Error(t, err) 187 | } 188 | 189 | func TestDuplicateLongFlag(t *testing.T) { 190 | app := newTestApp() 191 | app.Flag("a", "").String() 192 | app.Flag("a", "").String() 193 | _, err := app.Parse([]string{}) 194 | assert.Error(t, err) 195 | } 196 | 197 | func TestGetFlagAndOverrideDefault(t *testing.T) { 198 | app := newTestApp() 199 | a := app.Flag("a", "").Default("default").String() 200 | _, err := app.Parse([]string{}) 201 | assert.NoError(t, err) 202 | assert.Equal(t, "default", *a) 203 | app.GetFlag("a").Default("new") 204 | _, err = app.Parse([]string{}) 205 | assert.NoError(t, err) 206 | assert.Equal(t, "new", *a) 207 | } 208 | 209 | func TestEnvarOverrideDefault(t *testing.T) { 210 | os.Setenv("TEST_ENVAR", "123") 211 | app := newTestApp() 212 | flag := app.Flag("t", "").Default("default").Envar("TEST_ENVAR").String() 213 | _, err := app.Parse([]string{}) 214 | assert.NoError(t, err) 215 | assert.Equal(t, "123", *flag) 216 | } 217 | 218 | func TestFlagMultipleValuesDefault(t *testing.T) { 219 | app := newTestApp() 220 | a := app.Flag("a", "").Default("default1", "default2").Strings() 221 | _, err := app.Parse([]string{}) 222 | assert.NoError(t, err) 223 | assert.Equal(t, []string{"default1", "default2"}, *a) 224 | } 225 | 226 | func TestFlagMultipleValuesDefaultNonRepeatable(t *testing.T) { 227 | c := newTestApp() 228 | c.Flag("foo", "foo").Default("a", "b").String() 229 | _, err := c.Parse([]string{}) 230 | assert.Error(t, err) 231 | } 232 | 233 | func TestFlagMultipleValuesDefaultEnvarUnix(t *testing.T) { 234 | app := newTestApp() 235 | a := app.Flag("a", "").Envar("TEST_MULTIPLE_VALUES").Strings() 236 | os.Setenv("TEST_MULTIPLE_VALUES", "123\n456\n") 237 | _, err := app.Parse([]string{}) 238 | assert.NoError(t, err) 239 | assert.Equal(t, []string{"123", "456"}, *a) 240 | } 241 | 242 | func TestFlagMultipleValuesDefaultEnvarWindows(t *testing.T) { 243 | app := newTestApp() 244 | a := app.Flag("a", "").Envar("TEST_MULTIPLE_VALUES").Strings() 245 | os.Setenv("TEST_MULTIPLE_VALUES", "123\r\n456\r\n") 246 | _, err := app.Parse([]string{}) 247 | assert.NoError(t, err) 248 | assert.Equal(t, []string{"123", "456"}, *a) 249 | } 250 | 251 | func TestFlagMultipleValuesDefaultEnvarNonRepeatable(t *testing.T) { 252 | c := newTestApp() 253 | a := c.Flag("foo", "foo").Envar("TEST_MULTIPLE_VALUES_NON_REPEATABLE").String() 254 | os.Setenv("TEST_MULTIPLE_VALUES_NON_REPEATABLE", "123\n456") 255 | _, err := c.Parse([]string{}) 256 | assert.NoError(t, err) 257 | assert.Equal(t, "123\n456", *a) 258 | } 259 | 260 | func TestFlagHintAction(t *testing.T) { 261 | c := newTestApp() 262 | 263 | action := func() []string { 264 | return []string{"opt1", "opt2"} 265 | } 266 | 267 | a := c.Flag("foo", "foo").HintAction(action) 268 | args := a.resolveCompletions() 269 | assert.Equal(t, []string{"opt1", "opt2"}, args) 270 | } 271 | 272 | func TestFlagHintOptions(t *testing.T) { 273 | c := newTestApp() 274 | 275 | a := c.Flag("foo", "foo").HintOptions("opt1", "opt2") 276 | args := a.resolveCompletions() 277 | assert.Equal(t, []string{"opt1", "opt2"}, args) 278 | } 279 | 280 | func TestFlagEnumVar(t *testing.T) { 281 | c := newTestApp() 282 | var bar string 283 | 284 | a := c.Flag("foo", "foo") 285 | a.Enum("opt1", "opt2") 286 | b := c.Flag("bar", "bar") 287 | b.EnumVar(&bar, "opt3", "opt4") 288 | 289 | args := a.resolveCompletions() 290 | assert.Equal(t, []string{"opt1", "opt2"}, args) 291 | 292 | args = b.resolveCompletions() 293 | assert.Equal(t, []string{"opt3", "opt4"}, args) 294 | } 295 | 296 | func TestMultiHintOptions(t *testing.T) { 297 | c := newTestApp() 298 | 299 | a := c.Flag("foo", "foo").HintOptions("opt1").HintOptions("opt2") 300 | args := a.resolveCompletions() 301 | assert.Equal(t, []string{"opt1", "opt2"}, args) 302 | } 303 | func TestMultiHintActions(t *testing.T) { 304 | c := newTestApp() 305 | 306 | a := c.Flag("foo", "foo"). 307 | HintAction(func() []string { 308 | return []string{"opt1"} 309 | }). 310 | HintAction(func() []string { 311 | return []string{"opt2"} 312 | }) 313 | args := a.resolveCompletions() 314 | assert.Equal(t, []string{"opt1", "opt2"}, args) 315 | } 316 | 317 | func TestCombinationHintActionsOptions(t *testing.T) { 318 | c := newTestApp() 319 | 320 | a := c.Flag("foo", "foo").HintAction(func() []string { 321 | return []string{"opt1"} 322 | }).HintOptions("opt2") 323 | args := a.resolveCompletions() 324 | assert.Equal(t, []string{"opt1", "opt2"}, args) 325 | } 326 | 327 | func TestCombinationEnumActions(t *testing.T) { 328 | c := newTestApp() 329 | var foo string 330 | 331 | a := c.Flag("foo", "foo"). 332 | HintAction(func() []string { 333 | return []string{"opt1", "opt2"} 334 | }) 335 | a.Enum("opt3", "opt4") 336 | 337 | b := c.Flag("bar", "bar"). 338 | HintAction(func() []string { 339 | return []string{"opt5", "opt6"} 340 | }) 341 | b.EnumVar(&foo, "opt3", "opt4") 342 | 343 | // Provided HintActions should override automatically generated Enum options. 344 | args := a.resolveCompletions() 345 | assert.Equal(t, []string{"opt1", "opt2"}, args) 346 | 347 | args = b.resolveCompletions() 348 | assert.Equal(t, []string{"opt5", "opt6"}, args) 349 | } 350 | 351 | func TestCombinationEnumOptions(t *testing.T) { 352 | c := newTestApp() 353 | var foo string 354 | 355 | a := c.Flag("foo", "foo").HintOptions("opt1", "opt2") 356 | a.Enum("opt3", "opt4") 357 | 358 | b := c.Flag("bar", "bar").HintOptions("opt5", "opt6") 359 | b.EnumVar(&foo, "opt3", "opt4") 360 | 361 | // Provided HintOptions should override automatically generated Enum options. 362 | args := a.resolveCompletions() 363 | assert.Equal(t, []string{"opt1", "opt2"}, args) 364 | 365 | args = b.resolveCompletions() 366 | assert.Equal(t, []string{"opt5", "opt6"}, args) 367 | 368 | } 369 | -------------------------------------------------------------------------------- /vendor/gopkg.in/alecthomas/kingpin.v2/global.go: -------------------------------------------------------------------------------- 1 | package kingpin 2 | 3 | import ( 4 | "os" 5 | "path/filepath" 6 | ) 7 | 8 | var ( 9 | // CommandLine is the default Kingpin parser. 10 | CommandLine = New(filepath.Base(os.Args[0]), "") 11 | // Global help flag. Exposed for user customisation. 12 | HelpFlag = CommandLine.HelpFlag 13 | // Top-level help command. Exposed for user customisation. May be nil. 14 | HelpCommand = CommandLine.HelpCommand 15 | // Global version flag. Exposed for user customisation. May be nil. 16 | VersionFlag = CommandLine.VersionFlag 17 | ) 18 | 19 | // Command adds a new command to the default parser. 20 | func Command(name, help string) *CmdClause { 21 | return CommandLine.Command(name, help) 22 | } 23 | 24 | // Flag adds a new flag to the default parser. 25 | func Flag(name, help string) *FlagClause { 26 | return CommandLine.Flag(name, help) 27 | } 28 | 29 | // Arg adds a new argument to the top-level of the default parser. 30 | func Arg(name, help string) *ArgClause { 31 | return CommandLine.Arg(name, help) 32 | } 33 | 34 | // Parse and return the selected command. Will call the termination handler if 35 | // an error is encountered. 36 | func Parse() string { 37 | selected := MustParse(CommandLine.Parse(os.Args[1:])) 38 | if selected == "" && CommandLine.cmdGroup.have() { 39 | Usage() 40 | CommandLine.terminate(0) 41 | } 42 | return selected 43 | } 44 | 45 | // Errorf prints an error message to stderr. 46 | func Errorf(format string, args ...interface{}) { 47 | CommandLine.Errorf(format, args...) 48 | } 49 | 50 | // Fatalf prints an error message to stderr and exits. 51 | func Fatalf(format string, args ...interface{}) { 52 | CommandLine.Fatalf(format, args...) 53 | } 54 | 55 | // FatalIfError prints an error and exits if err is not nil. The error is printed 56 | // with the given prefix. 57 | func FatalIfError(err error, format string, args ...interface{}) { 58 | CommandLine.FatalIfError(err, format, args...) 59 | } 60 | 61 | // FatalUsage prints an error message followed by usage information, then 62 | // exits with a non-zero status. 63 | func FatalUsage(format string, args ...interface{}) { 64 | CommandLine.FatalUsage(format, args...) 65 | } 66 | 67 | // FatalUsageContext writes a printf formatted error message to stderr, then 68 | // usage information for the given ParseContext, before exiting. 69 | func FatalUsageContext(context *ParseContext, format string, args ...interface{}) { 70 | CommandLine.FatalUsageContext(context, format, args...) 71 | } 72 | 73 | // Usage prints usage to stderr. 74 | func Usage() { 75 | CommandLine.Usage(os.Args[1:]) 76 | } 77 | 78 | // Set global usage template to use (defaults to DefaultUsageTemplate). 79 | func UsageTemplate(template string) *Application { 80 | return CommandLine.UsageTemplate(template) 81 | } 82 | 83 | // MustParse can be used with app.Parse(args) to exit with an error if parsing fails. 84 | func MustParse(command string, err error) string { 85 | if err != nil { 86 | Fatalf("%s, try --help", err) 87 | } 88 | return command 89 | } 90 | 91 | // Version adds a flag for displaying the application version number. 92 | func Version(version string) *Application { 93 | return CommandLine.Version(version) 94 | } 95 | -------------------------------------------------------------------------------- /vendor/gopkg.in/alecthomas/kingpin.v2/guesswidth.go: -------------------------------------------------------------------------------- 1 | // +build appengine !linux,!freebsd,!darwin,!dragonfly,!netbsd,!openbsd 2 | 3 | package kingpin 4 | 5 | import "io" 6 | 7 | func guessWidth(w io.Writer) int { 8 | return 80 9 | } 10 | -------------------------------------------------------------------------------- /vendor/gopkg.in/alecthomas/kingpin.v2/guesswidth_unix.go: -------------------------------------------------------------------------------- 1 | // +build !appengine,linux freebsd darwin dragonfly netbsd openbsd 2 | 3 | package kingpin 4 | 5 | import ( 6 | "io" 7 | "os" 8 | "strconv" 9 | "syscall" 10 | "unsafe" 11 | ) 12 | 13 | func guessWidth(w io.Writer) int { 14 | // check if COLUMNS env is set to comply with 15 | // http://pubs.opengroup.org/onlinepubs/009604499/basedefs/xbd_chap08.html 16 | colsStr := os.Getenv("COLUMNS") 17 | if colsStr != "" { 18 | if cols, err := strconv.Atoi(colsStr); err == nil { 19 | return cols 20 | } 21 | } 22 | 23 | if t, ok := w.(*os.File); ok { 24 | fd := t.Fd() 25 | var dimensions [4]uint16 26 | 27 | if _, _, err := syscall.Syscall6( 28 | syscall.SYS_IOCTL, 29 | uintptr(fd), 30 | uintptr(syscall.TIOCGWINSZ), 31 | uintptr(unsafe.Pointer(&dimensions)), 32 | 0, 0, 0, 33 | ); err == 0 { 34 | return int(dimensions[1]) 35 | } 36 | } 37 | return 80 38 | } 39 | -------------------------------------------------------------------------------- /vendor/gopkg.in/alecthomas/kingpin.v2/model.go: -------------------------------------------------------------------------------- 1 | package kingpin 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "strings" 7 | ) 8 | 9 | // Data model for Kingpin command-line structure. 10 | 11 | type FlagGroupModel struct { 12 | Flags []*FlagModel 13 | } 14 | 15 | func (f *FlagGroupModel) FlagSummary() string { 16 | out := []string{} 17 | count := 0 18 | for _, flag := range f.Flags { 19 | if flag.Name != "help" { 20 | count++ 21 | } 22 | if flag.Required { 23 | if flag.IsBoolFlag() { 24 | out = append(out, fmt.Sprintf("--[no-]%s", flag.Name)) 25 | } else { 26 | out = append(out, fmt.Sprintf("--%s=%s", flag.Name, flag.FormatPlaceHolder())) 27 | } 28 | } 29 | } 30 | if count != len(out) { 31 | out = append(out, "[]") 32 | } 33 | return strings.Join(out, " ") 34 | } 35 | 36 | type FlagModel struct { 37 | Name string 38 | Help string 39 | Short rune 40 | Default []string 41 | Envar string 42 | PlaceHolder string 43 | Required bool 44 | Hidden bool 45 | Value Value 46 | } 47 | 48 | func (f *FlagModel) String() string { 49 | return f.Value.String() 50 | } 51 | 52 | func (f *FlagModel) IsBoolFlag() bool { 53 | if fl, ok := f.Value.(boolFlag); ok { 54 | return fl.IsBoolFlag() 55 | } 56 | return false 57 | } 58 | 59 | func (f *FlagModel) FormatPlaceHolder() string { 60 | if f.PlaceHolder != "" { 61 | return f.PlaceHolder 62 | } 63 | if len(f.Default) > 0 { 64 | ellipsis := "" 65 | if len(f.Default) > 1 { 66 | ellipsis = "..." 67 | } 68 | if _, ok := f.Value.(*stringValue); ok { 69 | return strconv.Quote(f.Default[0]) + ellipsis 70 | } 71 | return f.Default[0] + ellipsis 72 | } 73 | return strings.ToUpper(f.Name) 74 | } 75 | 76 | type ArgGroupModel struct { 77 | Args []*ArgModel 78 | } 79 | 80 | func (a *ArgGroupModel) ArgSummary() string { 81 | depth := 0 82 | out := []string{} 83 | for _, arg := range a.Args { 84 | h := "<" + arg.Name + ">" 85 | if !arg.Required { 86 | h = "[" + h 87 | depth++ 88 | } 89 | out = append(out, h) 90 | } 91 | out[len(out)-1] = out[len(out)-1] + strings.Repeat("]", depth) 92 | return strings.Join(out, " ") 93 | } 94 | 95 | type ArgModel struct { 96 | Name string 97 | Help string 98 | Default []string 99 | Envar string 100 | Required bool 101 | Value Value 102 | } 103 | 104 | func (a *ArgModel) String() string { 105 | return a.Value.String() 106 | } 107 | 108 | type CmdGroupModel struct { 109 | Commands []*CmdModel 110 | } 111 | 112 | func (c *CmdGroupModel) FlattenedCommands() (out []*CmdModel) { 113 | for _, cmd := range c.Commands { 114 | if len(cmd.Commands) == 0 { 115 | out = append(out, cmd) 116 | } 117 | out = append(out, cmd.FlattenedCommands()...) 118 | } 119 | return 120 | } 121 | 122 | type CmdModel struct { 123 | Name string 124 | Aliases []string 125 | Help string 126 | FullCommand string 127 | Depth int 128 | Hidden bool 129 | Default bool 130 | *FlagGroupModel 131 | *ArgGroupModel 132 | *CmdGroupModel 133 | } 134 | 135 | func (c *CmdModel) String() string { 136 | return c.FullCommand 137 | } 138 | 139 | type ApplicationModel struct { 140 | Name string 141 | Help string 142 | Version string 143 | Author string 144 | *ArgGroupModel 145 | *CmdGroupModel 146 | *FlagGroupModel 147 | } 148 | 149 | func (a *Application) Model() *ApplicationModel { 150 | return &ApplicationModel{ 151 | Name: a.Name, 152 | Help: a.Help, 153 | Version: a.version, 154 | Author: a.author, 155 | FlagGroupModel: a.flagGroup.Model(), 156 | ArgGroupModel: a.argGroup.Model(), 157 | CmdGroupModel: a.cmdGroup.Model(), 158 | } 159 | } 160 | 161 | func (a *argGroup) Model() *ArgGroupModel { 162 | m := &ArgGroupModel{} 163 | for _, arg := range a.args { 164 | m.Args = append(m.Args, arg.Model()) 165 | } 166 | return m 167 | } 168 | 169 | func (a *ArgClause) Model() *ArgModel { 170 | return &ArgModel{ 171 | Name: a.name, 172 | Help: a.help, 173 | Default: a.defaultValues, 174 | Envar: a.envar, 175 | Required: a.required, 176 | Value: a.value, 177 | } 178 | } 179 | 180 | func (f *flagGroup) Model() *FlagGroupModel { 181 | m := &FlagGroupModel{} 182 | for _, fl := range f.flagOrder { 183 | m.Flags = append(m.Flags, fl.Model()) 184 | } 185 | return m 186 | } 187 | 188 | func (f *FlagClause) Model() *FlagModel { 189 | return &FlagModel{ 190 | Name: f.name, 191 | Help: f.help, 192 | Short: rune(f.shorthand), 193 | Default: f.defaultValues, 194 | Envar: f.envar, 195 | PlaceHolder: f.placeholder, 196 | Required: f.required, 197 | Hidden: f.hidden, 198 | Value: f.value, 199 | } 200 | } 201 | 202 | func (c *cmdGroup) Model() *CmdGroupModel { 203 | m := &CmdGroupModel{} 204 | for _, cm := range c.commandOrder { 205 | m.Commands = append(m.Commands, cm.Model()) 206 | } 207 | return m 208 | } 209 | 210 | func (c *CmdClause) Model() *CmdModel { 211 | depth := 0 212 | for i := c; i != nil; i = i.parent { 213 | depth++ 214 | } 215 | return &CmdModel{ 216 | Name: c.name, 217 | Aliases: c.aliases, 218 | Help: c.help, 219 | Depth: depth, 220 | Hidden: c.hidden, 221 | Default: c.isDefault, 222 | FullCommand: c.FullCommand(), 223 | FlagGroupModel: c.flagGroup.Model(), 224 | ArgGroupModel: c.argGroup.Model(), 225 | CmdGroupModel: c.cmdGroup.Model(), 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /vendor/gopkg.in/alecthomas/kingpin.v2/parser.go: -------------------------------------------------------------------------------- 1 | package kingpin 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "os" 7 | "strings" 8 | "unicode/utf8" 9 | ) 10 | 11 | type TokenType int 12 | 13 | // Token types. 14 | const ( 15 | TokenShort TokenType = iota 16 | TokenLong 17 | TokenArg 18 | TokenError 19 | TokenEOL 20 | ) 21 | 22 | func (t TokenType) String() string { 23 | switch t { 24 | case TokenShort: 25 | return "short flag" 26 | case TokenLong: 27 | return "long flag" 28 | case TokenArg: 29 | return "argument" 30 | case TokenError: 31 | return "error" 32 | case TokenEOL: 33 | return "" 34 | } 35 | return "?" 36 | } 37 | 38 | var ( 39 | TokenEOLMarker = Token{-1, TokenEOL, ""} 40 | ) 41 | 42 | type Token struct { 43 | Index int 44 | Type TokenType 45 | Value string 46 | } 47 | 48 | func (t *Token) Equal(o *Token) bool { 49 | return t.Index == o.Index 50 | } 51 | 52 | func (t *Token) IsFlag() bool { 53 | return t.Type == TokenShort || t.Type == TokenLong 54 | } 55 | 56 | func (t *Token) IsEOF() bool { 57 | return t.Type == TokenEOL 58 | } 59 | 60 | func (t *Token) String() string { 61 | switch t.Type { 62 | case TokenShort: 63 | return "-" + t.Value 64 | case TokenLong: 65 | return "--" + t.Value 66 | case TokenArg: 67 | return t.Value 68 | case TokenError: 69 | return "error: " + t.Value 70 | case TokenEOL: 71 | return "" 72 | default: 73 | panic("unhandled type") 74 | } 75 | } 76 | 77 | // A union of possible elements in a parse stack. 78 | type ParseElement struct { 79 | // Clause is either *CmdClause, *ArgClause or *FlagClause. 80 | Clause interface{} 81 | // Value is corresponding value for an ArgClause or FlagClause (if any). 82 | Value *string 83 | } 84 | 85 | // ParseContext holds the current context of the parser. When passed to 86 | // Action() callbacks Elements will be fully populated with *FlagClause, 87 | // *ArgClause and *CmdClause values and their corresponding arguments (if 88 | // any). 89 | type ParseContext struct { 90 | SelectedCommand *CmdClause 91 | ignoreDefault bool 92 | argsOnly bool 93 | peek []*Token 94 | argi int // Index of current command-line arg we're processing. 95 | args []string 96 | rawArgs []string 97 | flags *flagGroup 98 | arguments *argGroup 99 | argumenti int // Cursor into arguments 100 | // Flags, arguments and commands encountered and collected during parse. 101 | Elements []*ParseElement 102 | } 103 | 104 | func (p *ParseContext) nextArg() *ArgClause { 105 | if p.argumenti >= len(p.arguments.args) { 106 | return nil 107 | } 108 | arg := p.arguments.args[p.argumenti] 109 | if !arg.consumesRemainder() { 110 | p.argumenti++ 111 | } 112 | return arg 113 | } 114 | 115 | func (p *ParseContext) next() { 116 | p.argi++ 117 | p.args = p.args[1:] 118 | } 119 | 120 | // HasTrailingArgs returns true if there are unparsed command-line arguments. 121 | // This can occur if the parser can not match remaining arguments. 122 | func (p *ParseContext) HasTrailingArgs() bool { 123 | return len(p.args) > 0 124 | } 125 | 126 | func tokenize(args []string, ignoreDefault bool) *ParseContext { 127 | return &ParseContext{ 128 | ignoreDefault: ignoreDefault, 129 | args: args, 130 | rawArgs: args, 131 | flags: newFlagGroup(), 132 | arguments: newArgGroup(), 133 | } 134 | } 135 | 136 | func (p *ParseContext) mergeFlags(flags *flagGroup) { 137 | for _, flag := range flags.flagOrder { 138 | if flag.shorthand != 0 { 139 | p.flags.short[string(flag.shorthand)] = flag 140 | } 141 | p.flags.long[flag.name] = flag 142 | p.flags.flagOrder = append(p.flags.flagOrder, flag) 143 | } 144 | } 145 | 146 | func (p *ParseContext) mergeArgs(args *argGroup) { 147 | for _, arg := range args.args { 148 | p.arguments.args = append(p.arguments.args, arg) 149 | } 150 | } 151 | 152 | func (p *ParseContext) EOL() bool { 153 | return p.Peek().Type == TokenEOL 154 | } 155 | 156 | // Next token in the parse context. 157 | func (p *ParseContext) Next() *Token { 158 | if len(p.peek) > 0 { 159 | return p.pop() 160 | } 161 | 162 | // End of tokens. 163 | if len(p.args) == 0 { 164 | return &Token{Index: p.argi, Type: TokenEOL} 165 | } 166 | 167 | arg := p.args[0] 168 | p.next() 169 | 170 | if p.argsOnly { 171 | return &Token{p.argi, TokenArg, arg} 172 | } 173 | 174 | // All remaining args are passed directly. 175 | if arg == "--" { 176 | p.argsOnly = true 177 | return p.Next() 178 | } 179 | 180 | if strings.HasPrefix(arg, "--") { 181 | parts := strings.SplitN(arg[2:], "=", 2) 182 | token := &Token{p.argi, TokenLong, parts[0]} 183 | if len(parts) == 2 { 184 | p.Push(&Token{p.argi, TokenArg, parts[1]}) 185 | } 186 | return token 187 | } 188 | 189 | if strings.HasPrefix(arg, "-") { 190 | if len(arg) == 1 { 191 | return &Token{Index: p.argi, Type: TokenShort} 192 | } 193 | shortRune, size := utf8.DecodeRuneInString(arg[1:]) 194 | short := string(shortRune) 195 | flag, ok := p.flags.short[short] 196 | // Not a known short flag, we'll just return it anyway. 197 | if !ok { 198 | } else if fb, ok := flag.value.(boolFlag); ok && fb.IsBoolFlag() { 199 | // Bool short flag. 200 | } else { 201 | // Short flag with combined argument: -fARG 202 | token := &Token{p.argi, TokenShort, short} 203 | if len(arg) > size+1 { 204 | p.Push(&Token{p.argi, TokenArg, arg[size+1:]}) 205 | } 206 | return token 207 | } 208 | 209 | if len(arg) > size+1 { 210 | p.args = append([]string{"-" + arg[size+1:]}, p.args...) 211 | } 212 | return &Token{p.argi, TokenShort, short} 213 | } else if strings.HasPrefix(arg, "@") { 214 | expanded, err := ExpandArgsFromFile(arg[1:]) 215 | if err != nil { 216 | return &Token{p.argi, TokenError, err.Error()} 217 | } 218 | if len(p.args) == 0 { 219 | p.args = expanded 220 | } else { 221 | p.args = append(expanded, p.args...) 222 | } 223 | return p.Next() 224 | } 225 | 226 | return &Token{p.argi, TokenArg, arg} 227 | } 228 | 229 | func (p *ParseContext) Peek() *Token { 230 | if len(p.peek) == 0 { 231 | return p.Push(p.Next()) 232 | } 233 | return p.peek[len(p.peek)-1] 234 | } 235 | 236 | func (p *ParseContext) Push(token *Token) *Token { 237 | p.peek = append(p.peek, token) 238 | return token 239 | } 240 | 241 | func (p *ParseContext) pop() *Token { 242 | end := len(p.peek) - 1 243 | token := p.peek[end] 244 | p.peek = p.peek[0:end] 245 | return token 246 | } 247 | 248 | func (p *ParseContext) String() string { 249 | return p.SelectedCommand.FullCommand() 250 | } 251 | 252 | func (p *ParseContext) matchedFlag(flag *FlagClause, value string) { 253 | p.Elements = append(p.Elements, &ParseElement{Clause: flag, Value: &value}) 254 | } 255 | 256 | func (p *ParseContext) matchedArg(arg *ArgClause, value string) { 257 | p.Elements = append(p.Elements, &ParseElement{Clause: arg, Value: &value}) 258 | } 259 | 260 | func (p *ParseContext) matchedCmd(cmd *CmdClause) { 261 | p.Elements = append(p.Elements, &ParseElement{Clause: cmd}) 262 | p.mergeFlags(cmd.flagGroup) 263 | p.mergeArgs(cmd.argGroup) 264 | p.SelectedCommand = cmd 265 | } 266 | 267 | // Expand arguments from a file. Lines starting with # will be treated as comments. 268 | func ExpandArgsFromFile(filename string) (out []string, err error) { 269 | r, err := os.Open(filename) 270 | if err != nil { 271 | return nil, err 272 | } 273 | defer r.Close() 274 | scanner := bufio.NewScanner(r) 275 | for scanner.Scan() { 276 | line := scanner.Text() 277 | if strings.HasPrefix(line, "#") { 278 | continue 279 | } 280 | out = append(out, line) 281 | } 282 | err = scanner.Err() 283 | return 284 | } 285 | 286 | func parse(context *ParseContext, app *Application) (err error) { 287 | context.mergeFlags(app.flagGroup) 288 | context.mergeArgs(app.argGroup) 289 | 290 | cmds := app.cmdGroup 291 | ignoreDefault := context.ignoreDefault 292 | 293 | loop: 294 | for !context.EOL() { 295 | token := context.Peek() 296 | 297 | switch token.Type { 298 | case TokenLong, TokenShort: 299 | if flag, err := context.flags.parse(context); err != nil { 300 | if !ignoreDefault { 301 | if cmd := cmds.defaultSubcommand(); cmd != nil { 302 | cmd.completionAlts = cmds.cmdNames() 303 | context.matchedCmd(cmd) 304 | cmds = cmd.cmdGroup 305 | break 306 | } 307 | } 308 | return err 309 | } else if flag == HelpFlag { 310 | ignoreDefault = true 311 | } 312 | 313 | case TokenArg: 314 | if cmds.have() { 315 | selectedDefault := false 316 | cmd, ok := cmds.commands[token.String()] 317 | if !ok { 318 | if !ignoreDefault { 319 | if cmd = cmds.defaultSubcommand(); cmd != nil { 320 | cmd.completionAlts = cmds.cmdNames() 321 | selectedDefault = true 322 | } 323 | } 324 | if cmd == nil { 325 | return fmt.Errorf("expected command but got %q", token) 326 | } 327 | } 328 | if cmd == HelpCommand { 329 | ignoreDefault = true 330 | } 331 | cmd.completionAlts = nil 332 | context.matchedCmd(cmd) 333 | cmds = cmd.cmdGroup 334 | if !selectedDefault { 335 | context.Next() 336 | } 337 | } else if context.arguments.have() { 338 | if app.noInterspersed { 339 | // no more flags 340 | context.argsOnly = true 341 | } 342 | arg := context.nextArg() 343 | if arg == nil { 344 | break loop 345 | } 346 | context.matchedArg(arg, token.String()) 347 | context.Next() 348 | } else { 349 | break loop 350 | } 351 | 352 | case TokenEOL: 353 | break loop 354 | } 355 | } 356 | 357 | // Move to innermost default command. 358 | for !ignoreDefault { 359 | if cmd := cmds.defaultSubcommand(); cmd != nil { 360 | cmd.completionAlts = cmds.cmdNames() 361 | context.matchedCmd(cmd) 362 | cmds = cmd.cmdGroup 363 | } else { 364 | break 365 | } 366 | } 367 | 368 | if !context.EOL() { 369 | return fmt.Errorf("unexpected %s", context.Peek()) 370 | } 371 | 372 | // Set defaults for all remaining args. 373 | for arg := context.nextArg(); arg != nil && !arg.consumesRemainder(); arg = context.nextArg() { 374 | for _, defaultValue := range arg.defaultValues { 375 | if err := arg.value.Set(defaultValue); err != nil { 376 | return fmt.Errorf("invalid default value '%s' for argument '%s'", defaultValue, arg.name) 377 | } 378 | } 379 | } 380 | 381 | return 382 | } 383 | -------------------------------------------------------------------------------- /vendor/gopkg.in/alecthomas/kingpin.v2/parser_test.go: -------------------------------------------------------------------------------- 1 | package kingpin 2 | 3 | import ( 4 | "io/ioutil" 5 | "os" 6 | "testing" 7 | 8 | "github.com/alecthomas/assert" 9 | ) 10 | 11 | func TestParserExpandFromFile(t *testing.T) { 12 | f, err := ioutil.TempFile("", "") 13 | assert.NoError(t, err) 14 | defer os.Remove(f.Name()) 15 | f.WriteString("hello\nworld\n") 16 | f.Close() 17 | 18 | app := New("test", "") 19 | arg0 := app.Arg("arg0", "").String() 20 | arg1 := app.Arg("arg1", "").String() 21 | 22 | _, err = app.Parse([]string{"@" + f.Name()}) 23 | assert.NoError(t, err) 24 | assert.Equal(t, "hello", *arg0) 25 | assert.Equal(t, "world", *arg1) 26 | } 27 | 28 | func TestParserExpandFromFileLeadingArg(t *testing.T) { 29 | f, err := ioutil.TempFile("", "") 30 | assert.NoError(t, err) 31 | defer os.Remove(f.Name()) 32 | f.WriteString("hello\nworld\n") 33 | f.Close() 34 | 35 | app := New("test", "") 36 | arg0 := app.Arg("arg0", "").String() 37 | arg1 := app.Arg("arg1", "").String() 38 | arg2 := app.Arg("arg2", "").String() 39 | 40 | _, err = app.Parse([]string{"prefix", "@" + f.Name()}) 41 | assert.NoError(t, err) 42 | assert.Equal(t, "prefix", *arg0) 43 | assert.Equal(t, "hello", *arg1) 44 | assert.Equal(t, "world", *arg2) 45 | } 46 | 47 | func TestParserExpandFromFileTrailingArg(t *testing.T) { 48 | f, err := ioutil.TempFile("", "") 49 | assert.NoError(t, err) 50 | defer os.Remove(f.Name()) 51 | f.WriteString("hello\nworld\n") 52 | f.Close() 53 | 54 | app := New("test", "") 55 | arg0 := app.Arg("arg0", "").String() 56 | arg1 := app.Arg("arg1", "").String() 57 | arg2 := app.Arg("arg2", "").String() 58 | 59 | _, err = app.Parse([]string{"@" + f.Name(), "suffix"}) 60 | assert.NoError(t, err) 61 | assert.Equal(t, "hello", *arg0) 62 | assert.Equal(t, "world", *arg1) 63 | assert.Equal(t, "suffix", *arg2) 64 | } 65 | 66 | func TestParserExpandFromFileMultipleSurroundingArgs(t *testing.T) { 67 | f, err := ioutil.TempFile("", "") 68 | assert.NoError(t, err) 69 | defer os.Remove(f.Name()) 70 | f.WriteString("hello\nworld\n") 71 | f.Close() 72 | 73 | app := New("test", "") 74 | arg0 := app.Arg("arg0", "").String() 75 | arg1 := app.Arg("arg1", "").String() 76 | arg2 := app.Arg("arg2", "").String() 77 | arg3 := app.Arg("arg3", "").String() 78 | 79 | _, err = app.Parse([]string{"prefix", "@" + f.Name(), "suffix"}) 80 | assert.NoError(t, err) 81 | assert.Equal(t, "prefix", *arg0) 82 | assert.Equal(t, "hello", *arg1) 83 | assert.Equal(t, "world", *arg2) 84 | assert.Equal(t, "suffix", *arg3) 85 | } 86 | 87 | func TestParserExpandFromFileMultipleFlags(t *testing.T) { 88 | f, err := ioutil.TempFile("", "") 89 | assert.NoError(t, err) 90 | defer os.Remove(f.Name()) 91 | f.WriteString("--flag1=f1\n--flag2=f2\n") 92 | f.Close() 93 | 94 | app := New("test", "") 95 | flag0 := app.Flag("flag0", "").String() 96 | flag1 := app.Flag("flag1", "").String() 97 | flag2 := app.Flag("flag2", "").String() 98 | flag3 := app.Flag("flag3", "").String() 99 | 100 | _, err = app.Parse([]string{"--flag0=f0", "@" + f.Name(), "--flag3=f3"}) 101 | assert.NoError(t, err) 102 | assert.Equal(t, "f0", *flag0) 103 | assert.Equal(t, "f1", *flag1) 104 | assert.Equal(t, "f2", *flag2) 105 | assert.Equal(t, "f3", *flag3) 106 | } 107 | 108 | func TestParseContextPush(t *testing.T) { 109 | app := New("test", "") 110 | app.Command("foo", "").Command("bar", "") 111 | c := tokenize([]string{"foo", "bar"}, false) 112 | a := c.Next() 113 | assert.Equal(t, TokenArg, a.Type) 114 | b := c.Next() 115 | assert.Equal(t, TokenArg, b.Type) 116 | c.Push(b) 117 | c.Push(a) 118 | a = c.Next() 119 | assert.Equal(t, "foo", a.Value) 120 | b = c.Next() 121 | assert.Equal(t, "bar", b.Value) 122 | } 123 | -------------------------------------------------------------------------------- /vendor/gopkg.in/alecthomas/kingpin.v2/parsers.go: -------------------------------------------------------------------------------- 1 | package kingpin 2 | 3 | import ( 4 | "net" 5 | "net/url" 6 | "os" 7 | "time" 8 | 9 | "github.com/alecthomas/units" 10 | ) 11 | 12 | type Settings interface { 13 | SetValue(value Value) 14 | } 15 | 16 | type parserMixin struct { 17 | value Value 18 | required bool 19 | } 20 | 21 | func (p *parserMixin) SetValue(value Value) { 22 | p.value = value 23 | } 24 | 25 | // StringMap provides key=value parsing into a map. 26 | func (p *parserMixin) StringMap() (target *map[string]string) { 27 | target = &(map[string]string{}) 28 | p.StringMapVar(target) 29 | return 30 | } 31 | 32 | // Duration sets the parser to a time.Duration parser. 33 | func (p *parserMixin) Duration() (target *time.Duration) { 34 | target = new(time.Duration) 35 | p.DurationVar(target) 36 | return 37 | } 38 | 39 | // Bytes parses numeric byte units. eg. 1.5KB 40 | func (p *parserMixin) Bytes() (target *units.Base2Bytes) { 41 | target = new(units.Base2Bytes) 42 | p.BytesVar(target) 43 | return 44 | } 45 | 46 | // IP sets the parser to a net.IP parser. 47 | func (p *parserMixin) IP() (target *net.IP) { 48 | target = new(net.IP) 49 | p.IPVar(target) 50 | return 51 | } 52 | 53 | // TCP (host:port) address. 54 | func (p *parserMixin) TCP() (target **net.TCPAddr) { 55 | target = new(*net.TCPAddr) 56 | p.TCPVar(target) 57 | return 58 | } 59 | 60 | // TCPVar (host:port) address. 61 | func (p *parserMixin) TCPVar(target **net.TCPAddr) { 62 | p.SetValue(newTCPAddrValue(target)) 63 | } 64 | 65 | // ExistingFile sets the parser to one that requires and returns an existing file. 66 | func (p *parserMixin) ExistingFile() (target *string) { 67 | target = new(string) 68 | p.ExistingFileVar(target) 69 | return 70 | } 71 | 72 | // ExistingDir sets the parser to one that requires and returns an existing directory. 73 | func (p *parserMixin) ExistingDir() (target *string) { 74 | target = new(string) 75 | p.ExistingDirVar(target) 76 | return 77 | } 78 | 79 | // ExistingFileOrDir sets the parser to one that requires and returns an existing file OR directory. 80 | func (p *parserMixin) ExistingFileOrDir() (target *string) { 81 | target = new(string) 82 | p.ExistingFileOrDirVar(target) 83 | return 84 | } 85 | 86 | // File returns an os.File against an existing file. 87 | func (p *parserMixin) File() (target **os.File) { 88 | target = new(*os.File) 89 | p.FileVar(target) 90 | return 91 | } 92 | 93 | // File attempts to open a File with os.OpenFile(flag, perm). 94 | func (p *parserMixin) OpenFile(flag int, perm os.FileMode) (target **os.File) { 95 | target = new(*os.File) 96 | p.OpenFileVar(target, flag, perm) 97 | return 98 | } 99 | 100 | // URL provides a valid, parsed url.URL. 101 | func (p *parserMixin) URL() (target **url.URL) { 102 | target = new(*url.URL) 103 | p.URLVar(target) 104 | return 105 | } 106 | 107 | // StringMap provides key=value parsing into a map. 108 | func (p *parserMixin) StringMapVar(target *map[string]string) { 109 | p.SetValue(newStringMapValue(target)) 110 | } 111 | 112 | // Float sets the parser to a float64 parser. 113 | func (p *parserMixin) Float() (target *float64) { 114 | return p.Float64() 115 | } 116 | 117 | // Float sets the parser to a float64 parser. 118 | func (p *parserMixin) FloatVar(target *float64) { 119 | p.Float64Var(target) 120 | } 121 | 122 | // Duration sets the parser to a time.Duration parser. 123 | func (p *parserMixin) DurationVar(target *time.Duration) { 124 | p.SetValue(newDurationValue(target)) 125 | } 126 | 127 | // BytesVar parses numeric byte units. eg. 1.5KB 128 | func (p *parserMixin) BytesVar(target *units.Base2Bytes) { 129 | p.SetValue(newBytesValue(target)) 130 | } 131 | 132 | // IP sets the parser to a net.IP parser. 133 | func (p *parserMixin) IPVar(target *net.IP) { 134 | p.SetValue(newIPValue(target)) 135 | } 136 | 137 | // ExistingFile sets the parser to one that requires and returns an existing file. 138 | func (p *parserMixin) ExistingFileVar(target *string) { 139 | p.SetValue(newExistingFileValue(target)) 140 | } 141 | 142 | // ExistingDir sets the parser to one that requires and returns an existing directory. 143 | func (p *parserMixin) ExistingDirVar(target *string) { 144 | p.SetValue(newExistingDirValue(target)) 145 | } 146 | 147 | // ExistingDir sets the parser to one that requires and returns an existing directory. 148 | func (p *parserMixin) ExistingFileOrDirVar(target *string) { 149 | p.SetValue(newExistingFileOrDirValue(target)) 150 | } 151 | 152 | // FileVar opens an existing file. 153 | func (p *parserMixin) FileVar(target **os.File) { 154 | p.SetValue(newFileValue(target, os.O_RDONLY, 0)) 155 | } 156 | 157 | // OpenFileVar calls os.OpenFile(flag, perm) 158 | func (p *parserMixin) OpenFileVar(target **os.File, flag int, perm os.FileMode) { 159 | p.SetValue(newFileValue(target, flag, perm)) 160 | } 161 | 162 | // URL provides a valid, parsed url.URL. 163 | func (p *parserMixin) URLVar(target **url.URL) { 164 | p.SetValue(newURLValue(target)) 165 | } 166 | 167 | // URLList provides a parsed list of url.URL values. 168 | func (p *parserMixin) URLList() (target *[]*url.URL) { 169 | target = new([]*url.URL) 170 | p.URLListVar(target) 171 | return 172 | } 173 | 174 | // URLListVar provides a parsed list of url.URL values. 175 | func (p *parserMixin) URLListVar(target *[]*url.URL) { 176 | p.SetValue(newURLListValue(target)) 177 | } 178 | 179 | // Enum allows a value from a set of options. 180 | func (p *parserMixin) Enum(options ...string) (target *string) { 181 | target = new(string) 182 | p.EnumVar(target, options...) 183 | return 184 | } 185 | 186 | // EnumVar allows a value from a set of options. 187 | func (p *parserMixin) EnumVar(target *string, options ...string) { 188 | p.SetValue(newEnumFlag(target, options...)) 189 | } 190 | 191 | // Enums allows a set of values from a set of options. 192 | func (p *parserMixin) Enums(options ...string) (target *[]string) { 193 | target = new([]string) 194 | p.EnumsVar(target, options...) 195 | return 196 | } 197 | 198 | // EnumVar allows a value from a set of options. 199 | func (p *parserMixin) EnumsVar(target *[]string, options ...string) { 200 | p.SetValue(newEnumsFlag(target, options...)) 201 | } 202 | 203 | // A Counter increments a number each time it is encountered. 204 | func (p *parserMixin) Counter() (target *int) { 205 | target = new(int) 206 | p.CounterVar(target) 207 | return 208 | } 209 | 210 | func (p *parserMixin) CounterVar(target *int) { 211 | p.SetValue(newCounterValue(target)) 212 | } 213 | -------------------------------------------------------------------------------- /vendor/gopkg.in/alecthomas/kingpin.v2/parsers_test.go: -------------------------------------------------------------------------------- 1 | package kingpin 2 | 3 | import ( 4 | "io/ioutil" 5 | "net" 6 | "net/url" 7 | "os" 8 | 9 | "github.com/alecthomas/assert" 10 | 11 | "testing" 12 | ) 13 | 14 | func TestParseStrings(t *testing.T) { 15 | p := parserMixin{} 16 | v := p.Strings() 17 | p.value.Set("a") 18 | p.value.Set("b") 19 | assert.Equal(t, []string{"a", "b"}, *v) 20 | } 21 | 22 | func TestStringsStringer(t *testing.T) { 23 | target := []string{} 24 | v := newAccumulator(&target, func(v interface{}) Value { return newStringValue(v.(*string)) }) 25 | v.Set("hello") 26 | v.Set("world") 27 | assert.Equal(t, "hello,world", v.String()) 28 | } 29 | 30 | func TestParseStringMap(t *testing.T) { 31 | p := parserMixin{} 32 | v := p.StringMap() 33 | p.value.Set("a:b") 34 | p.value.Set("b:c") 35 | assert.Equal(t, map[string]string{"a": "b", "b": "c"}, *v) 36 | } 37 | 38 | func TestParseIP(t *testing.T) { 39 | p := parserMixin{} 40 | v := p.IP() 41 | p.value.Set("10.1.1.2") 42 | ip := net.ParseIP("10.1.1.2") 43 | assert.Equal(t, ip, *v) 44 | } 45 | 46 | func TestParseURL(t *testing.T) { 47 | p := parserMixin{} 48 | v := p.URL() 49 | p.value.Set("http://w3.org") 50 | u, err := url.Parse("http://w3.org") 51 | assert.NoError(t, err) 52 | assert.Equal(t, *u, **v) 53 | } 54 | 55 | func TestParseExistingFile(t *testing.T) { 56 | f, err := ioutil.TempFile("", "") 57 | if err != nil { 58 | t.Fatal(err) 59 | } 60 | defer f.Close() 61 | defer os.Remove(f.Name()) 62 | 63 | p := parserMixin{} 64 | v := p.ExistingFile() 65 | err = p.value.Set(f.Name()) 66 | assert.NoError(t, err) 67 | assert.Equal(t, f.Name(), *v) 68 | err = p.value.Set("/etc/hostsDEFINITELYMISSING") 69 | assert.Error(t, err) 70 | } 71 | 72 | func TestParseTCPAddr(t *testing.T) { 73 | p := parserMixin{} 74 | v := p.TCP() 75 | err := p.value.Set("127.0.0.1:1234") 76 | assert.NoError(t, err) 77 | expected, err := net.ResolveTCPAddr("tcp", "127.0.0.1:1234") 78 | assert.NoError(t, err) 79 | assert.Equal(t, *expected, **v) 80 | } 81 | 82 | func TestParseTCPAddrList(t *testing.T) { 83 | p := parserMixin{} 84 | _ = p.TCPList() 85 | err := p.value.Set("127.0.0.1:1234") 86 | assert.NoError(t, err) 87 | err = p.value.Set("127.0.0.1:1235") 88 | assert.NoError(t, err) 89 | assert.Equal(t, "127.0.0.1:1234,127.0.0.1:1235", p.value.String()) 90 | } 91 | 92 | func TestFloat32(t *testing.T) { 93 | p := parserMixin{} 94 | v := p.Float32() 95 | err := p.value.Set("123.45") 96 | assert.NoError(t, err) 97 | assert.InEpsilon(t, 123.45, *v, 0.001) 98 | } 99 | -------------------------------------------------------------------------------- /vendor/gopkg.in/alecthomas/kingpin.v2/templates.go: -------------------------------------------------------------------------------- 1 | package kingpin 2 | 3 | // Default usage template. 4 | var DefaultUsageTemplate = `{{define "FormatCommand"}}\ 5 | {{if .FlagSummary}} {{.FlagSummary}}{{end}}\ 6 | {{range .Args}} {{if not .Required}}[{{end}}<{{.Name}}>{{if .Value|IsCumulative}}...{{end}}{{if not .Required}}]{{end}}{{end}}\ 7 | {{end}}\ 8 | 9 | {{define "FormatCommands"}}\ 10 | {{range .FlattenedCommands}}\ 11 | {{if not .Hidden}}\ 12 | {{.FullCommand}}{{if .Default}}*{{end}}{{template "FormatCommand" .}} 13 | {{.Help|Wrap 4}} 14 | {{end}}\ 15 | {{end}}\ 16 | {{end}}\ 17 | 18 | {{define "FormatUsage"}}\ 19 | {{template "FormatCommand" .}}{{if .Commands}} [ ...]{{end}} 20 | {{if .Help}} 21 | {{.Help|Wrap 0}}\ 22 | {{end}}\ 23 | 24 | {{end}}\ 25 | 26 | {{if .Context.SelectedCommand}}\ 27 | usage: {{.App.Name}} {{.Context.SelectedCommand}}{{template "FormatUsage" .Context.SelectedCommand}} 28 | {{else}}\ 29 | usage: {{.App.Name}}{{template "FormatUsage" .App}} 30 | {{end}}\ 31 | {{if .Context.Flags}}\ 32 | Flags: 33 | {{.Context.Flags|FlagsToTwoColumns|FormatTwoColumns}} 34 | {{end}}\ 35 | {{if .Context.Args}}\ 36 | Args: 37 | {{.Context.Args|ArgsToTwoColumns|FormatTwoColumns}} 38 | {{end}}\ 39 | {{if .Context.SelectedCommand}}\ 40 | {{if len .Context.SelectedCommand.Commands}}\ 41 | Subcommands: 42 | {{template "FormatCommands" .Context.SelectedCommand}} 43 | {{end}}\ 44 | {{else if .App.Commands}}\ 45 | Commands: 46 | {{template "FormatCommands" .App}} 47 | {{end}}\ 48 | ` 49 | 50 | // Usage template where command's optional flags are listed separately 51 | var SeparateOptionalFlagsUsageTemplate = `{{define "FormatCommand"}}\ 52 | {{if .FlagSummary}} {{.FlagSummary}}{{end}}\ 53 | {{range .Args}} {{if not .Required}}[{{end}}<{{.Name}}>{{if .Value|IsCumulative}}...{{end}}{{if not .Required}}]{{end}}{{end}}\ 54 | {{end}}\ 55 | 56 | {{define "FormatCommands"}}\ 57 | {{range .FlattenedCommands}}\ 58 | {{if not .Hidden}}\ 59 | {{.FullCommand}}{{if .Default}}*{{end}}{{template "FormatCommand" .}} 60 | {{.Help|Wrap 4}} 61 | {{end}}\ 62 | {{end}}\ 63 | {{end}}\ 64 | 65 | {{define "FormatUsage"}}\ 66 | {{template "FormatCommand" .}}{{if .Commands}} [ ...]{{end}} 67 | {{if .Help}} 68 | {{.Help|Wrap 0}}\ 69 | {{end}}\ 70 | 71 | {{end}}\ 72 | {{if .Context.SelectedCommand}}\ 73 | usage: {{.App.Name}} {{.Context.SelectedCommand}}{{template "FormatUsage" .Context.SelectedCommand}} 74 | {{else}}\ 75 | usage: {{.App.Name}}{{template "FormatUsage" .App}} 76 | {{end}}\ 77 | 78 | {{if .Context.Flags|RequiredFlags}}\ 79 | Required flags: 80 | {{.Context.Flags|RequiredFlags|FlagsToTwoColumns|FormatTwoColumns}} 81 | {{end}}\ 82 | {{if .Context.Flags|OptionalFlags}}\ 83 | Optional flags: 84 | {{.Context.Flags|OptionalFlags|FlagsToTwoColumns|FormatTwoColumns}} 85 | {{end}}\ 86 | {{if .Context.Args}}\ 87 | Args: 88 | {{.Context.Args|ArgsToTwoColumns|FormatTwoColumns}} 89 | {{end}}\ 90 | {{if .Context.SelectedCommand}}\ 91 | Subcommands: 92 | {{if .Context.SelectedCommand.Commands}}\ 93 | {{template "FormatCommands" .Context.SelectedCommand}} 94 | {{end}}\ 95 | {{else if .App.Commands}}\ 96 | Commands: 97 | {{template "FormatCommands" .App}} 98 | {{end}}\ 99 | ` 100 | 101 | // Usage template with compactly formatted commands. 102 | var CompactUsageTemplate = `{{define "FormatCommand"}}\ 103 | {{if .FlagSummary}} {{.FlagSummary}}{{end}}\ 104 | {{range .Args}} {{if not .Required}}[{{end}}<{{.Name}}>{{if .Value|IsCumulative}}...{{end}}{{if not .Required}}]{{end}}{{end}}\ 105 | {{end}}\ 106 | 107 | {{define "FormatCommandList"}}\ 108 | {{range .}}\ 109 | {{if not .Hidden}}\ 110 | {{.Depth|Indent}}{{.Name}}{{if .Default}}*{{end}}{{template "FormatCommand" .}} 111 | {{end}}\ 112 | {{template "FormatCommandList" .Commands}}\ 113 | {{end}}\ 114 | {{end}}\ 115 | 116 | {{define "FormatUsage"}}\ 117 | {{template "FormatCommand" .}}{{if .Commands}} [ ...]{{end}} 118 | {{if .Help}} 119 | {{.Help|Wrap 0}}\ 120 | {{end}}\ 121 | 122 | {{end}}\ 123 | 124 | {{if .Context.SelectedCommand}}\ 125 | usage: {{.App.Name}} {{.Context.SelectedCommand}}{{template "FormatUsage" .Context.SelectedCommand}} 126 | {{else}}\ 127 | usage: {{.App.Name}}{{template "FormatUsage" .App}} 128 | {{end}}\ 129 | {{if .Context.Flags}}\ 130 | Flags: 131 | {{.Context.Flags|FlagsToTwoColumns|FormatTwoColumns}} 132 | {{end}}\ 133 | {{if .Context.Args}}\ 134 | Args: 135 | {{.Context.Args|ArgsToTwoColumns|FormatTwoColumns}} 136 | {{end}}\ 137 | {{if .Context.SelectedCommand}}\ 138 | {{if .Context.SelectedCommand.Commands}}\ 139 | Commands: 140 | {{.Context.SelectedCommand}} 141 | {{template "FormatCommandList" .Context.SelectedCommand.Commands}} 142 | {{end}}\ 143 | {{else if .App.Commands}}\ 144 | Commands: 145 | {{template "FormatCommandList" .App.Commands}} 146 | {{end}}\ 147 | ` 148 | 149 | var ManPageTemplate = `{{define "FormatFlags"}}\ 150 | {{range .Flags}}\ 151 | {{if not .Hidden}}\ 152 | .TP 153 | \fB{{if .Short}}-{{.Short|Char}}, {{end}}--{{.Name}}{{if not .IsBoolFlag}}={{.FormatPlaceHolder}}{{end}}\\fR 154 | {{.Help}} 155 | {{end}}\ 156 | {{end}}\ 157 | {{end}}\ 158 | 159 | {{define "FormatCommand"}}\ 160 | {{if .FlagSummary}} {{.FlagSummary}}{{end}}\ 161 | {{range .Args}} {{if not .Required}}[{{end}}<{{.Name}}{{if .Default}}*{{end}}>{{if .Value|IsCumulative}}...{{end}}{{if not .Required}}]{{end}}{{end}}\ 162 | {{end}}\ 163 | 164 | {{define "FormatCommands"}}\ 165 | {{range .FlattenedCommands}}\ 166 | {{if not .Hidden}}\ 167 | .SS 168 | \fB{{.FullCommand}}{{template "FormatCommand" .}}\\fR 169 | .PP 170 | {{.Help}} 171 | {{template "FormatFlags" .}}\ 172 | {{end}}\ 173 | {{end}}\ 174 | {{end}}\ 175 | 176 | {{define "FormatUsage"}}\ 177 | {{template "FormatCommand" .}}{{if .Commands}} [ ...]{{end}}\\fR 178 | {{end}}\ 179 | 180 | .TH {{.App.Name}} 1 {{.App.Version}} "{{.App.Author}}" 181 | .SH "NAME" 182 | {{.App.Name}} 183 | .SH "SYNOPSIS" 184 | .TP 185 | \fB{{.App.Name}}{{template "FormatUsage" .App}} 186 | .SH "DESCRIPTION" 187 | {{.App.Help}} 188 | .SH "OPTIONS" 189 | {{template "FormatFlags" .App}}\ 190 | {{if .App.Commands}}\ 191 | .SH "COMMANDS" 192 | {{template "FormatCommands" .App}}\ 193 | {{end}}\ 194 | ` 195 | 196 | // Default usage template. 197 | var LongHelpTemplate = `{{define "FormatCommand"}}\ 198 | {{if .FlagSummary}} {{.FlagSummary}}{{end}}\ 199 | {{range .Args}} {{if not .Required}}[{{end}}<{{.Name}}>{{if .Value|IsCumulative}}...{{end}}{{if not .Required}}]{{end}}{{end}}\ 200 | {{end}}\ 201 | 202 | {{define "FormatCommands"}}\ 203 | {{range .FlattenedCommands}}\ 204 | {{if not .Hidden}}\ 205 | {{.FullCommand}}{{template "FormatCommand" .}} 206 | {{.Help|Wrap 4}} 207 | {{with .Flags|FlagsToTwoColumns}}{{FormatTwoColumnsWithIndent . 4 2}}{{end}} 208 | {{end}}\ 209 | {{end}}\ 210 | {{end}}\ 211 | 212 | {{define "FormatUsage"}}\ 213 | {{template "FormatCommand" .}}{{if .Commands}} [ ...]{{end}} 214 | {{if .Help}} 215 | {{.Help|Wrap 0}}\ 216 | {{end}}\ 217 | 218 | {{end}}\ 219 | 220 | usage: {{.App.Name}}{{template "FormatUsage" .App}} 221 | {{if .Context.Flags}}\ 222 | Flags: 223 | {{.Context.Flags|FlagsToTwoColumns|FormatTwoColumns}} 224 | {{end}}\ 225 | {{if .Context.Args}}\ 226 | Args: 227 | {{.Context.Args|ArgsToTwoColumns|FormatTwoColumns}} 228 | {{end}}\ 229 | {{if .App.Commands}}\ 230 | Commands: 231 | {{template "FormatCommands" .App}} 232 | {{end}}\ 233 | ` 234 | 235 | var BashCompletionTemplate = ` 236 | _{{.App.Name}}_bash_autocomplete() { 237 | local cur prev opts base 238 | COMPREPLY=() 239 | cur="${COMP_WORDS[COMP_CWORD]}" 240 | opts=$( ${COMP_WORDS[0]} --completion-bash ${COMP_WORDS[@]:1:$COMP_CWORD} ) 241 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 242 | return 0 243 | } 244 | complete -F _{{.App.Name}}_bash_autocomplete {{.App.Name}} 245 | 246 | ` 247 | 248 | var ZshCompletionTemplate = ` 249 | #compdef {{.App.Name}} 250 | autoload -U compinit && compinit 251 | autoload -U bashcompinit && bashcompinit 252 | 253 | _{{.App.Name}}_bash_autocomplete() { 254 | local cur prev opts base 255 | COMPREPLY=() 256 | cur="${COMP_WORDS[COMP_CWORD]}" 257 | opts=$( ${COMP_WORDS[0]} --completion-bash ${COMP_WORDS[@]:1:$COMP_CWORD} ) 258 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 259 | return 0 260 | } 261 | complete -F _{{.App.Name}}_bash_autocomplete {{.App.Name}} 262 | ` 263 | -------------------------------------------------------------------------------- /vendor/gopkg.in/alecthomas/kingpin.v2/usage.go: -------------------------------------------------------------------------------- 1 | package kingpin 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "go/doc" 7 | "io" 8 | "strings" 9 | 10 | "github.com/alecthomas/template" 11 | ) 12 | 13 | var ( 14 | preIndent = " " 15 | ) 16 | 17 | func formatTwoColumns(w io.Writer, indent, padding, width int, rows [][2]string) { 18 | // Find size of first column. 19 | s := 0 20 | for _, row := range rows { 21 | if c := len(row[0]); c > s && c < 30 { 22 | s = c 23 | } 24 | } 25 | 26 | indentStr := strings.Repeat(" ", indent) 27 | offsetStr := strings.Repeat(" ", s+padding) 28 | 29 | for _, row := range rows { 30 | buf := bytes.NewBuffer(nil) 31 | doc.ToText(buf, row[1], "", preIndent, width-s-padding-indent) 32 | lines := strings.Split(strings.TrimRight(buf.String(), "\n"), "\n") 33 | fmt.Fprintf(w, "%s%-*s%*s", indentStr, s, row[0], padding, "") 34 | if len(row[0]) >= 30 { 35 | fmt.Fprintf(w, "\n%s%s", indentStr, offsetStr) 36 | } 37 | fmt.Fprintf(w, "%s\n", lines[0]) 38 | for _, line := range lines[1:] { 39 | fmt.Fprintf(w, "%s%s%s\n", indentStr, offsetStr, line) 40 | } 41 | } 42 | } 43 | 44 | // Usage writes application usage to w. It parses args to determine 45 | // appropriate help context, such as which command to show help for. 46 | func (a *Application) Usage(args []string) { 47 | context, err := a.parseContext(true, args) 48 | a.FatalIfError(err, "") 49 | if err := a.UsageForContextWithTemplate(context, 2, a.usageTemplate); err != nil { 50 | panic(err) 51 | } 52 | } 53 | 54 | func formatAppUsage(app *ApplicationModel) string { 55 | s := []string{app.Name} 56 | if len(app.Flags) > 0 { 57 | s = append(s, app.FlagSummary()) 58 | } 59 | if len(app.Args) > 0 { 60 | s = append(s, app.ArgSummary()) 61 | } 62 | return strings.Join(s, " ") 63 | } 64 | 65 | func formatCmdUsage(app *ApplicationModel, cmd *CmdModel) string { 66 | s := []string{app.Name, cmd.String()} 67 | if len(app.Flags) > 0 { 68 | s = append(s, app.FlagSummary()) 69 | } 70 | if len(app.Args) > 0 { 71 | s = append(s, app.ArgSummary()) 72 | } 73 | return strings.Join(s, " ") 74 | } 75 | 76 | func formatFlag(haveShort bool, flag *FlagModel) string { 77 | flagString := "" 78 | if flag.Short != 0 { 79 | flagString += fmt.Sprintf("-%c, --%s", flag.Short, flag.Name) 80 | } else { 81 | if haveShort { 82 | flagString += fmt.Sprintf(" --%s", flag.Name) 83 | } else { 84 | flagString += fmt.Sprintf("--%s", flag.Name) 85 | } 86 | } 87 | if !flag.IsBoolFlag() { 88 | flagString += fmt.Sprintf("=%s", flag.FormatPlaceHolder()) 89 | } 90 | if v, ok := flag.Value.(repeatableFlag); ok && v.IsCumulative() { 91 | flagString += " ..." 92 | } 93 | return flagString 94 | } 95 | 96 | type templateParseContext struct { 97 | SelectedCommand *CmdModel 98 | *FlagGroupModel 99 | *ArgGroupModel 100 | } 101 | 102 | type templateContext struct { 103 | App *ApplicationModel 104 | Width int 105 | Context *templateParseContext 106 | } 107 | 108 | // UsageForContext displays usage information from a ParseContext (obtained from 109 | // Application.ParseContext() or Action(f) callbacks). 110 | func (a *Application) UsageForContext(context *ParseContext) error { 111 | return a.UsageForContextWithTemplate(context, 2, a.usageTemplate) 112 | } 113 | 114 | // UsageForContextWithTemplate is the base usage function. You generally don't need to use this. 115 | func (a *Application) UsageForContextWithTemplate(context *ParseContext, indent int, tmpl string) error { 116 | width := guessWidth(a.usageWriter) 117 | funcs := template.FuncMap{ 118 | "Indent": func(level int) string { 119 | return strings.Repeat(" ", level*indent) 120 | }, 121 | "Wrap": func(indent int, s string) string { 122 | buf := bytes.NewBuffer(nil) 123 | indentText := strings.Repeat(" ", indent) 124 | doc.ToText(buf, s, indentText, " "+indentText, width-indent) 125 | return buf.String() 126 | }, 127 | "FormatFlag": formatFlag, 128 | "FlagsToTwoColumns": func(f []*FlagModel) [][2]string { 129 | rows := [][2]string{} 130 | haveShort := false 131 | for _, flag := range f { 132 | if flag.Short != 0 { 133 | haveShort = true 134 | break 135 | } 136 | } 137 | for _, flag := range f { 138 | if !flag.Hidden { 139 | rows = append(rows, [2]string{formatFlag(haveShort, flag), flag.Help}) 140 | } 141 | } 142 | return rows 143 | }, 144 | "RequiredFlags": func(f []*FlagModel) []*FlagModel { 145 | requiredFlags := []*FlagModel{} 146 | for _, flag := range f { 147 | if flag.Required { 148 | requiredFlags = append(requiredFlags, flag) 149 | } 150 | } 151 | return requiredFlags 152 | }, 153 | "OptionalFlags": func(f []*FlagModel) []*FlagModel { 154 | optionalFlags := []*FlagModel{} 155 | for _, flag := range f { 156 | if !flag.Required { 157 | optionalFlags = append(optionalFlags, flag) 158 | } 159 | } 160 | return optionalFlags 161 | }, 162 | "ArgsToTwoColumns": func(a []*ArgModel) [][2]string { 163 | rows := [][2]string{} 164 | for _, arg := range a { 165 | s := "<" + arg.Name + ">" 166 | if !arg.Required { 167 | s = "[" + s + "]" 168 | } 169 | rows = append(rows, [2]string{s, arg.Help}) 170 | } 171 | return rows 172 | }, 173 | "FormatTwoColumns": func(rows [][2]string) string { 174 | buf := bytes.NewBuffer(nil) 175 | formatTwoColumns(buf, indent, indent, width, rows) 176 | return buf.String() 177 | }, 178 | "FormatTwoColumnsWithIndent": func(rows [][2]string, indent, padding int) string { 179 | buf := bytes.NewBuffer(nil) 180 | formatTwoColumns(buf, indent, padding, width, rows) 181 | return buf.String() 182 | }, 183 | "FormatAppUsage": formatAppUsage, 184 | "FormatCommandUsage": formatCmdUsage, 185 | "IsCumulative": func(value Value) bool { 186 | r, ok := value.(remainderArg) 187 | return ok && r.IsCumulative() 188 | }, 189 | "Char": func(c rune) string { 190 | return string(c) 191 | }, 192 | } 193 | t, err := template.New("usage").Funcs(funcs).Parse(tmpl) 194 | if err != nil { 195 | return err 196 | } 197 | var selectedCommand *CmdModel 198 | if context.SelectedCommand != nil { 199 | selectedCommand = context.SelectedCommand.Model() 200 | } 201 | ctx := templateContext{ 202 | App: a.Model(), 203 | Width: width, 204 | Context: &templateParseContext{ 205 | SelectedCommand: selectedCommand, 206 | FlagGroupModel: context.flags.Model(), 207 | ArgGroupModel: context.arguments.Model(), 208 | }, 209 | } 210 | return t.Execute(a.usageWriter, ctx) 211 | } 212 | -------------------------------------------------------------------------------- /vendor/gopkg.in/alecthomas/kingpin.v2/usage_test.go: -------------------------------------------------------------------------------- 1 | package kingpin 2 | 3 | import ( 4 | "bytes" 5 | "strings" 6 | "testing" 7 | 8 | "github.com/alecthomas/assert" 9 | ) 10 | 11 | func TestFormatTwoColumns(t *testing.T) { 12 | buf := bytes.NewBuffer(nil) 13 | formatTwoColumns(buf, 2, 2, 20, [][2]string{ 14 | {"--hello", "Hello world help with something that is cool."}, 15 | }) 16 | expected := ` --hello Hello 17 | world 18 | help with 19 | something 20 | that is 21 | cool. 22 | ` 23 | assert.Equal(t, expected, buf.String()) 24 | } 25 | 26 | func TestFormatTwoColumnsWide(t *testing.T) { 27 | samples := [][2]string{ 28 | {strings.Repeat("x", 29), "29 chars"}, 29 | {strings.Repeat("x", 30), "30 chars"}} 30 | buf := bytes.NewBuffer(nil) 31 | formatTwoColumns(buf, 0, 0, 200, samples) 32 | expected := `xxxxxxxxxxxxxxxxxxxxxxxxxxxxx29 chars 33 | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 34 | 30 chars 35 | ` 36 | assert.Equal(t, expected, buf.String()) 37 | } 38 | 39 | func TestHiddenCommand(t *testing.T) { 40 | templates := []struct{ name, template string }{ 41 | {"default", DefaultUsageTemplate}, 42 | {"Compact", CompactUsageTemplate}, 43 | {"Long", LongHelpTemplate}, 44 | {"Man", ManPageTemplate}, 45 | } 46 | 47 | var buf bytes.Buffer 48 | t.Log("1") 49 | 50 | a := New("test", "Test").Writer(&buf).Terminate(nil) 51 | a.Command("visible", "visible") 52 | a.Command("hidden", "hidden").Hidden() 53 | 54 | for _, tp := range templates { 55 | buf.Reset() 56 | a.UsageTemplate(tp.template) 57 | a.Parse(nil) 58 | // a.Parse([]string{"--help"}) 59 | usage := buf.String() 60 | t.Logf("Usage for %s is:\n%s\n", tp.name, usage) 61 | 62 | assert.NotContains(t, usage, "hidden") 63 | assert.Contains(t, usage, "visible") 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /vendor/gopkg.in/alecthomas/kingpin.v2/values.go: -------------------------------------------------------------------------------- 1 | package kingpin 2 | 3 | //go:generate go run ./cmd/genvalues/main.go 4 | 5 | import ( 6 | "fmt" 7 | "net" 8 | "net/url" 9 | "os" 10 | "reflect" 11 | "regexp" 12 | "strings" 13 | "time" 14 | 15 | "github.com/alecthomas/units" 16 | ) 17 | 18 | // NOTE: Most of the base type values were lifted from: 19 | // http://golang.org/src/pkg/flag/flag.go?s=20146:20222 20 | 21 | // Value is the interface to the dynamic value stored in a flag. 22 | // (The default value is represented as a string.) 23 | // 24 | // If a Value has an IsBoolFlag() bool method returning true, the command-line 25 | // parser makes --name equivalent to -name=true rather than using the next 26 | // command-line argument, and adds a --no-name counterpart for negating the 27 | // flag. 28 | type Value interface { 29 | String() string 30 | Set(string) error 31 | } 32 | 33 | // Getter is an interface that allows the contents of a Value to be retrieved. 34 | // It wraps the Value interface, rather than being part of it, because it 35 | // appeared after Go 1 and its compatibility rules. All Value types provided 36 | // by this package satisfy the Getter interface. 37 | type Getter interface { 38 | Value 39 | Get() interface{} 40 | } 41 | 42 | // Optional interface to indicate boolean flags that don't accept a value, and 43 | // implicitly have a --no- negation counterpart. 44 | type boolFlag interface { 45 | Value 46 | IsBoolFlag() bool 47 | } 48 | 49 | // Optional interface for arguments that cumulatively consume all remaining 50 | // input. 51 | type remainderArg interface { 52 | Value 53 | IsCumulative() bool 54 | } 55 | 56 | // Optional interface for flags that can be repeated. 57 | type repeatableFlag interface { 58 | Value 59 | IsCumulative() bool 60 | } 61 | 62 | type accumulator struct { 63 | element func(value interface{}) Value 64 | typ reflect.Type 65 | slice reflect.Value 66 | } 67 | 68 | // Use reflection to accumulate values into a slice. 69 | // 70 | // target := []string{} 71 | // newAccumulator(&target, func (value interface{}) Value { 72 | // return newStringValue(value.(*string)) 73 | // }) 74 | func newAccumulator(slice interface{}, element func(value interface{}) Value) *accumulator { 75 | typ := reflect.TypeOf(slice) 76 | if typ.Kind() != reflect.Ptr || typ.Elem().Kind() != reflect.Slice { 77 | panic("expected a pointer to a slice") 78 | } 79 | return &accumulator{ 80 | element: element, 81 | typ: typ.Elem().Elem(), 82 | slice: reflect.ValueOf(slice), 83 | } 84 | } 85 | 86 | func (a *accumulator) String() string { 87 | out := []string{} 88 | s := a.slice.Elem() 89 | for i := 0; i < s.Len(); i++ { 90 | out = append(out, a.element(s.Index(i).Addr().Interface()).String()) 91 | } 92 | return strings.Join(out, ",") 93 | } 94 | 95 | func (a *accumulator) Set(value string) error { 96 | e := reflect.New(a.typ) 97 | if err := a.element(e.Interface()).Set(value); err != nil { 98 | return err 99 | } 100 | slice := reflect.Append(a.slice.Elem(), e.Elem()) 101 | a.slice.Elem().Set(slice) 102 | return nil 103 | } 104 | 105 | func (a *accumulator) Get() interface{} { 106 | return a.slice.Interface() 107 | } 108 | 109 | func (a *accumulator) IsCumulative() bool { 110 | return true 111 | } 112 | 113 | func (b *boolValue) IsBoolFlag() bool { return true } 114 | 115 | // -- time.Duration Value 116 | type durationValue time.Duration 117 | 118 | func newDurationValue(p *time.Duration) *durationValue { 119 | return (*durationValue)(p) 120 | } 121 | 122 | func (d *durationValue) Set(s string) error { 123 | v, err := time.ParseDuration(s) 124 | *d = durationValue(v) 125 | return err 126 | } 127 | 128 | func (d *durationValue) Get() interface{} { return time.Duration(*d) } 129 | 130 | func (d *durationValue) String() string { return (*time.Duration)(d).String() } 131 | 132 | // -- map[string]string Value 133 | type stringMapValue map[string]string 134 | 135 | func newStringMapValue(p *map[string]string) *stringMapValue { 136 | return (*stringMapValue)(p) 137 | } 138 | 139 | var stringMapRegex = regexp.MustCompile("[:=]") 140 | 141 | func (s *stringMapValue) Set(value string) error { 142 | parts := stringMapRegex.Split(value, 2) 143 | if len(parts) != 2 { 144 | return fmt.Errorf("expected KEY=VALUE got '%s'", value) 145 | } 146 | (*s)[parts[0]] = parts[1] 147 | return nil 148 | } 149 | 150 | func (s *stringMapValue) Get() interface{} { 151 | return (map[string]string)(*s) 152 | } 153 | 154 | func (s *stringMapValue) String() string { 155 | return fmt.Sprintf("%s", map[string]string(*s)) 156 | } 157 | 158 | func (s *stringMapValue) IsCumulative() bool { 159 | return true 160 | } 161 | 162 | // -- net.IP Value 163 | type ipValue net.IP 164 | 165 | func newIPValue(p *net.IP) *ipValue { 166 | return (*ipValue)(p) 167 | } 168 | 169 | func (i *ipValue) Set(value string) error { 170 | if ip := net.ParseIP(value); ip == nil { 171 | return fmt.Errorf("'%s' is not an IP address", value) 172 | } else { 173 | *i = *(*ipValue)(&ip) 174 | return nil 175 | } 176 | } 177 | 178 | func (i *ipValue) Get() interface{} { 179 | return (net.IP)(*i) 180 | } 181 | 182 | func (i *ipValue) String() string { 183 | return (*net.IP)(i).String() 184 | } 185 | 186 | // -- *net.TCPAddr Value 187 | type tcpAddrValue struct { 188 | addr **net.TCPAddr 189 | } 190 | 191 | func newTCPAddrValue(p **net.TCPAddr) *tcpAddrValue { 192 | return &tcpAddrValue{p} 193 | } 194 | 195 | func (i *tcpAddrValue) Set(value string) error { 196 | if addr, err := net.ResolveTCPAddr("tcp", value); err != nil { 197 | return fmt.Errorf("'%s' is not a valid TCP address: %s", value, err) 198 | } else { 199 | *i.addr = addr 200 | return nil 201 | } 202 | } 203 | 204 | func (t *tcpAddrValue) Get() interface{} { 205 | return (*net.TCPAddr)(*t.addr) 206 | } 207 | 208 | func (i *tcpAddrValue) String() string { 209 | return (*i.addr).String() 210 | } 211 | 212 | // -- existingFile Value 213 | 214 | type fileStatValue struct { 215 | path *string 216 | predicate func(os.FileInfo) error 217 | } 218 | 219 | func newFileStatValue(p *string, predicate func(os.FileInfo) error) *fileStatValue { 220 | return &fileStatValue{ 221 | path: p, 222 | predicate: predicate, 223 | } 224 | } 225 | 226 | func (e *fileStatValue) Set(value string) error { 227 | if s, err := os.Stat(value); os.IsNotExist(err) { 228 | return fmt.Errorf("path '%s' does not exist", value) 229 | } else if err != nil { 230 | return err 231 | } else if err := e.predicate(s); err != nil { 232 | return err 233 | } 234 | *e.path = value 235 | return nil 236 | } 237 | 238 | func (f *fileStatValue) Get() interface{} { 239 | return (string)(*f.path) 240 | } 241 | 242 | func (e *fileStatValue) String() string { 243 | return *e.path 244 | } 245 | 246 | // -- os.File value 247 | 248 | type fileValue struct { 249 | f **os.File 250 | flag int 251 | perm os.FileMode 252 | } 253 | 254 | func newFileValue(p **os.File, flag int, perm os.FileMode) *fileValue { 255 | return &fileValue{p, flag, perm} 256 | } 257 | 258 | func (f *fileValue) Set(value string) error { 259 | if fd, err := os.OpenFile(value, f.flag, f.perm); err != nil { 260 | return err 261 | } else { 262 | *f.f = fd 263 | return nil 264 | } 265 | } 266 | 267 | func (f *fileValue) Get() interface{} { 268 | return (*os.File)(*f.f) 269 | } 270 | 271 | func (f *fileValue) String() string { 272 | if *f.f == nil { 273 | return "" 274 | } 275 | return (*f.f).Name() 276 | } 277 | 278 | // -- url.URL Value 279 | type urlValue struct { 280 | u **url.URL 281 | } 282 | 283 | func newURLValue(p **url.URL) *urlValue { 284 | return &urlValue{p} 285 | } 286 | 287 | func (u *urlValue) Set(value string) error { 288 | if url, err := url.Parse(value); err != nil { 289 | return fmt.Errorf("invalid URL: %s", err) 290 | } else { 291 | *u.u = url 292 | return nil 293 | } 294 | } 295 | 296 | func (u *urlValue) Get() interface{} { 297 | return (*url.URL)(*u.u) 298 | } 299 | 300 | func (u *urlValue) String() string { 301 | if *u.u == nil { 302 | return "" 303 | } 304 | return (*u.u).String() 305 | } 306 | 307 | // -- []*url.URL Value 308 | type urlListValue []*url.URL 309 | 310 | func newURLListValue(p *[]*url.URL) *urlListValue { 311 | return (*urlListValue)(p) 312 | } 313 | 314 | func (u *urlListValue) Set(value string) error { 315 | if url, err := url.Parse(value); err != nil { 316 | return fmt.Errorf("invalid URL: %s", err) 317 | } else { 318 | *u = append(*u, url) 319 | return nil 320 | } 321 | } 322 | 323 | func (u *urlListValue) Get() interface{} { 324 | return ([]*url.URL)(*u) 325 | } 326 | 327 | func (u *urlListValue) String() string { 328 | out := []string{} 329 | for _, url := range *u { 330 | out = append(out, url.String()) 331 | } 332 | return strings.Join(out, ",") 333 | } 334 | 335 | func (u *urlListValue) IsCumulative() bool { 336 | return true 337 | } 338 | 339 | // A flag whose value must be in a set of options. 340 | type enumValue struct { 341 | value *string 342 | options []string 343 | } 344 | 345 | func newEnumFlag(target *string, options ...string) *enumValue { 346 | return &enumValue{ 347 | value: target, 348 | options: options, 349 | } 350 | } 351 | 352 | func (a *enumValue) String() string { 353 | return *a.value 354 | } 355 | 356 | func (a *enumValue) Set(value string) error { 357 | for _, v := range a.options { 358 | if v == value { 359 | *a.value = value 360 | return nil 361 | } 362 | } 363 | return fmt.Errorf("enum value must be one of %s, got '%s'", strings.Join(a.options, ","), value) 364 | } 365 | 366 | func (e *enumValue) Get() interface{} { 367 | return (string)(*e.value) 368 | } 369 | 370 | // -- []string Enum Value 371 | type enumsValue struct { 372 | value *[]string 373 | options []string 374 | } 375 | 376 | func newEnumsFlag(target *[]string, options ...string) *enumsValue { 377 | return &enumsValue{ 378 | value: target, 379 | options: options, 380 | } 381 | } 382 | 383 | func (s *enumsValue) Set(value string) error { 384 | for _, v := range s.options { 385 | if v == value { 386 | *s.value = append(*s.value, value) 387 | return nil 388 | } 389 | } 390 | return fmt.Errorf("enum value must be one of %s, got '%s'", strings.Join(s.options, ","), value) 391 | } 392 | 393 | func (e *enumsValue) Get() interface{} { 394 | return ([]string)(*e.value) 395 | } 396 | 397 | func (s *enumsValue) String() string { 398 | return strings.Join(*s.value, ",") 399 | } 400 | 401 | func (s *enumsValue) IsCumulative() bool { 402 | return true 403 | } 404 | 405 | // -- units.Base2Bytes Value 406 | type bytesValue units.Base2Bytes 407 | 408 | func newBytesValue(p *units.Base2Bytes) *bytesValue { 409 | return (*bytesValue)(p) 410 | } 411 | 412 | func (d *bytesValue) Set(s string) error { 413 | v, err := units.ParseBase2Bytes(s) 414 | *d = bytesValue(v) 415 | return err 416 | } 417 | 418 | func (d *bytesValue) Get() interface{} { return units.Base2Bytes(*d) } 419 | 420 | func (d *bytesValue) String() string { return (*units.Base2Bytes)(d).String() } 421 | 422 | func newExistingFileValue(target *string) *fileStatValue { 423 | return newFileStatValue(target, func(s os.FileInfo) error { 424 | if s.IsDir() { 425 | return fmt.Errorf("'%s' is a directory", s.Name()) 426 | } 427 | return nil 428 | }) 429 | } 430 | 431 | func newExistingDirValue(target *string) *fileStatValue { 432 | return newFileStatValue(target, func(s os.FileInfo) error { 433 | if !s.IsDir() { 434 | return fmt.Errorf("'%s' is a file", s.Name()) 435 | } 436 | return nil 437 | }) 438 | } 439 | 440 | func newExistingFileOrDirValue(target *string) *fileStatValue { 441 | return newFileStatValue(target, func(s os.FileInfo) error { return nil }) 442 | } 443 | 444 | type counterValue int 445 | 446 | func newCounterValue(n *int) *counterValue { 447 | return (*counterValue)(n) 448 | } 449 | 450 | func (c *counterValue) Set(s string) error { 451 | *c++ 452 | return nil 453 | } 454 | 455 | func (c *counterValue) Get() interface{} { return (int)(*c) } 456 | func (c *counterValue) IsBoolFlag() bool { return true } 457 | func (c *counterValue) String() string { return fmt.Sprintf("%d", *c) } 458 | func (c *counterValue) IsCumulative() bool { return true } 459 | 460 | func resolveHost(value string) (net.IP, error) { 461 | if ip := net.ParseIP(value); ip != nil { 462 | return ip, nil 463 | } else { 464 | if addr, err := net.ResolveIPAddr("ip", value); err != nil { 465 | return nil, err 466 | } else { 467 | return addr.IP, nil 468 | } 469 | } 470 | } 471 | -------------------------------------------------------------------------------- /vendor/gopkg.in/alecthomas/kingpin.v2/values.json: -------------------------------------------------------------------------------- 1 | [ 2 | {"type": "bool", "parser": "strconv.ParseBool(s)"}, 3 | {"type": "string", "parser": "s, error(nil)", "format": "string(*f.v)", "plural": "Strings"}, 4 | {"type": "uint", "parser": "strconv.ParseUint(s, 0, 64)", "plural": "Uints"}, 5 | {"type": "uint8", "parser": "strconv.ParseUint(s, 0, 8)"}, 6 | {"type": "uint16", "parser": "strconv.ParseUint(s, 0, 16)"}, 7 | {"type": "uint32", "parser": "strconv.ParseUint(s, 0, 32)"}, 8 | {"type": "uint64", "parser": "strconv.ParseUint(s, 0, 64)"}, 9 | {"type": "int", "parser": "strconv.ParseFloat(s, 64)", "plural": "Ints"}, 10 | {"type": "int8", "parser": "strconv.ParseInt(s, 0, 8)"}, 11 | {"type": "int16", "parser": "strconv.ParseInt(s, 0, 16)"}, 12 | {"type": "int32", "parser": "strconv.ParseInt(s, 0, 32)"}, 13 | {"type": "int64", "parser": "strconv.ParseInt(s, 0, 64)"}, 14 | {"type": "float64", "parser": "strconv.ParseFloat(s, 64)"}, 15 | {"type": "float32", "parser": "strconv.ParseFloat(s, 32)"}, 16 | {"name": "Duration", "type": "time.Duration", "no_value_parser": true}, 17 | {"name": "IP", "type": "net.IP", "no_value_parser": true}, 18 | {"name": "TCPAddr", "Type": "*net.TCPAddr", "plural": "TCPList", "no_value_parser": true}, 19 | {"name": "ExistingFile", "Type": "string", "plural": "ExistingFiles", "no_value_parser": true}, 20 | {"name": "ExistingDir", "Type": "string", "plural": "ExistingDirs", "no_value_parser": true}, 21 | {"name": "ExistingFileOrDir", "Type": "string", "plural": "ExistingFilesOrDirs", "no_value_parser": true}, 22 | {"name": "Regexp", "Type": "*regexp.Regexp", "parser": "regexp.Compile(s)"}, 23 | {"name": "ResolvedIP", "Type": "net.IP", "parser": "resolveHost(s)", "help": "Resolve a hostname or IP to an IP."}, 24 | {"name": "HexBytes", "Type": "[]byte", "parser": "hex.DecodeString(s)", "help": "Bytes as a hex string."} 25 | ] 26 | -------------------------------------------------------------------------------- /vendor/gopkg.in/alecthomas/kingpin.v2/values_test.go: -------------------------------------------------------------------------------- 1 | package kingpin 2 | 3 | import ( 4 | "net" 5 | 6 | "github.com/alecthomas/assert" 7 | 8 | "testing" 9 | ) 10 | 11 | func TestAccumulatorStrings(t *testing.T) { 12 | target := []string{} 13 | acc := newAccumulator(&target, func(v interface{}) Value { return newStringValue(v.(*string)) }) 14 | acc.Set("a") 15 | assert.Equal(t, []string{"a"}, target) 16 | acc.Set("b") 17 | assert.Equal(t, []string{"a", "b"}, target) 18 | } 19 | 20 | func TestStrings(t *testing.T) { 21 | app := New("", "") 22 | app.Arg("a", "").Required().String() 23 | app.Arg("b", "").Required().String() 24 | c := app.Arg("c", "").Required().Strings() 25 | app.Parse([]string{"a", "b", "a", "b"}) 26 | assert.Equal(t, []string{"a", "b"}, *c) 27 | } 28 | 29 | func TestEnum(t *testing.T) { 30 | app := New("", "") 31 | a := app.Arg("a", "").Enum("one", "two", "three") 32 | _, err := app.Parse([]string{"moo"}) 33 | assert.Error(t, err) 34 | _, err = app.Parse([]string{"one"}) 35 | assert.NoError(t, err) 36 | assert.Equal(t, "one", *a) 37 | } 38 | 39 | func TestEnumVar(t *testing.T) { 40 | app := New("", "") 41 | var a string 42 | app.Arg("a", "").EnumVar(&a, "one", "two", "three") 43 | _, err := app.Parse([]string{"moo"}) 44 | assert.Error(t, err) 45 | _, err = app.Parse([]string{"one"}) 46 | assert.NoError(t, err) 47 | assert.Equal(t, "one", a) 48 | } 49 | 50 | func TestCounter(t *testing.T) { 51 | app := New("", "") 52 | c := app.Flag("f", "").Counter() 53 | _, err := app.Parse([]string{"--f", "--f", "--f"}) 54 | assert.NoError(t, err) 55 | assert.Equal(t, 3, *c) 56 | } 57 | 58 | func TestIPv4Addr(t *testing.T) { 59 | app := newTestApp() 60 | flag := app.Flag("addr", "").ResolvedIP() 61 | _, err := app.Parse([]string{"--addr", net.IPv4(1, 2, 3, 4).String()}) 62 | assert.NoError(t, err) 63 | assert.NotNil(t, *flag) 64 | assert.Equal(t, net.IPv4(1, 2, 3, 4), *flag) 65 | } 66 | 67 | func TestInvalidIPv4Addr(t *testing.T) { 68 | app := newTestApp() 69 | app.Flag("addr", "").ResolvedIP() 70 | _, err := app.Parse([]string{"--addr", "1.2.3.256"}) 71 | assert.Error(t, err) 72 | } 73 | 74 | func TestIPv6Addr(t *testing.T) { 75 | app := newTestApp() 76 | flag := app.Flag("addr", "").ResolvedIP() 77 | _, err := app.Parse([]string{"--addr", net.IPv6interfacelocalallnodes.String()}) 78 | assert.NoError(t, err) 79 | assert.NotNil(t, *flag) 80 | assert.Equal(t, net.IPv6interfacelocalallnodes, *flag) 81 | } 82 | 83 | func TestHexBytes(t *testing.T) { 84 | app := newTestApp() 85 | actual := app.Arg("bytes", "").HexBytes() 86 | _, err := app.Parse([]string{"01020aff"}) 87 | assert.NoError(t, err) 88 | assert.Equal(t, []byte{0x01, 0x02, 0x0a, 0xff}, *actual) 89 | } 90 | 91 | func TestSetValueDoesNotReset(t *testing.T) { 92 | app := newTestApp() 93 | mapping := map[string]string{ 94 | "key": "value", 95 | } 96 | app.Flag("set", "").StringMapVar(&mapping) 97 | assert.NotEmpty(t, mapping) 98 | } 99 | --------------------------------------------------------------------------------