├── .gitignore ├── _example ├── Tarfile ├── .heroku.json └── hello.go ├── dist ├── Tarfile.linux-amd64 └── Tarfile.darwin-amd64 ├── vendor ├── gopkg.in │ └── alecthomas │ │ └── kingpin.v2 │ │ ├── guesswidth.go │ │ ├── guesswidth_unix.go │ │ ├── completions.go │ │ ├── COPYING │ │ ├── actions.go │ │ ├── values.json │ │ ├── doc.go │ │ ├── args.go │ │ ├── global.go │ │ ├── model.go │ │ ├── parsers.go │ │ ├── usage.go │ │ ├── cmd.go │ │ ├── templates.go │ │ ├── flags.go │ │ ├── parser.go │ │ ├── values.go │ │ └── app.go ├── github.com │ └── alecthomas │ │ ├── units │ │ ├── README.md │ │ ├── doc.go │ │ ├── si.go │ │ ├── COPYING │ │ ├── bytes.go │ │ └── util.go │ │ └── template │ │ ├── README.md │ │ ├── LICENSE │ │ ├── helper.go │ │ ├── template.go │ │ ├── parse │ │ ├── lex.go │ │ └── parse.go │ │ ├── doc.go │ │ └── funcs.go ├── vendor.json └── limbo.services │ └── version │ ├── wercker.yml │ └── version.go ├── README.md ├── pkg └── xheroku │ ├── release.go │ └── slug.go ├── cmd └── x-heroku │ └── main.go └── wercker.yml /.gitignore: -------------------------------------------------------------------------------- 1 | /_example/hello 2 | -------------------------------------------------------------------------------- /_example/Tarfile: -------------------------------------------------------------------------------- 1 | COPY hello bin/hello 2 | COPY .heroku.json .heroku.json 3 | -------------------------------------------------------------------------------- /_example/.heroku.json: -------------------------------------------------------------------------------- 1 | { 2 | "process_types": { 3 | "web": "bin/hello" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /dist/Tarfile.linux-amd64: -------------------------------------------------------------------------------- 1 | COPY bin/x-heroku-linux-amd64/x-heroku . 2 | CHMOD 0755 x-heroku 3 | -------------------------------------------------------------------------------- /dist/Tarfile.darwin-amd64: -------------------------------------------------------------------------------- 1 | COPY bin/x-heroku-darwin-amd64/x-heroku . 2 | CHMOD 0755 x-heroku 3 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /_example/hello.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "io" 5 | "net/http" 6 | "os" 7 | ) 8 | 9 | func main() { 10 | http.ListenAndServe(":"+os.Getenv("PORT"), http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 11 | io.WriteString(w, "Hello world\n") 12 | })) 13 | } 14 | -------------------------------------------------------------------------------- /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/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/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/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/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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # heroku-utils 2 | 3 | 4 | # `x-heroku release ` 5 | 6 | This command makes and uploads a new heroku slug from a tar file after which the slug will be released as 7 | a new version of the app. 8 | 9 | The input tar file MUST contain a `.heroku.json` file and all application assets/executables. 10 | 11 | ``` 12 | -rw-r--r-- 0 root root 52 Feb 1 1988 .heroku.json 13 | drwxr-xr-x 0 root root 0 Feb 1 1988 bin/ 14 | -rwxr-xr-x 0 root root 7601672 Feb 1 1988 bin/hello 15 | ``` 16 | 17 | The content of the `.heroku.json` file must match the following example: 18 | 19 | ```json5 20 | { 21 | // required 22 | "process_types": { 23 | "web": "bin/hello" 24 | // ... 25 | }, 26 | 27 | // optional 28 | "commit": "71763e53638e91d778ceda3de420cc3a7473b0e4", 29 | "commit_description": "Commit message or description", 30 | "buildpack_provided_description": "Golang app", 31 | "stack": "cedar-14" 32 | } 33 | ``` 34 | 35 | 36 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /pkg/xheroku/release.go: -------------------------------------------------------------------------------- 1 | package xheroku 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "fmt" 7 | "io" 8 | "io/ioutil" 9 | "net/http" 10 | "os" 11 | ) 12 | 13 | type releaseOptions struct { 14 | Slug string `json:"slug"` 15 | } 16 | 17 | func CreateRelease(appID, slugID string) error { 18 | options := releaseOptions{ 19 | Slug: slugID, 20 | } 21 | 22 | payload, err := json.Marshal(&options) 23 | if err != nil { 24 | return err 25 | } 26 | 27 | req, err := http.NewRequest("POST", "https://api.heroku.com/apps/"+appID+"/releases", bytes.NewReader(payload)) 28 | if err != nil { 29 | return err 30 | } 31 | 32 | req.Header.Set("Content-Type", "application/json") 33 | req.Header.Set("Accept", "application/vnd.heroku+json; version=3") 34 | req.Header.Set("Authorization", "Bearer "+os.Getenv("HEROKU_API_KEY")) 35 | 36 | res, err := http.DefaultClient.Do(req) 37 | if err != nil { 38 | return err 39 | } 40 | 41 | defer res.Body.Close() 42 | defer io.Copy(ioutil.Discard, res.Body) 43 | 44 | if res.StatusCode/100 != 2 { 45 | return fmt.Errorf("unexpected status code: %d", res.StatusCode) 46 | } 47 | 48 | return nil 49 | } 50 | -------------------------------------------------------------------------------- /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/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/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/vendor.json: -------------------------------------------------------------------------------- 1 | { 2 | "comment": "", 3 | "ignore": "test", 4 | "package": [ 5 | { 6 | "checksumSHA1": "KmjnydoAbofMieIWm+it5OWERaM=", 7 | "path": "github.com/alecthomas/template", 8 | "revision": "a0175ee3bccc567396460bf5acd36800cb10c49c", 9 | "revisionTime": "2016-04-05T07:15:01Z" 10 | }, 11 | { 12 | "checksumSHA1": "3wt0pTXXeS+S93unwhGoLIyGX/Q=", 13 | "path": "github.com/alecthomas/template/parse", 14 | "revision": "a0175ee3bccc567396460bf5acd36800cb10c49c", 15 | "revisionTime": "2016-04-05T07:15:01Z" 16 | }, 17 | { 18 | "checksumSHA1": "fCc3grA7vIxfBru7R3SqjcW+oLI=", 19 | "path": "github.com/alecthomas/units", 20 | "revision": "2efee857e7cfd4f3d0138cc3cbb1b4966962b93a", 21 | "revisionTime": "2015-10-22T06:55:26Z" 22 | }, 23 | { 24 | "checksumSHA1": "QPs8F/aqKAAQJr8dLc3CVQ5SxlM=", 25 | "path": "gopkg.in/alecthomas/kingpin.v2", 26 | "revision": "8cccfa8eb2e3183254457fb1749b2667fbc364c7", 27 | "revisionTime": "2016-02-17T09:03:01Z" 28 | }, 29 | { 30 | "checksumSHA1": "+j9wYJlEH56k6As5ePXnsR/9arY=", 31 | "path": "limbo.services/version", 32 | "revision": "3a530609b189351315be57f0a17935dd96232759", 33 | "revisionTime": "2016-05-04T19:26:10Z" 34 | } 35 | ], 36 | "rootPath": "github.com/fd/heroku-utils" 37 | } 38 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /cmd/x-heroku/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io" 7 | "io/ioutil" 8 | "os" 9 | 10 | "github.com/fd/heroku-utils/pkg/xheroku" 11 | "gopkg.in/alecthomas/kingpin.v2" 12 | "limbo.services/version" 13 | ) 14 | 15 | func main() { 16 | err := run() 17 | if err != nil { 18 | fmt.Fprintf(os.Stderr, "error: %s\n", err) 19 | os.Exit(1) 20 | } 21 | } 22 | 23 | func run() error { 24 | var ( 25 | inputTar string 26 | appName string 27 | ) 28 | 29 | app := kingpin.New("x-heroku", "Heroku utilities").Version(version.Get().String()).Author(version.Get().ReleasedBy) 30 | 31 | releaseCmd := app.Command("release", "Release an app") 32 | releaseCmd.Flag("input", "Tar archive to use").Short('i').Default("-").PlaceHolder("FILE").StringVar(&inputTar) 33 | releaseCmd.Arg("application", "Name of the application").Required().StringVar(&appName) 34 | 35 | switch kingpin.MustParse(app.Parse(os.Args[1:])) { 36 | 37 | case releaseCmd.FullCommand(): 38 | r, err := openStream(inputTar) 39 | if err != nil { 40 | return err 41 | } 42 | 43 | slugID, err := xheroku.CreateSlug(r, appName) 44 | if err != nil { 45 | return err 46 | } 47 | 48 | err = xheroku.CreateRelease(appName, slugID) 49 | if err != nil { 50 | return err 51 | } 52 | 53 | } 54 | 55 | return nil 56 | } 57 | 58 | const stdio = "-" 59 | 60 | func openStream(name string) (io.Reader, error) { 61 | if name == stdio { 62 | return os.Stdin, nil 63 | } 64 | return os.Open(name) 65 | } 66 | 67 | func putStream(name string, buf *bytes.Buffer) error { 68 | if name == stdio { 69 | _, err := io.Copy(os.Stdout, buf) 70 | return err 71 | } 72 | return ioutil.WriteFile(name, buf.Bytes(), 0644) 73 | } 74 | -------------------------------------------------------------------------------- /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/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.v1" 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/limbo.services/version/wercker.yml: -------------------------------------------------------------------------------- 1 | box: simonmenke/go-build:latest 2 | 3 | build: 4 | steps: 5 | - setup-go-workspace: 6 | package-dir: limbo.services/version 7 | 8 | - script: 9 | name: "go install" 10 | code: | 11 | CGO_ENABLED=0 go install -v ./cmd/v5n . 12 | 13 | - script: 14 | name: "go test" 15 | code: | 16 | go test -v ./cmd/v5n . 17 | 18 | - script: 19 | name: "go build" 20 | code: | 21 | CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o ${WERCKER_OUTPUT_DIR}/bin/v5n-linux-amd64/v5n ./cmd/v5n 22 | CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -o ${WERCKER_OUTPUT_DIR}/bin/v5n-darwin-amd64/v5n ./cmd/v5n 23 | 24 | deploy: 25 | steps: 26 | - script: 27 | name: "Pull secrets" 28 | code: | 29 | curl -# https://gist.githubusercontent.com/fd/354eed3ebf924565741f0df980d11741/raw/sec.pem | sec dec | tar -C $HOME -x 30 | eval "$(install-creds)" 31 | - script: 32 | name: "Push release" 33 | code: | 34 | mkdir -p dist 35 | if [[ "$WERCKER_DEPLOYTARGET_NAME" == "prerelease" ]]; then 36 | VERSION="$(v5n store bump v5n rc)" 37 | v5n apply --commit $WERCKER_GIT_COMMIT --author "Simon Menke" bin/v5n-linux-amd64/v5n "$VERSION" 38 | v5n apply --commit $WERCKER_GIT_COMMIT --author "Simon Menke" bin/v5n-darwin-amd64/v5n "$VERSION" 39 | tar -C bin/v5n-linux-amd64 -f dist/v5n-linux-amd64.tar.gz -cz . 40 | tar -C bin/v5n-darwin-amd64 -f dist/v5n-darwin-amd64.tar.gz -cz . 41 | ghr -u limbo-services -r version -c $WERCKER_GIT_COMMIT --replace --prerelease "v$VERSION" ./dist 42 | else 43 | VERSION="$(v5n store bump v5n final+patch)" 44 | v5n apply --commit $WERCKER_GIT_COMMIT --author "Simon Menke" bin/v5n-linux-amd64/v5n "$VERSION" 45 | v5n apply --commit $WERCKER_GIT_COMMIT --author "Simon Menke" bin/v5n-darwin-amd64/v5n "$VERSION" 46 | tar -C bin/v5n-linux-amd64 -f dist/v5n-linux-amd64.tar.gz -cz . 47 | tar -C bin/v5n-darwin-amd64 -f dist/v5n-darwin-amd64.tar.gz -cz . 48 | ghr -u limbo-services -r version -c $WERCKER_GIT_COMMIT --replace "v$VERSION" ./dist 49 | fi 50 | - mbrevda/wercker-triggerbuild@0.0.10: 51 | token: $WRK_TOKEN 52 | application_id: $WRK_APP 53 | message: Triggered from v5n 54 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /wercker.yml: -------------------------------------------------------------------------------- 1 | box: simonmenke/go-build:latest 2 | 3 | build: 4 | steps: 5 | - setup-go-workspace 6 | 7 | - script: 8 | name: "go install" 9 | code: | 10 | CGO_ENABLED=0 go install -v ./cmd/... 11 | 12 | - script: 13 | name: "go test" 14 | code: | 15 | go test -v ./pkg/... 16 | 17 | - script: 18 | name: "go build" 19 | code: | 20 | cp -r dist/* ${WERCKER_OUTPUT_DIR}/ 21 | CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o ${WERCKER_OUTPUT_DIR}/bin/x-heroku-linux-amd64/x-heroku ./cmd/x-heroku 22 | CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -o ${WERCKER_OUTPUT_DIR}/bin/x-heroku-darwin-amd64/x-heroku ./cmd/x-heroku 23 | 24 | prerelease: 25 | steps: 26 | - script: 27 | name: "Pull secrets" 28 | code: | 29 | curl -# https://gist.githubusercontent.com/fd/354eed3ebf924565741f0df980d11741/raw/sec.pem | sec dec | tar -C $HOME -x 30 | eval "$(install-creds)" 31 | - script: 32 | name: "Push release" 33 | code: | 34 | mkdir -p dist 35 | VERSION="$(v5n store bump x-heroku rc)" 36 | v5n apply --commit $WERCKER_GIT_COMMIT --author "Simon Menke" bin/x-heroku-linux-amd64/x-heroku "$VERSION" 37 | v5n apply --commit $WERCKER_GIT_COMMIT --author "Simon Menke" bin/x-heroku-darwin-amd64/x-heroku "$VERSION" 38 | x-tar build -t Tarfile.linux-amd64 | gzip > dist/x-heroku-linux-amd64.tar.gz 39 | x-tar build -t Tarfile.darwin-amd64 | gzip > dist/x-heroku-darwin-amd64.tar.gz 40 | ghr -u fd -r heroku-utils -c $WERCKER_GIT_COMMIT --replace --prerelease "v$VERSION" ./dist 41 | - mbrevda/wercker-triggerbuild@0.0.10: 42 | token: $WRK_TOKEN 43 | application_id: $WRK_APP 44 | message: Triggered from x-heroku 45 | 46 | stable: 47 | steps: 48 | - script: 49 | name: "Pull secrets" 50 | code: | 51 | curl -# https://gist.githubusercontent.com/fd/354eed3ebf924565741f0df980d11741/raw/sec.pem | sec dec | tar -C $HOME -x 52 | eval "$(install-creds)" 53 | - script: 54 | name: "Push release" 55 | code: | 56 | mkdir -p dist 57 | VERSION="$(v5n store bump x-heroku final+patch)" 58 | v5n apply --commit $WERCKER_GIT_COMMIT --author "Simon Menke" bin/x-heroku-linux-amd64/x-heroku "$VERSION" 59 | v5n apply --commit $WERCKER_GIT_COMMIT --author "Simon Menke" bin/x-heroku-darwin-amd64/x-heroku "$VERSION" 60 | x-tar build -t Tarfile.linux-amd64 | gzip > dist/x-heroku-linux-amd64.tar.gz 61 | x-tar build -t Tarfile.darwin-amd64 | gzip > dist/x-heroku-darwin-amd64.tar.gz 62 | ghr -u fd -r heroku-utils -c $WERCKER_GIT_COMMIT --replace "v$VERSION" ./dist 63 | - mbrevda/wercker-triggerbuild@0.0.10: 64 | token: $WRK_TOKEN 65 | application_id: $WRK_APP 66 | message: Triggered from x-heroku 67 | -------------------------------------------------------------------------------- /vendor/gopkg.in/alecthomas/kingpin.v2/args.go: -------------------------------------------------------------------------------- 1 | package kingpin 2 | 3 | import "fmt" 4 | 5 | type argGroup struct { 6 | args []*ArgClause 7 | } 8 | 9 | func newArgGroup() *argGroup { 10 | return &argGroup{} 11 | } 12 | 13 | func (a *argGroup) have() bool { 14 | return len(a.args) > 0 15 | } 16 | 17 | // GetArg gets an argument definition. 18 | // 19 | // This allows existing arguments to be modified after definition but before parsing. Useful for 20 | // modular applications. 21 | func (a *argGroup) GetArg(name string) *ArgClause { 22 | for _, arg := range a.args { 23 | if arg.name == name { 24 | return arg 25 | } 26 | } 27 | return nil 28 | } 29 | 30 | func (a *argGroup) Arg(name, help string) *ArgClause { 31 | arg := newArg(name, help) 32 | a.args = append(a.args, arg) 33 | return arg 34 | } 35 | 36 | func (a *argGroup) init() error { 37 | required := 0 38 | seen := map[string]struct{}{} 39 | previousArgMustBeLast := false 40 | for i, arg := range a.args { 41 | if previousArgMustBeLast { 42 | return fmt.Errorf("Args() can't be followed by another argument '%s'", arg.name) 43 | } 44 | if arg.consumesRemainder() { 45 | previousArgMustBeLast = true 46 | } 47 | if _, ok := seen[arg.name]; ok { 48 | return fmt.Errorf("duplicate argument '%s'", arg.name) 49 | } 50 | seen[arg.name] = struct{}{} 51 | if arg.required && required != i { 52 | return fmt.Errorf("required arguments found after non-required") 53 | } 54 | if arg.required { 55 | required++ 56 | } 57 | if err := arg.init(); err != nil { 58 | return err 59 | } 60 | } 61 | return nil 62 | } 63 | 64 | type ArgClause struct { 65 | actionMixin 66 | parserMixin 67 | name string 68 | help string 69 | defaultValues []string 70 | required bool 71 | } 72 | 73 | func newArg(name, help string) *ArgClause { 74 | a := &ArgClause{ 75 | name: name, 76 | help: help, 77 | } 78 | return a 79 | } 80 | 81 | func (a *ArgClause) consumesRemainder() bool { 82 | if r, ok := a.value.(remainderArg); ok { 83 | return r.IsCumulative() 84 | } 85 | return false 86 | } 87 | 88 | // Required arguments must be input by the user. They can not have a Default() value provided. 89 | func (a *ArgClause) Required() *ArgClause { 90 | a.required = true 91 | return a 92 | } 93 | 94 | // Default values for this argument. They *must* be parseable by the value of the argument. 95 | func (a *ArgClause) Default(values ...string) *ArgClause { 96 | a.defaultValues = values 97 | return a 98 | } 99 | 100 | func (a *ArgClause) Action(action Action) *ArgClause { 101 | a.addAction(action) 102 | return a 103 | } 104 | 105 | func (a *ArgClause) PreAction(action Action) *ArgClause { 106 | a.addPreAction(action) 107 | return a 108 | } 109 | 110 | func (a *ArgClause) init() error { 111 | if a.required && len(a.defaultValues) > 0 { 112 | return fmt.Errorf("required argument '%s' with unusable default value", a.name) 113 | } 114 | if a.value == nil { 115 | return fmt.Errorf("no parser defined for arg '%s'", a.name) 116 | } 117 | return nil 118 | } 119 | -------------------------------------------------------------------------------- /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/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/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/limbo.services/version/version.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "fmt" 7 | "regexp" 8 | "strconv" 9 | "strings" 10 | "time" 11 | ) 12 | 13 | const payloadData = "e927cc4876808ab86054e3489a04efd20bc9cf9f3fe2356e56b1274aa8ff4fc0dfa8f97203153fb75a3e6f274c84094b5b20a0306943e121ce818b5af8333c9ebaf084abf27f78effaf7ea1c36ed89bdf8ff8a369da01388206d987a52ed22cb29fa600d61da0772c5822499337bc8ad8655ebe185bfff5c4eaba1d4de5a577863ed661607379003b94374dd85b0c35e24dcfc3dabb0147607582c7402a782be5fc0a19b7a92fb0c91599ed5dfe25cf180bc675cef87ccd1f79ba86c72768ab7862831bfaac0da54bca6166c78dbc558e2e324e5f85ee22156901b0e82c2ac9d2acf29ed11ae86852a57c3c53ef75d292d0c5d21cf1deb7e5fb1bd641fb46a97718f7983260b9415eb0b240731de0359bf1e3764954cfb94277bdf972b13eaa6a38c0e3bb0be58fa850857b774f325e336dcf2550644ecaa1edcdea3b44e7632c5ae7d723d4e8692c04e1d1a9fa64cdce23082a9032f34027a994ac6a13ddc7b9d3204350908fe1567bff31ece702051446e42e8ea7dfed88f88ad42de1b0effb19ccd1da94462d17411edc6fb510175912ea455bc7387e2040cbf0cd79ef4baae27375fae38f5351f5cf4ebd540d7d560eb7cbe8d6aa2e040faa0a2c00f8a32759f5a1bafee6ba690192b64bee612dfe6e142d3ff53854adb91e2da8f86f0a58685d12b832e083baf61ad80f6a353224e16bc7693585e2894147b8286985032" 14 | 15 | var payload = Info{PreRelease: []string{"dev"}} 16 | 17 | func init() { 18 | if strings.HasPrefix(payloadData, "e927cc4") { 19 | // payload is unset 20 | return 21 | } 22 | 23 | var info Info 24 | 25 | err := json.Unmarshal([]byte(payloadData), &info) 26 | if err != nil { 27 | return 28 | } 29 | 30 | payload = info 31 | } 32 | 33 | // Get version info 34 | func Get() *Info { 35 | return payload.clone() 36 | } 37 | 38 | // Info holds all version info 39 | type Info struct { 40 | // Major.Minor.Patch-Pre+Extra 41 | Major int 42 | Minor int 43 | Patch int 44 | PreRelease []string 45 | Metadata []string 46 | Released time.Time 47 | ReleasedBy string 48 | Commit string 49 | } 50 | 51 | func (i *Info) clone() *Info { 52 | c := &Info{} 53 | *c = *i 54 | c.PreRelease = nil 55 | c.Metadata = nil 56 | c.PreRelease = append(c.PreRelease, i.PreRelease...) 57 | c.Metadata = append(c.Metadata, i.Metadata...) 58 | return c 59 | } 60 | 61 | func (i *Info) String() string { 62 | return i.Semver() 63 | } 64 | 65 | // Semver returns the version info in semver 2 format 66 | func (i *Info) Semver() string { 67 | s := fmt.Sprintf("%d.%d.%d", i.Major, i.Minor, i.Patch) 68 | if len(i.PreRelease) > 0 { 69 | s += "-" + strings.Join(i.PreRelease, ".") 70 | } 71 | if len(i.Metadata) > 0 { 72 | s += "+" + strings.Join(i.Metadata, ".") 73 | } 74 | return s 75 | } 76 | 77 | // ParseSemver parses a semver 2 string 78 | func ParseSemver(s string) (*Info, error) { 79 | re := regexp.MustCompile(`^([0-9]+)(?:\.([0-9]+)(?:\.([0-9]+))?)?(?:[-]([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:[+]([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?$`) 80 | m := re.FindStringSubmatch(s) 81 | if m == nil { 82 | return nil, errors.New("invalid semver") 83 | } 84 | 85 | var ( 86 | major, _ = strconv.Atoi(m[1]) 87 | minor, _ = strconv.Atoi(m[2]) 88 | patch, _ = strconv.Atoi(m[3]) 89 | pre = strings.Split(m[4], ".") 90 | meta = strings.Split(m[5], ".") 91 | ) 92 | 93 | if m[4] == "" { 94 | pre = nil 95 | } 96 | if m[5] == "" { 97 | meta = nil 98 | } 99 | 100 | return &Info{ 101 | Major: major, 102 | Minor: minor, 103 | Patch: patch, 104 | PreRelease: pre, 105 | Metadata: meta, 106 | }, nil 107 | } 108 | 109 | type BumpType uint8 110 | 111 | const ( 112 | // What to bump 113 | ReleaseCandidate BumpType = 1 << iota 114 | Patch 115 | Minor 116 | Major 117 | Final 118 | ) 119 | 120 | func Bump(i *Info, typ BumpType) *Info { 121 | i = i.clone() 122 | 123 | if typ&Final > 0 { 124 | i.PreRelease = nil 125 | } 126 | if typ&Patch > 0 { 127 | i.PreRelease = nil 128 | i.Patch++ 129 | } 130 | if typ&Minor > 0 { 131 | i.PreRelease = nil 132 | i.Patch = 0 133 | i.Minor++ 134 | } 135 | if typ&Major > 0 { 136 | i.PreRelease = nil 137 | i.Patch = 0 138 | i.Minor = 0 139 | i.Major++ 140 | } 141 | if typ&ReleaseCandidate > 0 { 142 | var found bool 143 | for idx, v := range i.PreRelease { 144 | if strings.HasPrefix(v, "rc") { 145 | d, err := strconv.Atoi(v[2:]) 146 | if err != nil { 147 | continue 148 | } 149 | found = true 150 | d++ 151 | i.PreRelease[idx] = "rc" + strconv.Itoa(d) 152 | } 153 | } 154 | if !found { 155 | i.PreRelease = []string{"rc0"} 156 | } 157 | } 158 | 159 | return i 160 | } 161 | -------------------------------------------------------------------------------- /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 | Required bool 100 | Value Value 101 | } 102 | 103 | func (a *ArgModel) String() string { 104 | return a.Value.String() 105 | } 106 | 107 | type CmdGroupModel struct { 108 | Commands []*CmdModel 109 | } 110 | 111 | func (c *CmdGroupModel) FlattenedCommands() (out []*CmdModel) { 112 | for _, cmd := range c.Commands { 113 | if len(cmd.Commands) == 0 { 114 | out = append(out, cmd) 115 | } 116 | out = append(out, cmd.FlattenedCommands()...) 117 | } 118 | return 119 | } 120 | 121 | type CmdModel struct { 122 | Name string 123 | Aliases []string 124 | Help string 125 | FullCommand string 126 | Depth int 127 | Hidden bool 128 | Default bool 129 | *FlagGroupModel 130 | *ArgGroupModel 131 | *CmdGroupModel 132 | } 133 | 134 | func (c *CmdModel) String() string { 135 | return c.FullCommand 136 | } 137 | 138 | type ApplicationModel struct { 139 | Name string 140 | Help string 141 | Version string 142 | Author string 143 | *ArgGroupModel 144 | *CmdGroupModel 145 | *FlagGroupModel 146 | } 147 | 148 | func (a *Application) Model() *ApplicationModel { 149 | return &ApplicationModel{ 150 | Name: a.Name, 151 | Help: a.Help, 152 | Version: a.version, 153 | Author: a.author, 154 | FlagGroupModel: a.flagGroup.Model(), 155 | ArgGroupModel: a.argGroup.Model(), 156 | CmdGroupModel: a.cmdGroup.Model(), 157 | } 158 | } 159 | 160 | func (a *argGroup) Model() *ArgGroupModel { 161 | m := &ArgGroupModel{} 162 | for _, arg := range a.args { 163 | m.Args = append(m.Args, arg.Model()) 164 | } 165 | return m 166 | } 167 | 168 | func (a *ArgClause) Model() *ArgModel { 169 | return &ArgModel{ 170 | Name: a.name, 171 | Help: a.help, 172 | Default: a.defaultValues, 173 | Required: a.required, 174 | Value: a.value, 175 | } 176 | } 177 | 178 | func (f *flagGroup) Model() *FlagGroupModel { 179 | m := &FlagGroupModel{} 180 | for _, fl := range f.flagOrder { 181 | m.Flags = append(m.Flags, fl.Model()) 182 | } 183 | return m 184 | } 185 | 186 | func (f *FlagClause) Model() *FlagModel { 187 | return &FlagModel{ 188 | Name: f.name, 189 | Help: f.help, 190 | Short: rune(f.shorthand), 191 | Default: f.defaultValues, 192 | Envar: f.envar, 193 | PlaceHolder: f.placeholder, 194 | Required: f.required, 195 | Hidden: f.hidden, 196 | Value: f.value, 197 | } 198 | } 199 | 200 | func (c *cmdGroup) Model() *CmdGroupModel { 201 | m := &CmdGroupModel{} 202 | for _, cm := range c.commandOrder { 203 | m.Commands = append(m.Commands, cm.Model()) 204 | } 205 | return m 206 | } 207 | 208 | func (c *CmdClause) Model() *CmdModel { 209 | depth := 0 210 | for i := c; i != nil; i = i.parent { 211 | depth++ 212 | } 213 | return &CmdModel{ 214 | Name: c.name, 215 | Aliases: c.aliases, 216 | Help: c.help, 217 | Depth: depth, 218 | Hidden: c.hidden, 219 | Default: c.isDefault, 220 | FullCommand: c.FullCommand(), 221 | FlagGroupModel: c.flagGroup.Model(), 222 | ArgGroupModel: c.argGroup.Model(), 223 | CmdGroupModel: c.cmdGroup.Model(), 224 | } 225 | } 226 | -------------------------------------------------------------------------------- /pkg/xheroku/slug.go: -------------------------------------------------------------------------------- 1 | package xheroku 2 | 3 | import ( 4 | "archive/tar" 5 | "bytes" 6 | "compress/gzip" 7 | "crypto/sha256" 8 | "encoding/hex" 9 | "encoding/json" 10 | "fmt" 11 | "io" 12 | "io/ioutil" 13 | "net/http" 14 | "os" 15 | "path" 16 | "strings" 17 | "time" 18 | ) 19 | 20 | type slugInfo struct { 21 | ID string `json:"id,omitempty"` 22 | CreatedAt *time.Time `json:"created_at,omitempty"` 23 | UpdatedAt *time.Time `json:"updated_at,omitempty"` 24 | BuildpackProvidedDescription *string `json:"buildpack_provided_description,omitempty"` 25 | Checksum *string `json:"checksum,omitempty"` 26 | Commit *string `json:"commit,omitempty"` 27 | CommitDescription *string `json:"commit_description,omitempty"` 28 | ProcessTypes map[string]string `json:"process_types,omitempty"` 29 | Size int64 `json:"size,omitempty"` 30 | 31 | Stack *struct { 32 | ID string `json:"id,omitempty"` 33 | Name string `json:"name,omitempty"` 34 | } `json:"stack,omitempty"` 35 | 36 | Blob *struct { 37 | Method string `json:"method,omitempty"` 38 | URL string `json:"url,omitempty"` 39 | } `json:"blob,omitempty"` 40 | } 41 | 42 | type SlugConfig struct { 43 | Commit string `json:"commit,omitempty"` 44 | CommitDescription string `json:"commit_description,omitempty"` 45 | BuildpackProvidedDescription string `json:"buildpack_provided_description,omitempty"` 46 | ProcessTypes map[string]string `json:"process_types,omitempty"` 47 | Stack string `json:"stack,omitempty"` 48 | } 49 | 50 | type slugOptions struct { 51 | Checksum string `json:"checksum,omitempty"` 52 | Commit string `json:"commit,omitempty"` 53 | CommitDescription string `json:"commit_description,omitempty"` 54 | BuildpackProvidedDescription string `json:"buildpack_provided_description,omitempty"` 55 | ProcessTypes map[string]string `json:"process_types,omitempty"` 56 | Stack string `json:"stack,omitempty"` 57 | } 58 | 59 | func CreateSlug(r io.Reader, appID string) (string, error) { 60 | var ( 61 | buf bytes.Buffer 62 | checksum string 63 | config *SlugConfig 64 | info *slugInfo 65 | ) 66 | 67 | { 68 | tr := tar.NewReader(r) 69 | sw := sha256.New() 70 | zw := gzip.NewWriter(io.MultiWriter(&buf, sw)) 71 | tw := tar.NewWriter(zw) 72 | for { 73 | hdr, err := tr.Next() 74 | if err == io.EOF { 75 | break 76 | } 77 | if err != nil { 78 | return "", err 79 | } 80 | 81 | fmt.Fprintf(os.Stderr, "- %s\n", hdr.Name) 82 | hdr.Name = path.Join("/", hdr.Name) 83 | fmt.Fprintf(os.Stderr, " %s\n", hdr.Name) 84 | if hdr.Name == "/.heroku.json" { 85 | err = json.NewDecoder(tr).Decode(&config) 86 | if err != nil { 87 | return "", err 88 | } 89 | 90 | _, err = io.Copy(ioutil.Discard, tr) 91 | if err != nil && err != io.EOF { 92 | return "", err 93 | } 94 | 95 | continue 96 | } 97 | 98 | hdr.Name = "./app" + hdr.Name 99 | if hdr.FileInfo().IsDir() { 100 | hdr.Name = strings.TrimSuffix(hdr.Name, "/") + "/" 101 | } 102 | fmt.Fprintf(os.Stderr, " %s\n", hdr.Name) 103 | 104 | err = tw.WriteHeader(hdr) 105 | if err != nil { 106 | return "", err 107 | } 108 | 109 | _, err = io.Copy(tw, tr) 110 | if err != nil { 111 | return "", err 112 | } 113 | } 114 | 115 | err := tw.Close() 116 | if err != nil { 117 | return "", err 118 | } 119 | 120 | err = zw.Close() 121 | if err != nil { 122 | return "", err 123 | } 124 | 125 | checksum = "SHA256:" + hex.EncodeToString(sw.Sum(nil)) 126 | } 127 | 128 | { 129 | options := slugOptions{ 130 | Checksum: checksum, 131 | Commit: config.Commit, 132 | CommitDescription: config.CommitDescription, 133 | BuildpackProvidedDescription: config.BuildpackProvidedDescription, 134 | ProcessTypes: config.ProcessTypes, 135 | Stack: config.Stack, 136 | } 137 | 138 | payload, err := json.Marshal(&options) 139 | if err != nil { 140 | return "", err 141 | } 142 | 143 | req, err := http.NewRequest("POST", "https://api.heroku.com/apps/"+appID+"/slugs", bytes.NewReader(payload)) 144 | if err != nil { 145 | return "", err 146 | } 147 | 148 | fmt.Fprintf(os.Stderr, "payload: %s\n", payload) 149 | 150 | req.Header.Set("Content-Type", "application/json") 151 | req.Header.Set("Accept", "application/vnd.heroku+json; version=3") 152 | req.Header.Set("Authorization", "Bearer "+os.Getenv("HEROKU_API_KEY")) 153 | 154 | res, err := http.DefaultClient.Do(req) 155 | if err != nil { 156 | return "", err 157 | } 158 | 159 | defer res.Body.Close() 160 | defer io.Copy(ioutil.Discard, res.Body) 161 | 162 | if res.StatusCode/100 != 2 { 163 | return "", fmt.Errorf("unexpected status code: %d", res.StatusCode) 164 | } 165 | 166 | err = json.NewDecoder(res.Body).Decode(&info) 167 | if err != nil { 168 | return "", err 169 | } 170 | } 171 | 172 | infoJSON, _ := json.Marshal(&info) 173 | fmt.Fprintf(os.Stderr, "info: %q\n", infoJSON) 174 | 175 | { 176 | req, err := http.NewRequest("PUT", info.Blob.URL, bytes.NewReader(buf.Bytes())) 177 | if err != nil { 178 | return "", fmt.Errorf("upload slug: %s", err) 179 | } 180 | 181 | req.Header.Set("Content-Type", "") 182 | 183 | res, err := http.DefaultClient.Do(req) 184 | if err != nil { 185 | return "", fmt.Errorf("upload slug: %s", err) 186 | } 187 | 188 | defer res.Body.Close() 189 | defer io.Copy(ioutil.Discard, res.Body) 190 | 191 | if res.StatusCode/100 != 2 { 192 | return "", fmt.Errorf("upload slug: unexpected status code: %d", res.StatusCode) 193 | } 194 | } 195 | 196 | return info.ID, nil 197 | } 198 | -------------------------------------------------------------------------------- /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/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.writer) 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 == true { 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 == false { 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.writer, ctx) 211 | } 212 | -------------------------------------------------------------------------------- /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 | func (c *cmdMixin) CmdCompletion() []string { 16 | rv := []string{} 17 | if len(c.cmdGroup.commandOrder) > 0 { 18 | // This command has subcommands. We should 19 | // show these to the user. 20 | for _, option := range c.cmdGroup.commandOrder { 21 | rv = append(rv, option.name) 22 | } 23 | } else { 24 | // No subcommands 25 | rv = nil 26 | } 27 | return rv 28 | } 29 | 30 | func (c *cmdMixin) FlagCompletion(flagName string, flagValue string) (choices []string, flagMatch bool, optionMatch bool) { 31 | // Check if flagName matches a known flag. 32 | // If it does, show the options for the flag 33 | // Otherwise, show all flags 34 | 35 | options := []string{} 36 | 37 | for _, flag := range c.flagGroup.flagOrder { 38 | // Loop through each flag and determine if a match exists 39 | if flag.name == flagName { 40 | // User typed entire flag. Need to look for flag options. 41 | options = flag.resolveCompletions() 42 | if len(options) == 0 { 43 | // No Options to Choose From, Assume Match. 44 | return options, true, true 45 | } 46 | 47 | // Loop options to find if the user specified value matches 48 | isPrefix := false 49 | matched := false 50 | 51 | for _, opt := range options { 52 | if flagValue == opt { 53 | matched = true 54 | } else if strings.HasPrefix(opt, flagValue) { 55 | isPrefix = true 56 | } 57 | } 58 | 59 | // Matched Flag Directly 60 | // Flag Value Not Prefixed, and Matched Directly 61 | return options, true, !isPrefix && matched 62 | } 63 | 64 | if !flag.hidden { 65 | options = append(options, "--"+flag.name) 66 | } 67 | } 68 | // No Flag directly matched. 69 | return options, false, false 70 | 71 | } 72 | 73 | type cmdGroup struct { 74 | app *Application 75 | parent *CmdClause 76 | commands map[string]*CmdClause 77 | commandOrder []*CmdClause 78 | } 79 | 80 | func (c *cmdGroup) defaultSubcommand() *CmdClause { 81 | for _, cmd := range c.commandOrder { 82 | if cmd.isDefault { 83 | return cmd 84 | } 85 | } 86 | return nil 87 | } 88 | 89 | // GetArg gets a command definition. 90 | // 91 | // This allows existing commands to be modified after definition but before parsing. Useful for 92 | // modular applications. 93 | func (c *cmdGroup) GetCommand(name string) *CmdClause { 94 | return c.commands[name] 95 | } 96 | 97 | func newCmdGroup(app *Application) *cmdGroup { 98 | return &cmdGroup{ 99 | app: app, 100 | commands: make(map[string]*CmdClause), 101 | } 102 | } 103 | 104 | func (c *cmdGroup) flattenedCommands() (out []*CmdClause) { 105 | for _, cmd := range c.commandOrder { 106 | if len(cmd.commands) == 0 { 107 | out = append(out, cmd) 108 | } 109 | out = append(out, cmd.flattenedCommands()...) 110 | } 111 | return 112 | } 113 | 114 | func (c *cmdGroup) addCommand(name, help string) *CmdClause { 115 | cmd := newCommand(c.app, name, help) 116 | c.commands[name] = cmd 117 | c.commandOrder = append(c.commandOrder, cmd) 118 | return cmd 119 | } 120 | 121 | func (c *cmdGroup) init() error { 122 | seen := map[string]bool{} 123 | if c.defaultSubcommand() != nil && !c.have() { 124 | return fmt.Errorf("default subcommand %q provided but no subcommands defined", c.defaultSubcommand().name) 125 | } 126 | defaults := []string{} 127 | for _, cmd := range c.commandOrder { 128 | if cmd.isDefault { 129 | defaults = append(defaults, cmd.name) 130 | } 131 | if seen[cmd.name] { 132 | return fmt.Errorf("duplicate command %q", cmd.name) 133 | } 134 | seen[cmd.name] = true 135 | for _, alias := range cmd.aliases { 136 | if seen[alias] { 137 | return fmt.Errorf("alias duplicates existing command %q", alias) 138 | } 139 | c.commands[alias] = cmd 140 | } 141 | if err := cmd.init(); err != nil { 142 | return err 143 | } 144 | } 145 | if len(defaults) > 1 { 146 | return fmt.Errorf("more than one default subcommand exists: %s", strings.Join(defaults, ", ")) 147 | } 148 | return nil 149 | } 150 | 151 | func (c *cmdGroup) have() bool { 152 | return len(c.commands) > 0 153 | } 154 | 155 | type CmdClauseValidator func(*CmdClause) error 156 | 157 | // A CmdClause is a single top-level command. It encapsulates a set of flags 158 | // and either subcommands or positional arguments. 159 | type CmdClause struct { 160 | cmdMixin 161 | app *Application 162 | name string 163 | aliases []string 164 | help string 165 | isDefault bool 166 | validator CmdClauseValidator 167 | hidden bool 168 | } 169 | 170 | func newCommand(app *Application, name, help string) *CmdClause { 171 | c := &CmdClause{ 172 | app: app, 173 | name: name, 174 | help: help, 175 | } 176 | c.flagGroup = newFlagGroup() 177 | c.argGroup = newArgGroup() 178 | c.cmdGroup = newCmdGroup(app) 179 | return c 180 | } 181 | 182 | // Add an Alias for this command. 183 | func (c *CmdClause) Alias(name string) *CmdClause { 184 | c.aliases = append(c.aliases, name) 185 | return c 186 | } 187 | 188 | // Validate sets a validation function to run when parsing. 189 | func (c *CmdClause) Validate(validator CmdClauseValidator) *CmdClause { 190 | c.validator = validator 191 | return c 192 | } 193 | 194 | func (c *CmdClause) FullCommand() string { 195 | out := []string{c.name} 196 | for p := c.parent; p != nil; p = p.parent { 197 | out = append([]string{p.name}, out...) 198 | } 199 | return strings.Join(out, " ") 200 | } 201 | 202 | // Command adds a new sub-command. 203 | func (c *CmdClause) Command(name, help string) *CmdClause { 204 | cmd := c.addCommand(name, help) 205 | cmd.parent = c 206 | return cmd 207 | } 208 | 209 | // Default makes this command the default if commands don't match. 210 | func (c *CmdClause) Default() *CmdClause { 211 | c.isDefault = true 212 | return c 213 | } 214 | 215 | func (c *CmdClause) Action(action Action) *CmdClause { 216 | c.addAction(action) 217 | return c 218 | } 219 | 220 | func (c *CmdClause) PreAction(action Action) *CmdClause { 221 | c.addPreAction(action) 222 | return c 223 | } 224 | 225 | func (c *CmdClause) init() error { 226 | if err := c.flagGroup.init(c.app.defaultEnvarPrefix()); err != nil { 227 | return err 228 | } 229 | if c.argGroup.have() && c.cmdGroup.have() { 230 | return fmt.Errorf("can't mix Arg()s with Command()s") 231 | } 232 | if err := c.argGroup.init(); err != nil { 233 | return err 234 | } 235 | if err := c.cmdGroup.init(); err != nil { 236 | return err 237 | } 238 | return nil 239 | } 240 | 241 | func (c *CmdClause) Hidden() *CmdClause { 242 | c.hidden = true 243 | return c 244 | } 245 | -------------------------------------------------------------------------------- /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/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/flags.go: -------------------------------------------------------------------------------- 1 | package kingpin 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "regexp" 7 | "strings" 8 | ) 9 | 10 | var ( 11 | envVarValuesSeparator = "\r?\n" 12 | envVarValuesTrimmer = regexp.MustCompile(envVarValuesSeparator + "$") 13 | envVarValuesSplitter = regexp.MustCompile(envVarValuesSeparator) 14 | ) 15 | 16 | type flagGroup struct { 17 | short map[string]*FlagClause 18 | long map[string]*FlagClause 19 | flagOrder []*FlagClause 20 | } 21 | 22 | func newFlagGroup() *flagGroup { 23 | return &flagGroup{ 24 | short: map[string]*FlagClause{}, 25 | long: map[string]*FlagClause{}, 26 | } 27 | } 28 | 29 | // GetFlag gets a flag definition. 30 | // 31 | // This allows existing flags to be modified after definition but before parsing. Useful for 32 | // modular applications. 33 | func (f *flagGroup) GetFlag(name string) *FlagClause { 34 | return f.long[name] 35 | } 36 | 37 | // Flag defines a new flag with the given long name and help. 38 | func (f *flagGroup) Flag(name, help string) *FlagClause { 39 | flag := newFlag(name, help) 40 | f.long[name] = flag 41 | f.flagOrder = append(f.flagOrder, flag) 42 | return flag 43 | } 44 | 45 | func (f *flagGroup) init(defaultEnvarPrefix string) error { 46 | if err := f.checkDuplicates(); err != nil { 47 | return err 48 | } 49 | for _, flag := range f.long { 50 | if defaultEnvarPrefix != "" && !flag.noEnvar && flag.envar == "" { 51 | flag.envar = envarTransform(defaultEnvarPrefix + "_" + flag.name) 52 | } 53 | if err := flag.init(); err != nil { 54 | return err 55 | } 56 | if flag.shorthand != 0 { 57 | f.short[string(flag.shorthand)] = flag 58 | } 59 | } 60 | return nil 61 | } 62 | 63 | func (f *flagGroup) checkDuplicates() error { 64 | seenShort := map[byte]bool{} 65 | seenLong := map[string]bool{} 66 | for _, flag := range f.flagOrder { 67 | if flag.shorthand != 0 { 68 | if _, ok := seenShort[flag.shorthand]; ok { 69 | return fmt.Errorf("duplicate short flag -%c", flag.shorthand) 70 | } 71 | seenShort[flag.shorthand] = true 72 | } 73 | if _, ok := seenLong[flag.name]; ok { 74 | return fmt.Errorf("duplicate long flag --%s", flag.name) 75 | } 76 | seenLong[flag.name] = true 77 | } 78 | return nil 79 | } 80 | 81 | func (f *flagGroup) parse(context *ParseContext) (*FlagClause, error) { 82 | var token *Token 83 | 84 | loop: 85 | for { 86 | token = context.Peek() 87 | switch token.Type { 88 | case TokenEOL: 89 | break loop 90 | 91 | case TokenLong, TokenShort: 92 | flagToken := token 93 | defaultValue := "" 94 | var flag *FlagClause 95 | var ok bool 96 | invert := false 97 | 98 | name := token.Value 99 | if token.Type == TokenLong { 100 | if strings.HasPrefix(name, "no-") { 101 | name = name[3:] 102 | invert = true 103 | } 104 | flag, ok = f.long[name] 105 | if !ok { 106 | return nil, fmt.Errorf("unknown long flag '%s'", flagToken) 107 | } 108 | } else { 109 | flag, ok = f.short[name] 110 | if !ok { 111 | return nil, fmt.Errorf("unknown short flag '%s'", flagToken) 112 | } 113 | } 114 | 115 | context.Next() 116 | 117 | fb, ok := flag.value.(boolFlag) 118 | if ok && fb.IsBoolFlag() { 119 | if invert { 120 | defaultValue = "false" 121 | } else { 122 | defaultValue = "true" 123 | } 124 | } else { 125 | if invert { 126 | context.Push(token) 127 | return nil, fmt.Errorf("unknown long flag '%s'", flagToken) 128 | } 129 | token = context.Peek() 130 | if token.Type != TokenArg { 131 | context.Push(token) 132 | return nil, fmt.Errorf("expected argument for flag '%s'", flagToken) 133 | } 134 | context.Next() 135 | defaultValue = token.Value 136 | } 137 | 138 | context.matchedFlag(flag, defaultValue) 139 | return flag, nil 140 | 141 | default: 142 | break loop 143 | } 144 | } 145 | return nil, nil 146 | } 147 | 148 | func (f *flagGroup) visibleFlags() int { 149 | count := 0 150 | for _, flag := range f.long { 151 | if !flag.hidden { 152 | count++ 153 | } 154 | } 155 | return count 156 | } 157 | 158 | // FlagClause is a fluid interface used to build flags. 159 | type FlagClause struct { 160 | parserMixin 161 | actionMixin 162 | completionsMixin 163 | name string 164 | shorthand byte 165 | help string 166 | envar string 167 | noEnvar bool 168 | defaultValues []string 169 | placeholder string 170 | hidden bool 171 | } 172 | 173 | func newFlag(name, help string) *FlagClause { 174 | f := &FlagClause{ 175 | name: name, 176 | help: help, 177 | } 178 | return f 179 | } 180 | 181 | func (f *FlagClause) setDefault() error { 182 | if !f.noEnvar && f.envar != "" { 183 | if envarValue := os.Getenv(f.envar); envarValue != "" { 184 | if v, ok := f.value.(repeatableFlag); !ok || !v.IsCumulative() { 185 | // Use the value as-is 186 | return f.value.Set(envarValue) 187 | } else { 188 | // Split by new line to extract multiple values, if any. 189 | trimmed := envVarValuesTrimmer.ReplaceAllString(envarValue, "") 190 | for _, value := range envVarValuesSplitter.Split(trimmed, -1) { 191 | if err := f.value.Set(value); err != nil { 192 | return err 193 | } 194 | } 195 | return nil 196 | } 197 | } 198 | } 199 | 200 | if len(f.defaultValues) > 0 { 201 | for _, defaultValue := range f.defaultValues { 202 | if err := f.value.Set(defaultValue); err != nil { 203 | return err 204 | } 205 | } 206 | return nil 207 | } 208 | 209 | return nil 210 | } 211 | 212 | func (f *FlagClause) needsValue() bool { 213 | haveDefault := len(f.defaultValues) > 0 214 | haveEnvar := !f.noEnvar && f.envar != "" && os.Getenv(f.envar) != "" 215 | return f.required && !(haveDefault || haveEnvar) 216 | } 217 | 218 | func (f *FlagClause) init() error { 219 | if f.required && len(f.defaultValues) > 0 { 220 | return fmt.Errorf("required flag '--%s' with default value that will never be used", f.name) 221 | } 222 | if f.value == nil { 223 | return fmt.Errorf("no type defined for --%s (eg. .String())", f.name) 224 | } 225 | if v, ok := f.value.(repeatableFlag); (!ok || !v.IsCumulative()) && len(f.defaultValues) > 1 { 226 | return fmt.Errorf("invalid default for '--%s', expecting single value", f.name) 227 | } 228 | return nil 229 | } 230 | 231 | // Dispatch to the given function after the flag is parsed and validated. 232 | func (f *FlagClause) Action(action Action) *FlagClause { 233 | f.addAction(action) 234 | return f 235 | } 236 | 237 | func (f *FlagClause) PreAction(action Action) *FlagClause { 238 | f.addPreAction(action) 239 | return f 240 | } 241 | 242 | // HintAction registers a HintAction (function) for the flag to provide completions 243 | func (a *FlagClause) HintAction(action HintAction) *FlagClause { 244 | a.addHintAction(action) 245 | return a 246 | } 247 | 248 | // HintOptions registers any number of options for the flag to provide completions 249 | func (a *FlagClause) HintOptions(options ...string) *FlagClause { 250 | a.addHintAction(func() []string { 251 | return options 252 | }) 253 | return a 254 | } 255 | 256 | func (a *FlagClause) EnumVar(target *string, options ...string) { 257 | a.parserMixin.EnumVar(target, options...) 258 | a.addHintActionBuiltin(func() []string { 259 | return options 260 | }) 261 | } 262 | 263 | func (a *FlagClause) Enum(options ...string) (target *string) { 264 | a.addHintActionBuiltin(func() []string { 265 | return options 266 | }) 267 | return a.parserMixin.Enum(options...) 268 | } 269 | 270 | // Default values for this flag. They *must* be parseable by the value of the flag. 271 | func (f *FlagClause) Default(values ...string) *FlagClause { 272 | f.defaultValues = values 273 | return f 274 | } 275 | 276 | // DEPRECATED: Use Envar(name) instead. 277 | func (f *FlagClause) OverrideDefaultFromEnvar(envar string) *FlagClause { 278 | return f.Envar(envar) 279 | } 280 | 281 | // Envar overrides the default value(s) for a flag from an environment variable, 282 | // if it is set. Several default values can be provided by using new lines to 283 | // separate them. 284 | func (f *FlagClause) Envar(name string) *FlagClause { 285 | f.envar = name 286 | f.noEnvar = false 287 | return f 288 | } 289 | 290 | // NoEnvar forces environment variable defaults to be disabled for this flag. 291 | // Most useful in conjunction with app.DefaultEnvars(). 292 | func (f *FlagClause) NoEnvar() *FlagClause { 293 | f.envar = "" 294 | f.noEnvar = true 295 | return f 296 | } 297 | 298 | // PlaceHolder sets the place-holder string used for flag values in the help. The 299 | // default behaviour is to use the value provided by Default() if provided, 300 | // then fall back on the capitalized flag name. 301 | func (f *FlagClause) PlaceHolder(placeholder string) *FlagClause { 302 | f.placeholder = placeholder 303 | return f 304 | } 305 | 306 | // Hidden hides a flag from usage but still allows it to be used. 307 | func (f *FlagClause) Hidden() *FlagClause { 308 | f.hidden = true 309 | return f 310 | } 311 | 312 | // Required makes the flag required. You can not provide a Default() value to a Required() flag. 313 | func (f *FlagClause) Required() *FlagClause { 314 | f.required = true 315 | return f 316 | } 317 | 318 | // Short sets the short flag name. 319 | func (f *FlagClause) Short(name byte) *FlagClause { 320 | f.shorthand = name 321 | return f 322 | } 323 | 324 | // Bool makes this flag a boolean flag. 325 | func (f *FlagClause) Bool() (target *bool) { 326 | target = new(bool) 327 | f.SetValue(newBoolValue(target)) 328 | return 329 | } 330 | -------------------------------------------------------------------------------- /vendor/gopkg.in/alecthomas/kingpin.v2/parser.go: -------------------------------------------------------------------------------- 1 | package kingpin 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "os" 7 | "strings" 8 | ) 9 | 10 | type TokenType int 11 | 12 | // Token types. 13 | const ( 14 | TokenShort TokenType = iota 15 | TokenLong 16 | TokenArg 17 | TokenError 18 | TokenEOL 19 | ) 20 | 21 | func (t TokenType) String() string { 22 | switch t { 23 | case TokenShort: 24 | return "short flag" 25 | case TokenLong: 26 | return "long flag" 27 | case TokenArg: 28 | return "argument" 29 | case TokenError: 30 | return "error" 31 | case TokenEOL: 32 | return "" 33 | } 34 | return "?" 35 | } 36 | 37 | var ( 38 | TokenEOLMarker = Token{-1, TokenEOL, ""} 39 | ) 40 | 41 | type Token struct { 42 | Index int 43 | Type TokenType 44 | Value string 45 | } 46 | 47 | func (t *Token) Equal(o *Token) bool { 48 | return t.Index == o.Index 49 | } 50 | 51 | func (t *Token) IsFlag() bool { 52 | return t.Type == TokenShort || t.Type == TokenLong 53 | } 54 | 55 | func (t *Token) IsEOF() bool { 56 | return t.Type == TokenEOL 57 | } 58 | 59 | func (t *Token) String() string { 60 | switch t.Type { 61 | case TokenShort: 62 | return "-" + t.Value 63 | case TokenLong: 64 | return "--" + t.Value 65 | case TokenArg: 66 | return t.Value 67 | case TokenError: 68 | return "error: " + t.Value 69 | case TokenEOL: 70 | return "" 71 | default: 72 | panic("unhandled type") 73 | } 74 | } 75 | 76 | // A union of possible elements in a parse stack. 77 | type ParseElement struct { 78 | // Clause is either *CmdClause, *ArgClause or *FlagClause. 79 | Clause interface{} 80 | // Value is corresponding value for an ArgClause or FlagClause (if any). 81 | Value *string 82 | } 83 | 84 | // ParseContext holds the current context of the parser. When passed to 85 | // Action() callbacks Elements will be fully populated with *FlagClause, 86 | // *ArgClause and *CmdClause values and their corresponding arguments (if 87 | // any). 88 | type ParseContext struct { 89 | SelectedCommand *CmdClause 90 | ignoreDefault bool 91 | argsOnly bool 92 | peek []*Token 93 | argi int // Index of current command-line arg we're processing. 94 | args []string 95 | rawArgs []string 96 | flags *flagGroup 97 | arguments *argGroup 98 | argumenti int // Cursor into arguments 99 | // Flags, arguments and commands encountered and collected during parse. 100 | Elements []*ParseElement 101 | } 102 | 103 | func (p *ParseContext) nextArg() *ArgClause { 104 | if p.argumenti >= len(p.arguments.args) { 105 | return nil 106 | } 107 | arg := p.arguments.args[p.argumenti] 108 | if !arg.consumesRemainder() { 109 | p.argumenti++ 110 | } 111 | return arg 112 | } 113 | 114 | func (p *ParseContext) next() { 115 | p.argi++ 116 | p.args = p.args[1:] 117 | } 118 | 119 | // HasTrailingArgs returns true if there are unparsed command-line arguments. 120 | // This can occur if the parser can not match remaining arguments. 121 | func (p *ParseContext) HasTrailingArgs() bool { 122 | return len(p.args) > 0 123 | } 124 | 125 | func tokenize(args []string, ignoreDefault bool) *ParseContext { 126 | return &ParseContext{ 127 | ignoreDefault: ignoreDefault, 128 | args: args, 129 | rawArgs: args, 130 | flags: newFlagGroup(), 131 | arguments: newArgGroup(), 132 | } 133 | } 134 | 135 | func (p *ParseContext) mergeFlags(flags *flagGroup) { 136 | for _, flag := range flags.flagOrder { 137 | if flag.shorthand != 0 { 138 | p.flags.short[string(flag.shorthand)] = flag 139 | } 140 | p.flags.long[flag.name] = flag 141 | p.flags.flagOrder = append(p.flags.flagOrder, flag) 142 | } 143 | } 144 | 145 | func (p *ParseContext) mergeArgs(args *argGroup) { 146 | for _, arg := range args.args { 147 | p.arguments.args = append(p.arguments.args, arg) 148 | } 149 | } 150 | 151 | func (p *ParseContext) EOL() bool { 152 | return p.Peek().Type == TokenEOL 153 | } 154 | 155 | // Next token in the parse context. 156 | func (p *ParseContext) Next() *Token { 157 | if len(p.peek) > 0 { 158 | return p.pop() 159 | } 160 | 161 | // End of tokens. 162 | if len(p.args) == 0 { 163 | return &Token{Index: p.argi, Type: TokenEOL} 164 | } 165 | 166 | arg := p.args[0] 167 | p.next() 168 | 169 | if p.argsOnly { 170 | return &Token{p.argi, TokenArg, arg} 171 | } 172 | 173 | // All remaining args are passed directly. 174 | if arg == "--" { 175 | p.argsOnly = true 176 | return p.Next() 177 | } 178 | 179 | if strings.HasPrefix(arg, "--") { 180 | parts := strings.SplitN(arg[2:], "=", 2) 181 | token := &Token{p.argi, TokenLong, parts[0]} 182 | if len(parts) == 2 { 183 | p.Push(&Token{p.argi, TokenArg, parts[1]}) 184 | } 185 | return token 186 | } 187 | 188 | if strings.HasPrefix(arg, "-") { 189 | if len(arg) == 1 { 190 | return &Token{Index: p.argi, Type: TokenShort} 191 | } 192 | short := arg[1:2] 193 | flag, ok := p.flags.short[short] 194 | // Not a known short flag, we'll just return it anyway. 195 | if !ok { 196 | } else if fb, ok := flag.value.(boolFlag); ok && fb.IsBoolFlag() { 197 | // Bool short flag. 198 | } else { 199 | // Short flag with combined argument: -fARG 200 | token := &Token{p.argi, TokenShort, short} 201 | if len(arg) > 2 { 202 | p.Push(&Token{p.argi, TokenArg, arg[2:]}) 203 | } 204 | return token 205 | } 206 | 207 | if len(arg) > 2 { 208 | p.args = append([]string{"-" + arg[2:]}, p.args...) 209 | } 210 | return &Token{p.argi, TokenShort, short} 211 | } else if strings.HasPrefix(arg, "@") { 212 | expanded, err := ExpandArgsFromFile(arg[1:]) 213 | if err != nil { 214 | return &Token{p.argi, TokenError, err.Error()} 215 | } 216 | if p.argi >= len(p.args) { 217 | p.args = append(p.args[:p.argi-1], expanded...) 218 | } else { 219 | p.args = append(p.args[:p.argi-1], append(expanded, p.args[p.argi+1:]...)...) 220 | } 221 | return p.Next() 222 | } 223 | 224 | return &Token{p.argi, TokenArg, arg} 225 | } 226 | 227 | func (p *ParseContext) Peek() *Token { 228 | if len(p.peek) == 0 { 229 | return p.Push(p.Next()) 230 | } 231 | return p.peek[len(p.peek)-1] 232 | } 233 | 234 | func (p *ParseContext) Push(token *Token) *Token { 235 | p.peek = append(p.peek, token) 236 | return token 237 | } 238 | 239 | func (p *ParseContext) pop() *Token { 240 | end := len(p.peek) - 1 241 | token := p.peek[end] 242 | p.peek = p.peek[0:end] 243 | return token 244 | } 245 | 246 | func (p *ParseContext) String() string { 247 | return p.SelectedCommand.FullCommand() 248 | } 249 | 250 | func (p *ParseContext) matchedFlag(flag *FlagClause, value string) { 251 | p.Elements = append(p.Elements, &ParseElement{Clause: flag, Value: &value}) 252 | } 253 | 254 | func (p *ParseContext) matchedArg(arg *ArgClause, value string) { 255 | p.Elements = append(p.Elements, &ParseElement{Clause: arg, Value: &value}) 256 | } 257 | 258 | func (p *ParseContext) matchedCmd(cmd *CmdClause) { 259 | p.Elements = append(p.Elements, &ParseElement{Clause: cmd}) 260 | p.mergeFlags(cmd.flagGroup) 261 | p.mergeArgs(cmd.argGroup) 262 | p.SelectedCommand = cmd 263 | } 264 | 265 | // Expand arguments from a file. Lines starting with # will be treated as comments. 266 | func ExpandArgsFromFile(filename string) (out []string, err error) { 267 | r, err := os.Open(filename) 268 | if err != nil { 269 | return nil, err 270 | } 271 | defer r.Close() 272 | scanner := bufio.NewScanner(r) 273 | for scanner.Scan() { 274 | line := scanner.Text() 275 | if strings.HasPrefix(line, "#") { 276 | continue 277 | } 278 | out = append(out, line) 279 | } 280 | err = scanner.Err() 281 | return 282 | } 283 | 284 | func parse(context *ParseContext, app *Application) (err error) { 285 | context.mergeFlags(app.flagGroup) 286 | context.mergeArgs(app.argGroup) 287 | 288 | cmds := app.cmdGroup 289 | ignoreDefault := context.ignoreDefault 290 | 291 | loop: 292 | for !context.EOL() { 293 | token := context.Peek() 294 | 295 | switch token.Type { 296 | case TokenLong, TokenShort: 297 | if flag, err := context.flags.parse(context); err != nil { 298 | if !ignoreDefault { 299 | if cmd := cmds.defaultSubcommand(); cmd != nil { 300 | context.matchedCmd(cmd) 301 | cmds = cmd.cmdGroup 302 | break 303 | } 304 | } 305 | return err 306 | } else if flag == HelpFlag { 307 | ignoreDefault = true 308 | } 309 | 310 | case TokenArg: 311 | if cmds.have() { 312 | selectedDefault := false 313 | cmd, ok := cmds.commands[token.String()] 314 | if !ok { 315 | if !ignoreDefault { 316 | if cmd = cmds.defaultSubcommand(); cmd != nil { 317 | selectedDefault = true 318 | } 319 | } 320 | if cmd == nil { 321 | return fmt.Errorf("expected command but got %q", token) 322 | } 323 | } 324 | if cmd == HelpCommand { 325 | ignoreDefault = true 326 | } 327 | context.matchedCmd(cmd) 328 | cmds = cmd.cmdGroup 329 | if !selectedDefault { 330 | context.Next() 331 | } 332 | } else if context.arguments.have() { 333 | if app.noInterspersed { 334 | // no more flags 335 | context.argsOnly = true 336 | } 337 | arg := context.nextArg() 338 | if arg == nil { 339 | break loop 340 | } 341 | context.matchedArg(arg, token.String()) 342 | context.Next() 343 | } else { 344 | break loop 345 | } 346 | 347 | case TokenEOL: 348 | break loop 349 | } 350 | } 351 | 352 | // Move to innermost default command. 353 | for !ignoreDefault { 354 | if cmd := cmds.defaultSubcommand(); cmd != nil { 355 | context.matchedCmd(cmd) 356 | cmds = cmd.cmdGroup 357 | } else { 358 | break 359 | } 360 | } 361 | 362 | if !context.EOL() { 363 | return fmt.Errorf("unexpected %s", context.Peek()) 364 | } 365 | 366 | // Set defaults for all remaining args. 367 | for arg := context.nextArg(); arg != nil && !arg.consumesRemainder(); arg = context.nextArg() { 368 | for _, defaultValue := range arg.defaultValues { 369 | if err := arg.value.Set(defaultValue); err != nil { 370 | return fmt.Errorf("invalid default value '%s' for argument '%s'", defaultValue, arg.name) 371 | } 372 | } 373 | } 374 | 375 | return 376 | } 377 | -------------------------------------------------------------------------------- /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 | // A flag whose value must be in a set of options. 336 | type enumValue struct { 337 | value *string 338 | options []string 339 | } 340 | 341 | func newEnumFlag(target *string, options ...string) *enumValue { 342 | return &enumValue{ 343 | value: target, 344 | options: options, 345 | } 346 | } 347 | 348 | func (a *enumValue) String() string { 349 | return *a.value 350 | } 351 | 352 | func (a *enumValue) Set(value string) error { 353 | for _, v := range a.options { 354 | if v == value { 355 | *a.value = value 356 | return nil 357 | } 358 | } 359 | return fmt.Errorf("enum value must be one of %s, got '%s'", strings.Join(a.options, ","), value) 360 | } 361 | 362 | func (e *enumValue) Get() interface{} { 363 | return (string)(*e.value) 364 | } 365 | 366 | // -- []string Enum Value 367 | type enumsValue struct { 368 | value *[]string 369 | options []string 370 | } 371 | 372 | func newEnumsFlag(target *[]string, options ...string) *enumsValue { 373 | return &enumsValue{ 374 | value: target, 375 | options: options, 376 | } 377 | } 378 | 379 | func (s *enumsValue) Set(value string) error { 380 | for _, v := range s.options { 381 | if v == value { 382 | *s.value = append(*s.value, value) 383 | return nil 384 | } 385 | } 386 | return fmt.Errorf("enum value must be one of %s, got '%s'", strings.Join(s.options, ","), value) 387 | } 388 | 389 | func (e *enumsValue) Get() interface{} { 390 | return ([]string)(*e.value) 391 | } 392 | 393 | func (s *enumsValue) String() string { 394 | return strings.Join(*s.value, ",") 395 | } 396 | 397 | func (s *enumsValue) IsCumulative() bool { 398 | return true 399 | } 400 | 401 | // -- units.Base2Bytes Value 402 | type bytesValue units.Base2Bytes 403 | 404 | func newBytesValue(p *units.Base2Bytes) *bytesValue { 405 | return (*bytesValue)(p) 406 | } 407 | 408 | func (d *bytesValue) Set(s string) error { 409 | v, err := units.ParseBase2Bytes(s) 410 | *d = bytesValue(v) 411 | return err 412 | } 413 | 414 | func (d *bytesValue) Get() interface{} { return units.Base2Bytes(*d) } 415 | 416 | func (d *bytesValue) String() string { return (*units.Base2Bytes)(d).String() } 417 | 418 | func newExistingFileValue(target *string) *fileStatValue { 419 | return newFileStatValue(target, func(s os.FileInfo) error { 420 | if s.IsDir() { 421 | return fmt.Errorf("'%s' is a directory", s.Name()) 422 | } 423 | return nil 424 | }) 425 | } 426 | 427 | func newExistingDirValue(target *string) *fileStatValue { 428 | return newFileStatValue(target, func(s os.FileInfo) error { 429 | if !s.IsDir() { 430 | return fmt.Errorf("'%s' is a file", s.Name()) 431 | } 432 | return nil 433 | }) 434 | } 435 | 436 | func newExistingFileOrDirValue(target *string) *fileStatValue { 437 | return newFileStatValue(target, func(s os.FileInfo) error { return nil }) 438 | } 439 | 440 | type counterValue int 441 | 442 | func newCounterValue(n *int) *counterValue { 443 | return (*counterValue)(n) 444 | } 445 | 446 | func (c *counterValue) Set(s string) error { 447 | *c++ 448 | return nil 449 | } 450 | 451 | func (c *counterValue) Get() interface{} { return (int)(*c) } 452 | func (c *counterValue) IsBoolFlag() bool { return true } 453 | func (c *counterValue) String() string { return fmt.Sprintf("%d", *c) } 454 | func (c *counterValue) IsCumulative() bool { return true } 455 | 456 | func resolveHost(value string) (net.IP, error) { 457 | if ip := net.ParseIP(value); ip != nil { 458 | return ip, nil 459 | } else { 460 | if addr, err := net.ResolveIPAddr("ip", value); err != nil { 461 | return nil, err 462 | } else { 463 | return addr.IP, nil 464 | } 465 | } 466 | } 467 | -------------------------------------------------------------------------------- /vendor/github.com/alecthomas/template/parse/lex.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 parse 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | "unicode" 11 | "unicode/utf8" 12 | ) 13 | 14 | // item represents a token or text string returned from the scanner. 15 | type item struct { 16 | typ itemType // The type of this item. 17 | pos Pos // The starting position, in bytes, of this item in the input string. 18 | val string // The value of this item. 19 | } 20 | 21 | func (i item) String() string { 22 | switch { 23 | case i.typ == itemEOF: 24 | return "EOF" 25 | case i.typ == itemError: 26 | return i.val 27 | case i.typ > itemKeyword: 28 | return fmt.Sprintf("<%s>", i.val) 29 | case len(i.val) > 10: 30 | return fmt.Sprintf("%.10q...", i.val) 31 | } 32 | return fmt.Sprintf("%q", i.val) 33 | } 34 | 35 | // itemType identifies the type of lex items. 36 | type itemType int 37 | 38 | const ( 39 | itemError itemType = iota // error occurred; value is text of error 40 | itemBool // boolean constant 41 | itemChar // printable ASCII character; grab bag for comma etc. 42 | itemCharConstant // character constant 43 | itemComplex // complex constant (1+2i); imaginary is just a number 44 | itemColonEquals // colon-equals (':=') introducing a declaration 45 | itemEOF 46 | itemField // alphanumeric identifier starting with '.' 47 | itemIdentifier // alphanumeric identifier not starting with '.' 48 | itemLeftDelim // left action delimiter 49 | itemLeftParen // '(' inside action 50 | itemNumber // simple number, including imaginary 51 | itemPipe // pipe symbol 52 | itemRawString // raw quoted string (includes quotes) 53 | itemRightDelim // right action delimiter 54 | itemElideNewline // elide newline after right delim 55 | itemRightParen // ')' inside action 56 | itemSpace // run of spaces separating arguments 57 | itemString // quoted string (includes quotes) 58 | itemText // plain text 59 | itemVariable // variable starting with '$', such as '$' or '$1' or '$hello' 60 | // Keywords appear after all the rest. 61 | itemKeyword // used only to delimit the keywords 62 | itemDot // the cursor, spelled '.' 63 | itemDefine // define keyword 64 | itemElse // else keyword 65 | itemEnd // end keyword 66 | itemIf // if keyword 67 | itemNil // the untyped nil constant, easiest to treat as a keyword 68 | itemRange // range keyword 69 | itemTemplate // template keyword 70 | itemWith // with keyword 71 | ) 72 | 73 | var key = map[string]itemType{ 74 | ".": itemDot, 75 | "define": itemDefine, 76 | "else": itemElse, 77 | "end": itemEnd, 78 | "if": itemIf, 79 | "range": itemRange, 80 | "nil": itemNil, 81 | "template": itemTemplate, 82 | "with": itemWith, 83 | } 84 | 85 | const eof = -1 86 | 87 | // stateFn represents the state of the scanner as a function that returns the next state. 88 | type stateFn func(*lexer) stateFn 89 | 90 | // lexer holds the state of the scanner. 91 | type lexer struct { 92 | name string // the name of the input; used only for error reports 93 | input string // the string being scanned 94 | leftDelim string // start of action 95 | rightDelim string // end of action 96 | state stateFn // the next lexing function to enter 97 | pos Pos // current position in the input 98 | start Pos // start position of this item 99 | width Pos // width of last rune read from input 100 | lastPos Pos // position of most recent item returned by nextItem 101 | items chan item // channel of scanned items 102 | parenDepth int // nesting depth of ( ) exprs 103 | } 104 | 105 | // next returns the next rune in the input. 106 | func (l *lexer) next() rune { 107 | if int(l.pos) >= len(l.input) { 108 | l.width = 0 109 | return eof 110 | } 111 | r, w := utf8.DecodeRuneInString(l.input[l.pos:]) 112 | l.width = Pos(w) 113 | l.pos += l.width 114 | return r 115 | } 116 | 117 | // peek returns but does not consume the next rune in the input. 118 | func (l *lexer) peek() rune { 119 | r := l.next() 120 | l.backup() 121 | return r 122 | } 123 | 124 | // backup steps back one rune. Can only be called once per call of next. 125 | func (l *lexer) backup() { 126 | l.pos -= l.width 127 | } 128 | 129 | // emit passes an item back to the client. 130 | func (l *lexer) emit(t itemType) { 131 | l.items <- item{t, l.start, l.input[l.start:l.pos]} 132 | l.start = l.pos 133 | } 134 | 135 | // ignore skips over the pending input before this point. 136 | func (l *lexer) ignore() { 137 | l.start = l.pos 138 | } 139 | 140 | // accept consumes the next rune if it's from the valid set. 141 | func (l *lexer) accept(valid string) bool { 142 | if strings.IndexRune(valid, l.next()) >= 0 { 143 | return true 144 | } 145 | l.backup() 146 | return false 147 | } 148 | 149 | // acceptRun consumes a run of runes from the valid set. 150 | func (l *lexer) acceptRun(valid string) { 151 | for strings.IndexRune(valid, l.next()) >= 0 { 152 | } 153 | l.backup() 154 | } 155 | 156 | // lineNumber reports which line we're on, based on the position of 157 | // the previous item returned by nextItem. Doing it this way 158 | // means we don't have to worry about peek double counting. 159 | func (l *lexer) lineNumber() int { 160 | return 1 + strings.Count(l.input[:l.lastPos], "\n") 161 | } 162 | 163 | // errorf returns an error token and terminates the scan by passing 164 | // back a nil pointer that will be the next state, terminating l.nextItem. 165 | func (l *lexer) errorf(format string, args ...interface{}) stateFn { 166 | l.items <- item{itemError, l.start, fmt.Sprintf(format, args...)} 167 | return nil 168 | } 169 | 170 | // nextItem returns the next item from the input. 171 | func (l *lexer) nextItem() item { 172 | item := <-l.items 173 | l.lastPos = item.pos 174 | return item 175 | } 176 | 177 | // lex creates a new scanner for the input string. 178 | func lex(name, input, left, right string) *lexer { 179 | if left == "" { 180 | left = leftDelim 181 | } 182 | if right == "" { 183 | right = rightDelim 184 | } 185 | l := &lexer{ 186 | name: name, 187 | input: input, 188 | leftDelim: left, 189 | rightDelim: right, 190 | items: make(chan item), 191 | } 192 | go l.run() 193 | return l 194 | } 195 | 196 | // run runs the state machine for the lexer. 197 | func (l *lexer) run() { 198 | for l.state = lexText; l.state != nil; { 199 | l.state = l.state(l) 200 | } 201 | } 202 | 203 | // state functions 204 | 205 | const ( 206 | leftDelim = "{{" 207 | rightDelim = "}}" 208 | leftComment = "/*" 209 | rightComment = "*/" 210 | ) 211 | 212 | // lexText scans until an opening action delimiter, "{{". 213 | func lexText(l *lexer) stateFn { 214 | for { 215 | if strings.HasPrefix(l.input[l.pos:], l.leftDelim) { 216 | if l.pos > l.start { 217 | l.emit(itemText) 218 | } 219 | return lexLeftDelim 220 | } 221 | if l.next() == eof { 222 | break 223 | } 224 | } 225 | // Correctly reached EOF. 226 | if l.pos > l.start { 227 | l.emit(itemText) 228 | } 229 | l.emit(itemEOF) 230 | return nil 231 | } 232 | 233 | // lexLeftDelim scans the left delimiter, which is known to be present. 234 | func lexLeftDelim(l *lexer) stateFn { 235 | l.pos += Pos(len(l.leftDelim)) 236 | if strings.HasPrefix(l.input[l.pos:], leftComment) { 237 | return lexComment 238 | } 239 | l.emit(itemLeftDelim) 240 | l.parenDepth = 0 241 | return lexInsideAction 242 | } 243 | 244 | // lexComment scans a comment. The left comment marker is known to be present. 245 | func lexComment(l *lexer) stateFn { 246 | l.pos += Pos(len(leftComment)) 247 | i := strings.Index(l.input[l.pos:], rightComment) 248 | if i < 0 { 249 | return l.errorf("unclosed comment") 250 | } 251 | l.pos += Pos(i + len(rightComment)) 252 | if !strings.HasPrefix(l.input[l.pos:], l.rightDelim) { 253 | return l.errorf("comment ends before closing delimiter") 254 | 255 | } 256 | l.pos += Pos(len(l.rightDelim)) 257 | l.ignore() 258 | return lexText 259 | } 260 | 261 | // lexRightDelim scans the right delimiter, which is known to be present. 262 | func lexRightDelim(l *lexer) stateFn { 263 | l.pos += Pos(len(l.rightDelim)) 264 | l.emit(itemRightDelim) 265 | if l.peek() == '\\' { 266 | l.pos++ 267 | l.emit(itemElideNewline) 268 | } 269 | return lexText 270 | } 271 | 272 | // lexInsideAction scans the elements inside action delimiters. 273 | func lexInsideAction(l *lexer) stateFn { 274 | // Either number, quoted string, or identifier. 275 | // Spaces separate arguments; runs of spaces turn into itemSpace. 276 | // Pipe symbols separate and are emitted. 277 | if strings.HasPrefix(l.input[l.pos:], l.rightDelim+"\\") || strings.HasPrefix(l.input[l.pos:], l.rightDelim) { 278 | if l.parenDepth == 0 { 279 | return lexRightDelim 280 | } 281 | return l.errorf("unclosed left paren") 282 | } 283 | switch r := l.next(); { 284 | case r == eof || isEndOfLine(r): 285 | return l.errorf("unclosed action") 286 | case isSpace(r): 287 | return lexSpace 288 | case r == ':': 289 | if l.next() != '=' { 290 | return l.errorf("expected :=") 291 | } 292 | l.emit(itemColonEquals) 293 | case r == '|': 294 | l.emit(itemPipe) 295 | case r == '"': 296 | return lexQuote 297 | case r == '`': 298 | return lexRawQuote 299 | case r == '$': 300 | return lexVariable 301 | case r == '\'': 302 | return lexChar 303 | case r == '.': 304 | // special look-ahead for ".field" so we don't break l.backup(). 305 | if l.pos < Pos(len(l.input)) { 306 | r := l.input[l.pos] 307 | if r < '0' || '9' < r { 308 | return lexField 309 | } 310 | } 311 | fallthrough // '.' can start a number. 312 | case r == '+' || r == '-' || ('0' <= r && r <= '9'): 313 | l.backup() 314 | return lexNumber 315 | case isAlphaNumeric(r): 316 | l.backup() 317 | return lexIdentifier 318 | case r == '(': 319 | l.emit(itemLeftParen) 320 | l.parenDepth++ 321 | return lexInsideAction 322 | case r == ')': 323 | l.emit(itemRightParen) 324 | l.parenDepth-- 325 | if l.parenDepth < 0 { 326 | return l.errorf("unexpected right paren %#U", r) 327 | } 328 | return lexInsideAction 329 | case r <= unicode.MaxASCII && unicode.IsPrint(r): 330 | l.emit(itemChar) 331 | return lexInsideAction 332 | default: 333 | return l.errorf("unrecognized character in action: %#U", r) 334 | } 335 | return lexInsideAction 336 | } 337 | 338 | // lexSpace scans a run of space characters. 339 | // One space has already been seen. 340 | func lexSpace(l *lexer) stateFn { 341 | for isSpace(l.peek()) { 342 | l.next() 343 | } 344 | l.emit(itemSpace) 345 | return lexInsideAction 346 | } 347 | 348 | // lexIdentifier scans an alphanumeric. 349 | func lexIdentifier(l *lexer) stateFn { 350 | Loop: 351 | for { 352 | switch r := l.next(); { 353 | case isAlphaNumeric(r): 354 | // absorb. 355 | default: 356 | l.backup() 357 | word := l.input[l.start:l.pos] 358 | if !l.atTerminator() { 359 | return l.errorf("bad character %#U", r) 360 | } 361 | switch { 362 | case key[word] > itemKeyword: 363 | l.emit(key[word]) 364 | case word[0] == '.': 365 | l.emit(itemField) 366 | case word == "true", word == "false": 367 | l.emit(itemBool) 368 | default: 369 | l.emit(itemIdentifier) 370 | } 371 | break Loop 372 | } 373 | } 374 | return lexInsideAction 375 | } 376 | 377 | // lexField scans a field: .Alphanumeric. 378 | // The . has been scanned. 379 | func lexField(l *lexer) stateFn { 380 | return lexFieldOrVariable(l, itemField) 381 | } 382 | 383 | // lexVariable scans a Variable: $Alphanumeric. 384 | // The $ has been scanned. 385 | func lexVariable(l *lexer) stateFn { 386 | if l.atTerminator() { // Nothing interesting follows -> "$". 387 | l.emit(itemVariable) 388 | return lexInsideAction 389 | } 390 | return lexFieldOrVariable(l, itemVariable) 391 | } 392 | 393 | // lexVariable scans a field or variable: [.$]Alphanumeric. 394 | // The . or $ has been scanned. 395 | func lexFieldOrVariable(l *lexer, typ itemType) stateFn { 396 | if l.atTerminator() { // Nothing interesting follows -> "." or "$". 397 | if typ == itemVariable { 398 | l.emit(itemVariable) 399 | } else { 400 | l.emit(itemDot) 401 | } 402 | return lexInsideAction 403 | } 404 | var r rune 405 | for { 406 | r = l.next() 407 | if !isAlphaNumeric(r) { 408 | l.backup() 409 | break 410 | } 411 | } 412 | if !l.atTerminator() { 413 | return l.errorf("bad character %#U", r) 414 | } 415 | l.emit(typ) 416 | return lexInsideAction 417 | } 418 | 419 | // atTerminator reports whether the input is at valid termination character to 420 | // appear after an identifier. Breaks .X.Y into two pieces. Also catches cases 421 | // like "$x+2" not being acceptable without a space, in case we decide one 422 | // day to implement arithmetic. 423 | func (l *lexer) atTerminator() bool { 424 | r := l.peek() 425 | if isSpace(r) || isEndOfLine(r) { 426 | return true 427 | } 428 | switch r { 429 | case eof, '.', ',', '|', ':', ')', '(': 430 | return true 431 | } 432 | // Does r start the delimiter? This can be ambiguous (with delim=="//", $x/2 will 433 | // succeed but should fail) but only in extremely rare cases caused by willfully 434 | // bad choice of delimiter. 435 | if rd, _ := utf8.DecodeRuneInString(l.rightDelim); rd == r { 436 | return true 437 | } 438 | return false 439 | } 440 | 441 | // lexChar scans a character constant. The initial quote is already 442 | // scanned. Syntax checking is done by the parser. 443 | func lexChar(l *lexer) stateFn { 444 | Loop: 445 | for { 446 | switch l.next() { 447 | case '\\': 448 | if r := l.next(); r != eof && r != '\n' { 449 | break 450 | } 451 | fallthrough 452 | case eof, '\n': 453 | return l.errorf("unterminated character constant") 454 | case '\'': 455 | break Loop 456 | } 457 | } 458 | l.emit(itemCharConstant) 459 | return lexInsideAction 460 | } 461 | 462 | // lexNumber scans a number: decimal, octal, hex, float, or imaginary. This 463 | // isn't a perfect number scanner - for instance it accepts "." and "0x0.2" 464 | // and "089" - but when it's wrong the input is invalid and the parser (via 465 | // strconv) will notice. 466 | func lexNumber(l *lexer) stateFn { 467 | if !l.scanNumber() { 468 | return l.errorf("bad number syntax: %q", l.input[l.start:l.pos]) 469 | } 470 | if sign := l.peek(); sign == '+' || sign == '-' { 471 | // Complex: 1+2i. No spaces, must end in 'i'. 472 | if !l.scanNumber() || l.input[l.pos-1] != 'i' { 473 | return l.errorf("bad number syntax: %q", l.input[l.start:l.pos]) 474 | } 475 | l.emit(itemComplex) 476 | } else { 477 | l.emit(itemNumber) 478 | } 479 | return lexInsideAction 480 | } 481 | 482 | func (l *lexer) scanNumber() bool { 483 | // Optional leading sign. 484 | l.accept("+-") 485 | // Is it hex? 486 | digits := "0123456789" 487 | if l.accept("0") && l.accept("xX") { 488 | digits = "0123456789abcdefABCDEF" 489 | } 490 | l.acceptRun(digits) 491 | if l.accept(".") { 492 | l.acceptRun(digits) 493 | } 494 | if l.accept("eE") { 495 | l.accept("+-") 496 | l.acceptRun("0123456789") 497 | } 498 | // Is it imaginary? 499 | l.accept("i") 500 | // Next thing mustn't be alphanumeric. 501 | if isAlphaNumeric(l.peek()) { 502 | l.next() 503 | return false 504 | } 505 | return true 506 | } 507 | 508 | // lexQuote scans a quoted string. 509 | func lexQuote(l *lexer) stateFn { 510 | Loop: 511 | for { 512 | switch l.next() { 513 | case '\\': 514 | if r := l.next(); r != eof && r != '\n' { 515 | break 516 | } 517 | fallthrough 518 | case eof, '\n': 519 | return l.errorf("unterminated quoted string") 520 | case '"': 521 | break Loop 522 | } 523 | } 524 | l.emit(itemString) 525 | return lexInsideAction 526 | } 527 | 528 | // lexRawQuote scans a raw quoted string. 529 | func lexRawQuote(l *lexer) stateFn { 530 | Loop: 531 | for { 532 | switch l.next() { 533 | case eof, '\n': 534 | return l.errorf("unterminated raw quoted string") 535 | case '`': 536 | break Loop 537 | } 538 | } 539 | l.emit(itemRawString) 540 | return lexInsideAction 541 | } 542 | 543 | // isSpace reports whether r is a space character. 544 | func isSpace(r rune) bool { 545 | return r == ' ' || r == '\t' 546 | } 547 | 548 | // isEndOfLine reports whether r is an end-of-line character. 549 | func isEndOfLine(r rune) bool { 550 | return r == '\r' || r == '\n' 551 | } 552 | 553 | // isAlphaNumeric reports whether r is an alphabetic, digit, or underscore. 554 | func isAlphaNumeric(r rune) bool { 555 | return r == '_' || unicode.IsLetter(r) || unicode.IsDigit(r) 556 | } 557 | -------------------------------------------------------------------------------- /vendor/github.com/alecthomas/template/doc.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 | /* 6 | Package template implements data-driven templates for generating textual output. 7 | 8 | To generate HTML output, see package html/template, which has the same interface 9 | as this package but automatically secures HTML output against certain attacks. 10 | 11 | Templates are executed by applying them to a data structure. Annotations in the 12 | template refer to elements of the data structure (typically a field of a struct 13 | or a key in a map) to control execution and derive values to be displayed. 14 | Execution of the template walks the structure and sets the cursor, represented 15 | by a period '.' and called "dot", to the value at the current location in the 16 | structure as execution proceeds. 17 | 18 | The input text for a template is UTF-8-encoded text in any format. 19 | "Actions"--data evaluations or control structures--are delimited by 20 | "{{" and "}}"; all text outside actions is copied to the output unchanged. 21 | Actions may not span newlines, although comments can. 22 | 23 | Once parsed, a template may be executed safely in parallel. 24 | 25 | Here is a trivial example that prints "17 items are made of wool". 26 | 27 | type Inventory struct { 28 | Material string 29 | Count uint 30 | } 31 | sweaters := Inventory{"wool", 17} 32 | tmpl, err := template.New("test").Parse("{{.Count}} items are made of {{.Material}}") 33 | if err != nil { panic(err) } 34 | err = tmpl.Execute(os.Stdout, sweaters) 35 | if err != nil { panic(err) } 36 | 37 | More intricate examples appear below. 38 | 39 | Actions 40 | 41 | Here is the list of actions. "Arguments" and "pipelines" are evaluations of 42 | data, defined in detail below. 43 | 44 | */ 45 | // {{/* a comment */}} 46 | // A comment; discarded. May contain newlines. 47 | // Comments do not nest and must start and end at the 48 | // delimiters, as shown here. 49 | /* 50 | 51 | {{pipeline}} 52 | The default textual representation of the value of the pipeline 53 | is copied to the output. 54 | 55 | {{if pipeline}} T1 {{end}} 56 | If the value of the pipeline is empty, no output is generated; 57 | otherwise, T1 is executed. The empty values are false, 0, any 58 | nil pointer or interface value, and any array, slice, map, or 59 | string of length zero. 60 | Dot is unaffected. 61 | 62 | {{if pipeline}} T1 {{else}} T0 {{end}} 63 | If the value of the pipeline is empty, T0 is executed; 64 | otherwise, T1 is executed. Dot is unaffected. 65 | 66 | {{if pipeline}} T1 {{else if pipeline}} T0 {{end}} 67 | To simplify the appearance of if-else chains, the else action 68 | of an if may include another if directly; the effect is exactly 69 | the same as writing 70 | {{if pipeline}} T1 {{else}}{{if pipeline}} T0 {{end}}{{end}} 71 | 72 | {{range pipeline}} T1 {{end}} 73 | The value of the pipeline must be an array, slice, map, or channel. 74 | If the value of the pipeline has length zero, nothing is output; 75 | otherwise, dot is set to the successive elements of the array, 76 | slice, or map and T1 is executed. If the value is a map and the 77 | keys are of basic type with a defined order ("comparable"), the 78 | elements will be visited in sorted key order. 79 | 80 | {{range pipeline}} T1 {{else}} T0 {{end}} 81 | The value of the pipeline must be an array, slice, map, or channel. 82 | If the value of the pipeline has length zero, dot is unaffected and 83 | T0 is executed; otherwise, dot is set to the successive elements 84 | of the array, slice, or map and T1 is executed. 85 | 86 | {{template "name"}} 87 | The template with the specified name is executed with nil data. 88 | 89 | {{template "name" pipeline}} 90 | The template with the specified name is executed with dot set 91 | to the value of the pipeline. 92 | 93 | {{with pipeline}} T1 {{end}} 94 | If the value of the pipeline is empty, no output is generated; 95 | otherwise, dot is set to the value of the pipeline and T1 is 96 | executed. 97 | 98 | {{with pipeline}} T1 {{else}} T0 {{end}} 99 | If the value of the pipeline is empty, dot is unaffected and T0 100 | is executed; otherwise, dot is set to the value of the pipeline 101 | and T1 is executed. 102 | 103 | Arguments 104 | 105 | An argument is a simple value, denoted by one of the following. 106 | 107 | - A boolean, string, character, integer, floating-point, imaginary 108 | or complex constant in Go syntax. These behave like Go's untyped 109 | constants, although raw strings may not span newlines. 110 | - The keyword nil, representing an untyped Go nil. 111 | - The character '.' (period): 112 | . 113 | The result is the value of dot. 114 | - A variable name, which is a (possibly empty) alphanumeric string 115 | preceded by a dollar sign, such as 116 | $piOver2 117 | or 118 | $ 119 | The result is the value of the variable. 120 | Variables are described below. 121 | - The name of a field of the data, which must be a struct, preceded 122 | by a period, such as 123 | .Field 124 | The result is the value of the field. Field invocations may be 125 | chained: 126 | .Field1.Field2 127 | Fields can also be evaluated on variables, including chaining: 128 | $x.Field1.Field2 129 | - The name of a key of the data, which must be a map, preceded 130 | by a period, such as 131 | .Key 132 | The result is the map element value indexed by the key. 133 | Key invocations may be chained and combined with fields to any 134 | depth: 135 | .Field1.Key1.Field2.Key2 136 | Although the key must be an alphanumeric identifier, unlike with 137 | field names they do not need to start with an upper case letter. 138 | Keys can also be evaluated on variables, including chaining: 139 | $x.key1.key2 140 | - The name of a niladic method of the data, preceded by a period, 141 | such as 142 | .Method 143 | The result is the value of invoking the method with dot as the 144 | receiver, dot.Method(). Such a method must have one return value (of 145 | any type) or two return values, the second of which is an error. 146 | If it has two and the returned error is non-nil, execution terminates 147 | and an error is returned to the caller as the value of Execute. 148 | Method invocations may be chained and combined with fields and keys 149 | to any depth: 150 | .Field1.Key1.Method1.Field2.Key2.Method2 151 | Methods can also be evaluated on variables, including chaining: 152 | $x.Method1.Field 153 | - The name of a niladic function, such as 154 | fun 155 | The result is the value of invoking the function, fun(). The return 156 | types and values behave as in methods. Functions and function 157 | names are described below. 158 | - A parenthesized instance of one the above, for grouping. The result 159 | may be accessed by a field or map key invocation. 160 | print (.F1 arg1) (.F2 arg2) 161 | (.StructValuedMethod "arg").Field 162 | 163 | Arguments may evaluate to any type; if they are pointers the implementation 164 | automatically indirects to the base type when required. 165 | If an evaluation yields a function value, such as a function-valued 166 | field of a struct, the function is not invoked automatically, but it 167 | can be used as a truth value for an if action and the like. To invoke 168 | it, use the call function, defined below. 169 | 170 | A pipeline is a possibly chained sequence of "commands". A command is a simple 171 | value (argument) or a function or method call, possibly with multiple arguments: 172 | 173 | Argument 174 | The result is the value of evaluating the argument. 175 | .Method [Argument...] 176 | The method can be alone or the last element of a chain but, 177 | unlike methods in the middle of a chain, it can take arguments. 178 | The result is the value of calling the method with the 179 | arguments: 180 | dot.Method(Argument1, etc.) 181 | functionName [Argument...] 182 | The result is the value of calling the function associated 183 | with the name: 184 | function(Argument1, etc.) 185 | Functions and function names are described below. 186 | 187 | Pipelines 188 | 189 | A pipeline may be "chained" by separating a sequence of commands with pipeline 190 | characters '|'. In a chained pipeline, the result of the each command is 191 | passed as the last argument of the following command. The output of the final 192 | command in the pipeline is the value of the pipeline. 193 | 194 | The output of a command will be either one value or two values, the second of 195 | which has type error. If that second value is present and evaluates to 196 | non-nil, execution terminates and the error is returned to the caller of 197 | Execute. 198 | 199 | Variables 200 | 201 | A pipeline inside an action may initialize a variable to capture the result. 202 | The initialization has syntax 203 | 204 | $variable := pipeline 205 | 206 | where $variable is the name of the variable. An action that declares a 207 | variable produces no output. 208 | 209 | If a "range" action initializes a variable, the variable is set to the 210 | successive elements of the iteration. Also, a "range" may declare two 211 | variables, separated by a comma: 212 | 213 | range $index, $element := pipeline 214 | 215 | in which case $index and $element are set to the successive values of the 216 | array/slice index or map key and element, respectively. Note that if there is 217 | only one variable, it is assigned the element; this is opposite to the 218 | convention in Go range clauses. 219 | 220 | A variable's scope extends to the "end" action of the control structure ("if", 221 | "with", or "range") in which it is declared, or to the end of the template if 222 | there is no such control structure. A template invocation does not inherit 223 | variables from the point of its invocation. 224 | 225 | When execution begins, $ is set to the data argument passed to Execute, that is, 226 | to the starting value of dot. 227 | 228 | Examples 229 | 230 | Here are some example one-line templates demonstrating pipelines and variables. 231 | All produce the quoted word "output": 232 | 233 | {{"\"output\""}} 234 | A string constant. 235 | {{`"output"`}} 236 | A raw string constant. 237 | {{printf "%q" "output"}} 238 | A function call. 239 | {{"output" | printf "%q"}} 240 | A function call whose final argument comes from the previous 241 | command. 242 | {{printf "%q" (print "out" "put")}} 243 | A parenthesized argument. 244 | {{"put" | printf "%s%s" "out" | printf "%q"}} 245 | A more elaborate call. 246 | {{"output" | printf "%s" | printf "%q"}} 247 | A longer chain. 248 | {{with "output"}}{{printf "%q" .}}{{end}} 249 | A with action using dot. 250 | {{with $x := "output" | printf "%q"}}{{$x}}{{end}} 251 | A with action that creates and uses a variable. 252 | {{with $x := "output"}}{{printf "%q" $x}}{{end}} 253 | A with action that uses the variable in another action. 254 | {{with $x := "output"}}{{$x | printf "%q"}}{{end}} 255 | The same, but pipelined. 256 | 257 | Functions 258 | 259 | During execution functions are found in two function maps: first in the 260 | template, then in the global function map. By default, no functions are defined 261 | in the template but the Funcs method can be used to add them. 262 | 263 | Predefined global functions are named as follows. 264 | 265 | and 266 | Returns the boolean AND of its arguments by returning the 267 | first empty argument or the last argument, that is, 268 | "and x y" behaves as "if x then y else x". All the 269 | arguments are evaluated. 270 | call 271 | Returns the result of calling the first argument, which 272 | must be a function, with the remaining arguments as parameters. 273 | Thus "call .X.Y 1 2" is, in Go notation, dot.X.Y(1, 2) where 274 | Y is a func-valued field, map entry, or the like. 275 | The first argument must be the result of an evaluation 276 | that yields a value of function type (as distinct from 277 | a predefined function such as print). The function must 278 | return either one or two result values, the second of which 279 | is of type error. If the arguments don't match the function 280 | or the returned error value is non-nil, execution stops. 281 | html 282 | Returns the escaped HTML equivalent of the textual 283 | representation of its arguments. 284 | index 285 | Returns the result of indexing its first argument by the 286 | following arguments. Thus "index x 1 2 3" is, in Go syntax, 287 | x[1][2][3]. Each indexed item must be a map, slice, or array. 288 | js 289 | Returns the escaped JavaScript equivalent of the textual 290 | representation of its arguments. 291 | len 292 | Returns the integer length of its argument. 293 | not 294 | Returns the boolean negation of its single argument. 295 | or 296 | Returns the boolean OR of its arguments by returning the 297 | first non-empty argument or the last argument, that is, 298 | "or x y" behaves as "if x then x else y". All the 299 | arguments are evaluated. 300 | print 301 | An alias for fmt.Sprint 302 | printf 303 | An alias for fmt.Sprintf 304 | println 305 | An alias for fmt.Sprintln 306 | urlquery 307 | Returns the escaped value of the textual representation of 308 | its arguments in a form suitable for embedding in a URL query. 309 | 310 | The boolean functions take any zero value to be false and a non-zero 311 | value to be true. 312 | 313 | There is also a set of binary comparison operators defined as 314 | functions: 315 | 316 | eq 317 | Returns the boolean truth of arg1 == arg2 318 | ne 319 | Returns the boolean truth of arg1 != arg2 320 | lt 321 | Returns the boolean truth of arg1 < arg2 322 | le 323 | Returns the boolean truth of arg1 <= arg2 324 | gt 325 | Returns the boolean truth of arg1 > arg2 326 | ge 327 | Returns the boolean truth of arg1 >= arg2 328 | 329 | For simpler multi-way equality tests, eq (only) accepts two or more 330 | arguments and compares the second and subsequent to the first, 331 | returning in effect 332 | 333 | arg1==arg2 || arg1==arg3 || arg1==arg4 ... 334 | 335 | (Unlike with || in Go, however, eq is a function call and all the 336 | arguments will be evaluated.) 337 | 338 | The comparison functions work on basic types only (or named basic 339 | types, such as "type Celsius float32"). They implement the Go rules 340 | for comparison of values, except that size and exact type are 341 | ignored, so any integer value, signed or unsigned, may be compared 342 | with any other integer value. (The arithmetic value is compared, 343 | not the bit pattern, so all negative integers are less than all 344 | unsigned integers.) However, as usual, one may not compare an int 345 | with a float32 and so on. 346 | 347 | Associated templates 348 | 349 | Each template is named by a string specified when it is created. Also, each 350 | template is associated with zero or more other templates that it may invoke by 351 | name; such associations are transitive and form a name space of templates. 352 | 353 | A template may use a template invocation to instantiate another associated 354 | template; see the explanation of the "template" action above. The name must be 355 | that of a template associated with the template that contains the invocation. 356 | 357 | Nested template definitions 358 | 359 | When parsing a template, another template may be defined and associated with the 360 | template being parsed. Template definitions must appear at the top level of the 361 | template, much like global variables in a Go program. 362 | 363 | The syntax of such definitions is to surround each template declaration with a 364 | "define" and "end" action. 365 | 366 | The define action names the template being created by providing a string 367 | constant. Here is a simple example: 368 | 369 | `{{define "T1"}}ONE{{end}} 370 | {{define "T2"}}TWO{{end}} 371 | {{define "T3"}}{{template "T1"}} {{template "T2"}}{{end}} 372 | {{template "T3"}}` 373 | 374 | This defines two templates, T1 and T2, and a third T3 that invokes the other two 375 | when it is executed. Finally it invokes T3. If executed this template will 376 | produce the text 377 | 378 | ONE TWO 379 | 380 | By construction, a template may reside in only one association. If it's 381 | necessary to have a template addressable from multiple associations, the 382 | template definition must be parsed multiple times to create distinct *Template 383 | values, or must be copied with the Clone or AddParseTree method. 384 | 385 | Parse may be called multiple times to assemble the various associated templates; 386 | see the ParseFiles and ParseGlob functions and methods for simple ways to parse 387 | related templates stored in files. 388 | 389 | A template may be executed directly or through ExecuteTemplate, which executes 390 | an associated template identified by name. To invoke our example above, we 391 | might write, 392 | 393 | err := tmpl.Execute(os.Stdout, "no data needed") 394 | if err != nil { 395 | log.Fatalf("execution failed: %s", err) 396 | } 397 | 398 | or to invoke a particular template explicitly by name, 399 | 400 | err := tmpl.ExecuteTemplate(os.Stdout, "T2", "no data needed") 401 | if err != nil { 402 | log.Fatalf("execution failed: %s", err) 403 | } 404 | 405 | */ 406 | package template 407 | -------------------------------------------------------------------------------- /vendor/github.com/alecthomas/template/funcs.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 | "bytes" 9 | "errors" 10 | "fmt" 11 | "io" 12 | "net/url" 13 | "reflect" 14 | "strings" 15 | "unicode" 16 | "unicode/utf8" 17 | ) 18 | 19 | // FuncMap is the type of the map defining the mapping from names to functions. 20 | // Each function must have either a single return value, or two return values of 21 | // which the second has type error. In that case, if the second (error) 22 | // return value evaluates to non-nil during execution, execution terminates and 23 | // Execute returns that error. 24 | type FuncMap map[string]interface{} 25 | 26 | var builtins = FuncMap{ 27 | "and": and, 28 | "call": call, 29 | "html": HTMLEscaper, 30 | "index": index, 31 | "js": JSEscaper, 32 | "len": length, 33 | "not": not, 34 | "or": or, 35 | "print": fmt.Sprint, 36 | "printf": fmt.Sprintf, 37 | "println": fmt.Sprintln, 38 | "urlquery": URLQueryEscaper, 39 | 40 | // Comparisons 41 | "eq": eq, // == 42 | "ge": ge, // >= 43 | "gt": gt, // > 44 | "le": le, // <= 45 | "lt": lt, // < 46 | "ne": ne, // != 47 | } 48 | 49 | var builtinFuncs = createValueFuncs(builtins) 50 | 51 | // createValueFuncs turns a FuncMap into a map[string]reflect.Value 52 | func createValueFuncs(funcMap FuncMap) map[string]reflect.Value { 53 | m := make(map[string]reflect.Value) 54 | addValueFuncs(m, funcMap) 55 | return m 56 | } 57 | 58 | // addValueFuncs adds to values the functions in funcs, converting them to reflect.Values. 59 | func addValueFuncs(out map[string]reflect.Value, in FuncMap) { 60 | for name, fn := range in { 61 | v := reflect.ValueOf(fn) 62 | if v.Kind() != reflect.Func { 63 | panic("value for " + name + " not a function") 64 | } 65 | if !goodFunc(v.Type()) { 66 | panic(fmt.Errorf("can't install method/function %q with %d results", name, v.Type().NumOut())) 67 | } 68 | out[name] = v 69 | } 70 | } 71 | 72 | // addFuncs adds to values the functions in funcs. It does no checking of the input - 73 | // call addValueFuncs first. 74 | func addFuncs(out, in FuncMap) { 75 | for name, fn := range in { 76 | out[name] = fn 77 | } 78 | } 79 | 80 | // goodFunc checks that the function or method has the right result signature. 81 | func goodFunc(typ reflect.Type) bool { 82 | // We allow functions with 1 result or 2 results where the second is an error. 83 | switch { 84 | case typ.NumOut() == 1: 85 | return true 86 | case typ.NumOut() == 2 && typ.Out(1) == errorType: 87 | return true 88 | } 89 | return false 90 | } 91 | 92 | // findFunction looks for a function in the template, and global map. 93 | func findFunction(name string, tmpl *Template) (reflect.Value, bool) { 94 | if tmpl != nil && tmpl.common != nil { 95 | if fn := tmpl.execFuncs[name]; fn.IsValid() { 96 | return fn, true 97 | } 98 | } 99 | if fn := builtinFuncs[name]; fn.IsValid() { 100 | return fn, true 101 | } 102 | return reflect.Value{}, false 103 | } 104 | 105 | // Indexing. 106 | 107 | // index returns the result of indexing its first argument by the following 108 | // arguments. Thus "index x 1 2 3" is, in Go syntax, x[1][2][3]. Each 109 | // indexed item must be a map, slice, or array. 110 | func index(item interface{}, indices ...interface{}) (interface{}, error) { 111 | v := reflect.ValueOf(item) 112 | for _, i := range indices { 113 | index := reflect.ValueOf(i) 114 | var isNil bool 115 | if v, isNil = indirect(v); isNil { 116 | return nil, fmt.Errorf("index of nil pointer") 117 | } 118 | switch v.Kind() { 119 | case reflect.Array, reflect.Slice, reflect.String: 120 | var x int64 121 | switch index.Kind() { 122 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 123 | x = index.Int() 124 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 125 | x = int64(index.Uint()) 126 | default: 127 | return nil, fmt.Errorf("cannot index slice/array with type %s", index.Type()) 128 | } 129 | if x < 0 || x >= int64(v.Len()) { 130 | return nil, fmt.Errorf("index out of range: %d", x) 131 | } 132 | v = v.Index(int(x)) 133 | case reflect.Map: 134 | if !index.IsValid() { 135 | index = reflect.Zero(v.Type().Key()) 136 | } 137 | if !index.Type().AssignableTo(v.Type().Key()) { 138 | return nil, fmt.Errorf("%s is not index type for %s", index.Type(), v.Type()) 139 | } 140 | if x := v.MapIndex(index); x.IsValid() { 141 | v = x 142 | } else { 143 | v = reflect.Zero(v.Type().Elem()) 144 | } 145 | default: 146 | return nil, fmt.Errorf("can't index item of type %s", v.Type()) 147 | } 148 | } 149 | return v.Interface(), nil 150 | } 151 | 152 | // Length 153 | 154 | // length returns the length of the item, with an error if it has no defined length. 155 | func length(item interface{}) (int, error) { 156 | v, isNil := indirect(reflect.ValueOf(item)) 157 | if isNil { 158 | return 0, fmt.Errorf("len of nil pointer") 159 | } 160 | switch v.Kind() { 161 | case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice, reflect.String: 162 | return v.Len(), nil 163 | } 164 | return 0, fmt.Errorf("len of type %s", v.Type()) 165 | } 166 | 167 | // Function invocation 168 | 169 | // call returns the result of evaluating the first argument as a function. 170 | // The function must return 1 result, or 2 results, the second of which is an error. 171 | func call(fn interface{}, args ...interface{}) (interface{}, error) { 172 | v := reflect.ValueOf(fn) 173 | typ := v.Type() 174 | if typ.Kind() != reflect.Func { 175 | return nil, fmt.Errorf("non-function of type %s", typ) 176 | } 177 | if !goodFunc(typ) { 178 | return nil, fmt.Errorf("function called with %d args; should be 1 or 2", typ.NumOut()) 179 | } 180 | numIn := typ.NumIn() 181 | var dddType reflect.Type 182 | if typ.IsVariadic() { 183 | if len(args) < numIn-1 { 184 | return nil, fmt.Errorf("wrong number of args: got %d want at least %d", len(args), numIn-1) 185 | } 186 | dddType = typ.In(numIn - 1).Elem() 187 | } else { 188 | if len(args) != numIn { 189 | return nil, fmt.Errorf("wrong number of args: got %d want %d", len(args), numIn) 190 | } 191 | } 192 | argv := make([]reflect.Value, len(args)) 193 | for i, arg := range args { 194 | value := reflect.ValueOf(arg) 195 | // Compute the expected type. Clumsy because of variadics. 196 | var argType reflect.Type 197 | if !typ.IsVariadic() || i < numIn-1 { 198 | argType = typ.In(i) 199 | } else { 200 | argType = dddType 201 | } 202 | if !value.IsValid() && canBeNil(argType) { 203 | value = reflect.Zero(argType) 204 | } 205 | if !value.Type().AssignableTo(argType) { 206 | return nil, fmt.Errorf("arg %d has type %s; should be %s", i, value.Type(), argType) 207 | } 208 | argv[i] = value 209 | } 210 | result := v.Call(argv) 211 | if len(result) == 2 && !result[1].IsNil() { 212 | return result[0].Interface(), result[1].Interface().(error) 213 | } 214 | return result[0].Interface(), nil 215 | } 216 | 217 | // Boolean logic. 218 | 219 | func truth(a interface{}) bool { 220 | t, _ := isTrue(reflect.ValueOf(a)) 221 | return t 222 | } 223 | 224 | // and computes the Boolean AND of its arguments, returning 225 | // the first false argument it encounters, or the last argument. 226 | func and(arg0 interface{}, args ...interface{}) interface{} { 227 | if !truth(arg0) { 228 | return arg0 229 | } 230 | for i := range args { 231 | arg0 = args[i] 232 | if !truth(arg0) { 233 | break 234 | } 235 | } 236 | return arg0 237 | } 238 | 239 | // or computes the Boolean OR of its arguments, returning 240 | // the first true argument it encounters, or the last argument. 241 | func or(arg0 interface{}, args ...interface{}) interface{} { 242 | if truth(arg0) { 243 | return arg0 244 | } 245 | for i := range args { 246 | arg0 = args[i] 247 | if truth(arg0) { 248 | break 249 | } 250 | } 251 | return arg0 252 | } 253 | 254 | // not returns the Boolean negation of its argument. 255 | func not(arg interface{}) (truth bool) { 256 | truth, _ = isTrue(reflect.ValueOf(arg)) 257 | return !truth 258 | } 259 | 260 | // Comparison. 261 | 262 | // TODO: Perhaps allow comparison between signed and unsigned integers. 263 | 264 | var ( 265 | errBadComparisonType = errors.New("invalid type for comparison") 266 | errBadComparison = errors.New("incompatible types for comparison") 267 | errNoComparison = errors.New("missing argument for comparison") 268 | ) 269 | 270 | type kind int 271 | 272 | const ( 273 | invalidKind kind = iota 274 | boolKind 275 | complexKind 276 | intKind 277 | floatKind 278 | integerKind 279 | stringKind 280 | uintKind 281 | ) 282 | 283 | func basicKind(v reflect.Value) (kind, error) { 284 | switch v.Kind() { 285 | case reflect.Bool: 286 | return boolKind, nil 287 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 288 | return intKind, nil 289 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 290 | return uintKind, nil 291 | case reflect.Float32, reflect.Float64: 292 | return floatKind, nil 293 | case reflect.Complex64, reflect.Complex128: 294 | return complexKind, nil 295 | case reflect.String: 296 | return stringKind, nil 297 | } 298 | return invalidKind, errBadComparisonType 299 | } 300 | 301 | // eq evaluates the comparison a == b || a == c || ... 302 | func eq(arg1 interface{}, arg2 ...interface{}) (bool, error) { 303 | v1 := reflect.ValueOf(arg1) 304 | k1, err := basicKind(v1) 305 | if err != nil { 306 | return false, err 307 | } 308 | if len(arg2) == 0 { 309 | return false, errNoComparison 310 | } 311 | for _, arg := range arg2 { 312 | v2 := reflect.ValueOf(arg) 313 | k2, err := basicKind(v2) 314 | if err != nil { 315 | return false, err 316 | } 317 | truth := false 318 | if k1 != k2 { 319 | // Special case: Can compare integer values regardless of type's sign. 320 | switch { 321 | case k1 == intKind && k2 == uintKind: 322 | truth = v1.Int() >= 0 && uint64(v1.Int()) == v2.Uint() 323 | case k1 == uintKind && k2 == intKind: 324 | truth = v2.Int() >= 0 && v1.Uint() == uint64(v2.Int()) 325 | default: 326 | return false, errBadComparison 327 | } 328 | } else { 329 | switch k1 { 330 | case boolKind: 331 | truth = v1.Bool() == v2.Bool() 332 | case complexKind: 333 | truth = v1.Complex() == v2.Complex() 334 | case floatKind: 335 | truth = v1.Float() == v2.Float() 336 | case intKind: 337 | truth = v1.Int() == v2.Int() 338 | case stringKind: 339 | truth = v1.String() == v2.String() 340 | case uintKind: 341 | truth = v1.Uint() == v2.Uint() 342 | default: 343 | panic("invalid kind") 344 | } 345 | } 346 | if truth { 347 | return true, nil 348 | } 349 | } 350 | return false, nil 351 | } 352 | 353 | // ne evaluates the comparison a != b. 354 | func ne(arg1, arg2 interface{}) (bool, error) { 355 | // != is the inverse of ==. 356 | equal, err := eq(arg1, arg2) 357 | return !equal, err 358 | } 359 | 360 | // lt evaluates the comparison a < b. 361 | func lt(arg1, arg2 interface{}) (bool, error) { 362 | v1 := reflect.ValueOf(arg1) 363 | k1, err := basicKind(v1) 364 | if err != nil { 365 | return false, err 366 | } 367 | v2 := reflect.ValueOf(arg2) 368 | k2, err := basicKind(v2) 369 | if err != nil { 370 | return false, err 371 | } 372 | truth := false 373 | if k1 != k2 { 374 | // Special case: Can compare integer values regardless of type's sign. 375 | switch { 376 | case k1 == intKind && k2 == uintKind: 377 | truth = v1.Int() < 0 || uint64(v1.Int()) < v2.Uint() 378 | case k1 == uintKind && k2 == intKind: 379 | truth = v2.Int() >= 0 && v1.Uint() < uint64(v2.Int()) 380 | default: 381 | return false, errBadComparison 382 | } 383 | } else { 384 | switch k1 { 385 | case boolKind, complexKind: 386 | return false, errBadComparisonType 387 | case floatKind: 388 | truth = v1.Float() < v2.Float() 389 | case intKind: 390 | truth = v1.Int() < v2.Int() 391 | case stringKind: 392 | truth = v1.String() < v2.String() 393 | case uintKind: 394 | truth = v1.Uint() < v2.Uint() 395 | default: 396 | panic("invalid kind") 397 | } 398 | } 399 | return truth, nil 400 | } 401 | 402 | // le evaluates the comparison <= b. 403 | func le(arg1, arg2 interface{}) (bool, error) { 404 | // <= is < or ==. 405 | lessThan, err := lt(arg1, arg2) 406 | if lessThan || err != nil { 407 | return lessThan, err 408 | } 409 | return eq(arg1, arg2) 410 | } 411 | 412 | // gt evaluates the comparison a > b. 413 | func gt(arg1, arg2 interface{}) (bool, error) { 414 | // > is the inverse of <=. 415 | lessOrEqual, err := le(arg1, arg2) 416 | if err != nil { 417 | return false, err 418 | } 419 | return !lessOrEqual, nil 420 | } 421 | 422 | // ge evaluates the comparison a >= b. 423 | func ge(arg1, arg2 interface{}) (bool, error) { 424 | // >= is the inverse of <. 425 | lessThan, err := lt(arg1, arg2) 426 | if err != nil { 427 | return false, err 428 | } 429 | return !lessThan, nil 430 | } 431 | 432 | // HTML escaping. 433 | 434 | var ( 435 | htmlQuot = []byte(""") // shorter than """ 436 | htmlApos = []byte("'") // shorter than "'" and apos was not in HTML until HTML5 437 | htmlAmp = []byte("&") 438 | htmlLt = []byte("<") 439 | htmlGt = []byte(">") 440 | ) 441 | 442 | // HTMLEscape writes to w the escaped HTML equivalent of the plain text data b. 443 | func HTMLEscape(w io.Writer, b []byte) { 444 | last := 0 445 | for i, c := range b { 446 | var html []byte 447 | switch c { 448 | case '"': 449 | html = htmlQuot 450 | case '\'': 451 | html = htmlApos 452 | case '&': 453 | html = htmlAmp 454 | case '<': 455 | html = htmlLt 456 | case '>': 457 | html = htmlGt 458 | default: 459 | continue 460 | } 461 | w.Write(b[last:i]) 462 | w.Write(html) 463 | last = i + 1 464 | } 465 | w.Write(b[last:]) 466 | } 467 | 468 | // HTMLEscapeString returns the escaped HTML equivalent of the plain text data s. 469 | func HTMLEscapeString(s string) string { 470 | // Avoid allocation if we can. 471 | if strings.IndexAny(s, `'"&<>`) < 0 { 472 | return s 473 | } 474 | var b bytes.Buffer 475 | HTMLEscape(&b, []byte(s)) 476 | return b.String() 477 | } 478 | 479 | // HTMLEscaper returns the escaped HTML equivalent of the textual 480 | // representation of its arguments. 481 | func HTMLEscaper(args ...interface{}) string { 482 | return HTMLEscapeString(evalArgs(args)) 483 | } 484 | 485 | // JavaScript escaping. 486 | 487 | var ( 488 | jsLowUni = []byte(`\u00`) 489 | hex = []byte("0123456789ABCDEF") 490 | 491 | jsBackslash = []byte(`\\`) 492 | jsApos = []byte(`\'`) 493 | jsQuot = []byte(`\"`) 494 | jsLt = []byte(`\x3C`) 495 | jsGt = []byte(`\x3E`) 496 | ) 497 | 498 | // JSEscape writes to w the escaped JavaScript equivalent of the plain text data b. 499 | func JSEscape(w io.Writer, b []byte) { 500 | last := 0 501 | for i := 0; i < len(b); i++ { 502 | c := b[i] 503 | 504 | if !jsIsSpecial(rune(c)) { 505 | // fast path: nothing to do 506 | continue 507 | } 508 | w.Write(b[last:i]) 509 | 510 | if c < utf8.RuneSelf { 511 | // Quotes, slashes and angle brackets get quoted. 512 | // Control characters get written as \u00XX. 513 | switch c { 514 | case '\\': 515 | w.Write(jsBackslash) 516 | case '\'': 517 | w.Write(jsApos) 518 | case '"': 519 | w.Write(jsQuot) 520 | case '<': 521 | w.Write(jsLt) 522 | case '>': 523 | w.Write(jsGt) 524 | default: 525 | w.Write(jsLowUni) 526 | t, b := c>>4, c&0x0f 527 | w.Write(hex[t : t+1]) 528 | w.Write(hex[b : b+1]) 529 | } 530 | } else { 531 | // Unicode rune. 532 | r, size := utf8.DecodeRune(b[i:]) 533 | if unicode.IsPrint(r) { 534 | w.Write(b[i : i+size]) 535 | } else { 536 | fmt.Fprintf(w, "\\u%04X", r) 537 | } 538 | i += size - 1 539 | } 540 | last = i + 1 541 | } 542 | w.Write(b[last:]) 543 | } 544 | 545 | // JSEscapeString returns the escaped JavaScript equivalent of the plain text data s. 546 | func JSEscapeString(s string) string { 547 | // Avoid allocation if we can. 548 | if strings.IndexFunc(s, jsIsSpecial) < 0 { 549 | return s 550 | } 551 | var b bytes.Buffer 552 | JSEscape(&b, []byte(s)) 553 | return b.String() 554 | } 555 | 556 | func jsIsSpecial(r rune) bool { 557 | switch r { 558 | case '\\', '\'', '"', '<', '>': 559 | return true 560 | } 561 | return r < ' ' || utf8.RuneSelf <= r 562 | } 563 | 564 | // JSEscaper returns the escaped JavaScript equivalent of the textual 565 | // representation of its arguments. 566 | func JSEscaper(args ...interface{}) string { 567 | return JSEscapeString(evalArgs(args)) 568 | } 569 | 570 | // URLQueryEscaper returns the escaped value of the textual representation of 571 | // its arguments in a form suitable for embedding in a URL query. 572 | func URLQueryEscaper(args ...interface{}) string { 573 | return url.QueryEscape(evalArgs(args)) 574 | } 575 | 576 | // evalArgs formats the list of arguments into a string. It is therefore equivalent to 577 | // fmt.Sprint(args...) 578 | // except that each argument is indirected (if a pointer), as required, 579 | // using the same rules as the default string evaluation during template 580 | // execution. 581 | func evalArgs(args []interface{}) string { 582 | ok := false 583 | var s string 584 | // Fast path for simple common case. 585 | if len(args) == 1 { 586 | s, ok = args[0].(string) 587 | } 588 | if !ok { 589 | for i, arg := range args { 590 | a, ok := printableValue(reflect.ValueOf(arg)) 591 | if ok { 592 | args[i] = a 593 | } // else left fmt do its thing 594 | } 595 | s = fmt.Sprint(args...) 596 | } 597 | return s 598 | } 599 | -------------------------------------------------------------------------------- /vendor/gopkg.in/alecthomas/kingpin.v2/app.go: -------------------------------------------------------------------------------- 1 | package kingpin 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "os" 7 | "regexp" 8 | "strings" 9 | ) 10 | 11 | var ( 12 | ErrCommandNotSpecified = fmt.Errorf("command not specified") 13 | ) 14 | 15 | var ( 16 | envarTransformRegexp = regexp.MustCompile(`[^a-zA-Z_]+`) 17 | ) 18 | 19 | type ApplicationValidator func(*Application) error 20 | 21 | // An Application contains the definitions of flags, arguments and commands 22 | // for an application. 23 | type Application struct { 24 | cmdMixin 25 | initialized bool 26 | 27 | Name string 28 | Help string 29 | 30 | author string 31 | version string 32 | writer io.Writer // Destination for usage and errors. 33 | usageTemplate string 34 | validator ApplicationValidator 35 | terminate func(status int) // See Terminate() 36 | noInterspersed bool // can flags be interspersed with args (or must they come first) 37 | defaultEnvars bool 38 | completion bool 39 | 40 | // Help flag. Exposed for user customisation. 41 | HelpFlag *FlagClause 42 | // Help command. Exposed for user customisation. May be nil. 43 | HelpCommand *CmdClause 44 | // Version flag. Exposed for user customisation. May be nil. 45 | VersionFlag *FlagClause 46 | } 47 | 48 | // New creates a new Kingpin application instance. 49 | func New(name, help string) *Application { 50 | a := &Application{ 51 | Name: name, 52 | Help: help, 53 | writer: os.Stderr, 54 | usageTemplate: DefaultUsageTemplate, 55 | terminate: os.Exit, 56 | } 57 | a.flagGroup = newFlagGroup() 58 | a.argGroup = newArgGroup() 59 | a.cmdGroup = newCmdGroup(a) 60 | a.HelpFlag = a.Flag("help", "Show context-sensitive help (also try --help-long and --help-man).") 61 | a.HelpFlag.Bool() 62 | a.Flag("help-long", "Generate long help.").Hidden().PreAction(a.generateLongHelp).Bool() 63 | a.Flag("help-man", "Generate a man page.").Hidden().PreAction(a.generateManPage).Bool() 64 | a.Flag("completion-bash", "Output possible completions for the given args.").Hidden().BoolVar(&a.completion) 65 | a.Flag("completion-script-bash", "Generate completion script for bash.").Hidden().PreAction(a.generateBashCompletionScript).Bool() 66 | a.Flag("completion-script-zsh", "Generate completion script for ZSH.").Hidden().PreAction(a.generateZSHCompletionScript).Bool() 67 | 68 | return a 69 | } 70 | 71 | func (a *Application) generateLongHelp(c *ParseContext) error { 72 | a.Writer(os.Stdout) 73 | if err := a.UsageForContextWithTemplate(c, 2, LongHelpTemplate); err != nil { 74 | return err 75 | } 76 | a.terminate(0) 77 | return nil 78 | } 79 | 80 | func (a *Application) generateManPage(c *ParseContext) error { 81 | a.Writer(os.Stdout) 82 | if err := a.UsageForContextWithTemplate(c, 2, ManPageTemplate); err != nil { 83 | return err 84 | } 85 | a.terminate(0) 86 | return nil 87 | } 88 | 89 | func (a *Application) generateBashCompletionScript(c *ParseContext) error { 90 | a.Writer(os.Stdout) 91 | if err := a.UsageForContextWithTemplate(c, 2, BashCompletionTemplate); err != nil { 92 | return err 93 | } 94 | a.terminate(0) 95 | return nil 96 | } 97 | 98 | func (a *Application) generateZSHCompletionScript(c *ParseContext) error { 99 | a.Writer(os.Stdout) 100 | if err := a.UsageForContextWithTemplate(c, 2, ZshCompletionTemplate); err != nil { 101 | return err 102 | } 103 | a.terminate(0) 104 | return nil 105 | } 106 | 107 | // DefaultEnvars configures all flags (that do not already have an associated 108 | // envar) to use a default environment variable in the form "_". 109 | // 110 | // For example, if the application is named "foo" and a flag is named "bar- 111 | // waz" the environment variable: "FOO_BAR_WAZ". 112 | func (a *Application) DefaultEnvars() *Application { 113 | a.defaultEnvars = true 114 | return a 115 | } 116 | 117 | // Terminate specifies the termination handler. Defaults to os.Exit(status). 118 | // If nil is passed, a no-op function will be used. 119 | func (a *Application) Terminate(terminate func(int)) *Application { 120 | if terminate == nil { 121 | terminate = func(int) {} 122 | } 123 | a.terminate = terminate 124 | return a 125 | } 126 | 127 | // Specify the writer to use for usage and errors. Defaults to os.Stderr. 128 | func (a *Application) Writer(w io.Writer) *Application { 129 | a.writer = w 130 | return a 131 | } 132 | 133 | // UsageTemplate specifies the text template to use when displaying usage 134 | // information. The default is UsageTemplate. 135 | func (a *Application) UsageTemplate(template string) *Application { 136 | a.usageTemplate = template 137 | return a 138 | } 139 | 140 | // Validate sets a validation function to run when parsing. 141 | func (a *Application) Validate(validator ApplicationValidator) *Application { 142 | a.validator = validator 143 | return a 144 | } 145 | 146 | // ParseContext parses the given command line and returns the fully populated 147 | // ParseContext. 148 | func (a *Application) ParseContext(args []string) (*ParseContext, error) { 149 | return a.parseContext(false, args) 150 | } 151 | 152 | func (a *Application) parseContext(ignoreDefault bool, args []string) (*ParseContext, error) { 153 | if err := a.init(); err != nil { 154 | return nil, err 155 | } 156 | context := tokenize(args, ignoreDefault) 157 | err := parse(context, a) 158 | return context, err 159 | } 160 | 161 | // Parse parses command-line arguments. It returns the selected command and an 162 | // error. The selected command will be a space separated subcommand, if 163 | // subcommands have been configured. 164 | // 165 | // This will populate all flag and argument values, call all callbacks, and so 166 | // on. 167 | func (a *Application) Parse(args []string) (command string, err error) { 168 | 169 | context, parseErr := a.ParseContext(args) 170 | selected := []string{} 171 | var setValuesErr error 172 | 173 | if context == nil { 174 | // Since we do not throw error immediately, there could be a case 175 | // where a context returns nil. Protect against that. 176 | return "", parseErr 177 | } 178 | 179 | if err = a.setDefaults(context); err != nil { 180 | return "", err 181 | } 182 | 183 | selected, setValuesErr = a.setValues(context) 184 | 185 | if err = a.applyPreActions(context, !a.completion); err != nil { 186 | return "", err 187 | } 188 | 189 | if a.completion { 190 | a.generateBashCompletion(context) 191 | a.terminate(0) 192 | } else { 193 | if parseErr != nil { 194 | return "", parseErr 195 | } 196 | 197 | a.maybeHelp(context) 198 | if !context.EOL() { 199 | return "", fmt.Errorf("unexpected argument '%s'", context.Peek()) 200 | } 201 | 202 | if setValuesErr != nil { 203 | return "", setValuesErr 204 | } 205 | 206 | command, err = a.execute(context, selected) 207 | if err == ErrCommandNotSpecified { 208 | a.writeUsage(context, nil) 209 | } 210 | } 211 | return command, err 212 | } 213 | 214 | func (a *Application) writeUsage(context *ParseContext, err error) { 215 | if err != nil { 216 | a.Errorf("%s", err) 217 | } 218 | if err := a.UsageForContext(context); err != nil { 219 | panic(err) 220 | } 221 | a.terminate(1) 222 | } 223 | 224 | func (a *Application) maybeHelp(context *ParseContext) { 225 | for _, element := range context.Elements { 226 | if flag, ok := element.Clause.(*FlagClause); ok && flag == a.HelpFlag { 227 | a.writeUsage(context, nil) 228 | } 229 | } 230 | } 231 | 232 | // findCommandFromArgs finds a command (if any) from the given command line arguments. 233 | func (a *Application) findCommandFromArgs(args []string) (command string, err error) { 234 | if err := a.init(); err != nil { 235 | return "", err 236 | } 237 | context := tokenize(args, false) 238 | if _, err := a.parse(context); err != nil { 239 | return "", err 240 | } 241 | return a.findCommandFromContext(context), nil 242 | } 243 | 244 | // findCommandFromContext finds a command (if any) from a parsed context. 245 | func (a *Application) findCommandFromContext(context *ParseContext) string { 246 | commands := []string{} 247 | for _, element := range context.Elements { 248 | if c, ok := element.Clause.(*CmdClause); ok { 249 | commands = append(commands, c.name) 250 | } 251 | } 252 | return strings.Join(commands, " ") 253 | } 254 | 255 | // Version adds a --version flag for displaying the application version. 256 | func (a *Application) Version(version string) *Application { 257 | a.version = version 258 | a.VersionFlag = a.Flag("version", "Show application version.").PreAction(func(*ParseContext) error { 259 | fmt.Fprintln(a.writer, version) 260 | a.terminate(0) 261 | return nil 262 | }) 263 | a.VersionFlag.Bool() 264 | return a 265 | } 266 | 267 | func (a *Application) Author(author string) *Application { 268 | a.author = author 269 | return a 270 | } 271 | 272 | // Action callback to call when all values are populated and parsing is 273 | // complete, but before any command, flag or argument actions. 274 | // 275 | // All Action() callbacks are called in the order they are encountered on the 276 | // command line. 277 | func (a *Application) Action(action Action) *Application { 278 | a.addAction(action) 279 | return a 280 | } 281 | 282 | // Action called after parsing completes but before validation and execution. 283 | func (a *Application) PreAction(action Action) *Application { 284 | a.addPreAction(action) 285 | return a 286 | } 287 | 288 | // Command adds a new top-level command. 289 | func (a *Application) Command(name, help string) *CmdClause { 290 | return a.addCommand(name, help) 291 | } 292 | 293 | // Interspersed control if flags can be interspersed with positional arguments 294 | // 295 | // true (the default) means that they can, false means that all the flags must appear before the first positional arguments. 296 | func (a *Application) Interspersed(interspersed bool) *Application { 297 | a.noInterspersed = !interspersed 298 | return a 299 | } 300 | 301 | func (a *Application) defaultEnvarPrefix() string { 302 | if a.defaultEnvars { 303 | return a.Name 304 | } 305 | return "" 306 | } 307 | 308 | func (a *Application) init() error { 309 | if a.initialized { 310 | return nil 311 | } 312 | if a.cmdGroup.have() && a.argGroup.have() { 313 | return fmt.Errorf("can't mix top-level Arg()s with Command()s") 314 | } 315 | 316 | // If we have subcommands, add a help command at the top-level. 317 | if a.cmdGroup.have() { 318 | var command []string 319 | a.HelpCommand = a.Command("help", "Show help.").PreAction(func(context *ParseContext) error { 320 | a.Usage(command) 321 | a.terminate(0) 322 | return nil 323 | }) 324 | a.HelpCommand.Arg("command", "Show help on command.").StringsVar(&command) 325 | // Make help first command. 326 | l := len(a.commandOrder) 327 | a.commandOrder = append(a.commandOrder[l-1:l], a.commandOrder[:l-1]...) 328 | } 329 | 330 | if err := a.flagGroup.init(a.defaultEnvarPrefix()); err != nil { 331 | return err 332 | } 333 | if err := a.cmdGroup.init(); err != nil { 334 | return err 335 | } 336 | if err := a.argGroup.init(); err != nil { 337 | return err 338 | } 339 | for _, cmd := range a.commands { 340 | if err := cmd.init(); err != nil { 341 | return err 342 | } 343 | } 344 | flagGroups := []*flagGroup{a.flagGroup} 345 | for _, cmd := range a.commandOrder { 346 | if err := checkDuplicateFlags(cmd, flagGroups); err != nil { 347 | return err 348 | } 349 | } 350 | a.initialized = true 351 | return nil 352 | } 353 | 354 | // Recursively check commands for duplicate flags. 355 | func checkDuplicateFlags(current *CmdClause, flagGroups []*flagGroup) error { 356 | // Check for duplicates. 357 | for _, flags := range flagGroups { 358 | for _, flag := range current.flagOrder { 359 | if flag.shorthand != 0 { 360 | if _, ok := flags.short[string(flag.shorthand)]; ok { 361 | return fmt.Errorf("duplicate short flag -%c", flag.shorthand) 362 | } 363 | } 364 | if _, ok := flags.long[flag.name]; ok { 365 | return fmt.Errorf("duplicate long flag --%s", flag.name) 366 | } 367 | } 368 | } 369 | flagGroups = append(flagGroups, current.flagGroup) 370 | // Check subcommands. 371 | for _, subcmd := range current.commandOrder { 372 | if err := checkDuplicateFlags(subcmd, flagGroups); err != nil { 373 | return err 374 | } 375 | } 376 | return nil 377 | } 378 | 379 | func (a *Application) execute(context *ParseContext, selected []string) (string, error) { 380 | var err error 381 | 382 | if err = a.validateRequired(context); err != nil { 383 | return "", err 384 | } 385 | 386 | if err = a.applyValidators(context); err != nil { 387 | return "", err 388 | } 389 | 390 | if err = a.applyActions(context); err != nil { 391 | return "", err 392 | } 393 | 394 | command := strings.Join(selected, " ") 395 | if command == "" && a.cmdGroup.have() { 396 | return "", ErrCommandNotSpecified 397 | } 398 | return command, err 399 | } 400 | 401 | func (a *Application) setDefaults(context *ParseContext) error { 402 | flagElements := map[string]*ParseElement{} 403 | for _, element := range context.Elements { 404 | if flag, ok := element.Clause.(*FlagClause); ok { 405 | flagElements[flag.name] = element 406 | } 407 | } 408 | 409 | argElements := map[string]*ParseElement{} 410 | for _, element := range context.Elements { 411 | if arg, ok := element.Clause.(*ArgClause); ok { 412 | argElements[arg.name] = element 413 | } 414 | } 415 | 416 | // Check required flags and set defaults. 417 | for _, flag := range context.flags.long { 418 | if flagElements[flag.name] == nil { 419 | if err := flag.setDefault(); err != nil { 420 | return err 421 | } 422 | } 423 | } 424 | 425 | for _, arg := range context.arguments.args { 426 | if argElements[arg.name] == nil { 427 | // Set defaults, if any. 428 | for _, defaultValue := range arg.defaultValues { 429 | if err := arg.value.Set(defaultValue); err != nil { 430 | return err 431 | } 432 | } 433 | } 434 | } 435 | 436 | return nil 437 | } 438 | 439 | func (a *Application) validateRequired(context *ParseContext) error { 440 | flagElements := map[string]*ParseElement{} 441 | for _, element := range context.Elements { 442 | if flag, ok := element.Clause.(*FlagClause); ok { 443 | flagElements[flag.name] = element 444 | } 445 | } 446 | 447 | argElements := map[string]*ParseElement{} 448 | for _, element := range context.Elements { 449 | if arg, ok := element.Clause.(*ArgClause); ok { 450 | argElements[arg.name] = element 451 | } 452 | } 453 | 454 | // Check required flags and set defaults. 455 | for _, flag := range context.flags.long { 456 | if flagElements[flag.name] == nil { 457 | // Check required flags were provided. 458 | if flag.needsValue() { 459 | return fmt.Errorf("required flag --%s not provided", flag.name) 460 | } 461 | } 462 | } 463 | 464 | for _, arg := range context.arguments.args { 465 | if argElements[arg.name] == nil { 466 | if arg.required { 467 | return fmt.Errorf("required argument '%s' not provided", arg.name) 468 | } 469 | } 470 | } 471 | return nil 472 | } 473 | 474 | func (a *Application) setValues(context *ParseContext) (selected []string, err error) { 475 | // Set all arg and flag values. 476 | var ( 477 | lastCmd *CmdClause 478 | flagSet = map[string]struct{}{} 479 | ) 480 | for _, element := range context.Elements { 481 | switch clause := element.Clause.(type) { 482 | case *FlagClause: 483 | if _, ok := flagSet[clause.name]; ok { 484 | if v, ok := clause.value.(repeatableFlag); !ok || !v.IsCumulative() { 485 | return nil, fmt.Errorf("flag '%s' cannot be repeated", clause.name) 486 | } 487 | } 488 | if err = clause.value.Set(*element.Value); err != nil { 489 | return 490 | } 491 | flagSet[clause.name] = struct{}{} 492 | 493 | case *ArgClause: 494 | if err = clause.value.Set(*element.Value); err != nil { 495 | return 496 | } 497 | 498 | case *CmdClause: 499 | if clause.validator != nil { 500 | if err = clause.validator(clause); err != nil { 501 | return 502 | } 503 | } 504 | selected = append(selected, clause.name) 505 | lastCmd = clause 506 | } 507 | } 508 | 509 | if lastCmd != nil && len(lastCmd.commands) > 0 { 510 | return nil, fmt.Errorf("must select a subcommand of '%s'", lastCmd.FullCommand()) 511 | } 512 | 513 | return 514 | } 515 | 516 | func (a *Application) applyValidators(context *ParseContext) (err error) { 517 | // Call command validation functions. 518 | for _, element := range context.Elements { 519 | if cmd, ok := element.Clause.(*CmdClause); ok && cmd.validator != nil { 520 | if err = cmd.validator(cmd); err != nil { 521 | return err 522 | } 523 | } 524 | } 525 | 526 | if a.validator != nil { 527 | err = a.validator(a) 528 | } 529 | return err 530 | } 531 | 532 | func (a *Application) applyPreActions(context *ParseContext, dispatch bool) error { 533 | if err := a.actionMixin.applyPreActions(context); err != nil { 534 | return err 535 | } 536 | // Dispatch to actions. 537 | if dispatch { 538 | for _, element := range context.Elements { 539 | if applier, ok := element.Clause.(actionApplier); ok { 540 | if err := applier.applyPreActions(context); err != nil { 541 | return err 542 | } 543 | } 544 | } 545 | } 546 | 547 | return nil 548 | } 549 | 550 | func (a *Application) applyActions(context *ParseContext) error { 551 | if err := a.actionMixin.applyActions(context); err != nil { 552 | return err 553 | } 554 | // Dispatch to actions. 555 | for _, element := range context.Elements { 556 | if applier, ok := element.Clause.(actionApplier); ok { 557 | if err := applier.applyActions(context); err != nil { 558 | return err 559 | } 560 | } 561 | } 562 | return nil 563 | } 564 | 565 | // Errorf prints an error message to w in the format ": error: ". 566 | func (a *Application) Errorf(format string, args ...interface{}) { 567 | fmt.Fprintf(a.writer, a.Name+": error: "+format+"\n", args...) 568 | } 569 | 570 | // Fatalf writes a formatted error to w then terminates with exit status 1. 571 | func (a *Application) Fatalf(format string, args ...interface{}) { 572 | a.Errorf(format, args...) 573 | a.terminate(1) 574 | } 575 | 576 | // FatalUsage prints an error message followed by usage information, then 577 | // exits with a non-zero status. 578 | func (a *Application) FatalUsage(format string, args ...interface{}) { 579 | a.Errorf(format, args...) 580 | a.Usage([]string{}) 581 | a.terminate(1) 582 | } 583 | 584 | // FatalUsageContext writes a printf formatted error message to w, then usage 585 | // information for the given ParseContext, before exiting. 586 | func (a *Application) FatalUsageContext(context *ParseContext, format string, args ...interface{}) { 587 | a.Errorf(format, args...) 588 | if err := a.UsageForContext(context); err != nil { 589 | panic(err) 590 | } 591 | a.terminate(1) 592 | } 593 | 594 | // FatalIfError prints an error and exits if err is not nil. The error is printed 595 | // with the given formatted string, if any. 596 | func (a *Application) FatalIfError(err error, format string, args ...interface{}) { 597 | if err != nil { 598 | prefix := "" 599 | if format != "" { 600 | prefix = fmt.Sprintf(format, args...) + ": " 601 | } 602 | a.Errorf(prefix+"%s", err) 603 | a.terminate(1) 604 | } 605 | } 606 | 607 | func (a *Application) completionOptions(context *ParseContext) []string { 608 | args := context.rawArgs 609 | 610 | var ( 611 | currArg string 612 | prevArg string 613 | target cmdMixin 614 | ) 615 | 616 | numArgs := len(args) 617 | if numArgs > 1 { 618 | args = args[1:] 619 | currArg = args[len(args)-1] 620 | } 621 | if numArgs > 2 { 622 | prevArg = args[len(args)-2] 623 | } 624 | 625 | target = a.cmdMixin 626 | if context.SelectedCommand != nil { 627 | // A subcommand was in use. We will use it as the target 628 | target = context.SelectedCommand.cmdMixin 629 | } 630 | 631 | if (currArg != "" && strings.HasPrefix(currArg, "--")) || strings.HasPrefix(prevArg, "--") { 632 | // Perform completion for A flag. The last/current argument started with "-" 633 | var ( 634 | flagName string // The name of a flag if given (could be half complete) 635 | flagValue string // The value assigned to a flag (if given) (could be half complete) 636 | ) 637 | 638 | if strings.HasPrefix(prevArg, "--") && !strings.HasPrefix(currArg, "--") { 639 | // Matches: ./myApp --flag value 640 | // Wont Match: ./myApp --flag -- 641 | flagName = prevArg[2:] // Strip the "--" 642 | flagValue = currArg 643 | } else if strings.HasPrefix(currArg, "--") { 644 | // Matches: ./myApp --flag -- 645 | // Matches: ./myApp --flag somevalue -- 646 | // Matches: ./myApp -- 647 | flagName = currArg[2:] // Strip the "--" 648 | } 649 | 650 | options, flagMatched, valueMatched := target.FlagCompletion(flagName, flagValue) 651 | if valueMatched { 652 | // Value Matched. Show cmdCompletions 653 | return target.CmdCompletion() 654 | } 655 | 656 | // Add top level flags if we're not at the top level and no match was found. 657 | if context.SelectedCommand != nil && flagMatched == false { 658 | topOptions, topFlagMatched, topValueMatched := a.FlagCompletion(flagName, flagValue) 659 | if topValueMatched { 660 | // Value Matched. Back to cmdCompletions 661 | return target.CmdCompletion() 662 | } 663 | 664 | if topFlagMatched { 665 | // Top level had a flag which matched the input. Return it's options. 666 | options = topOptions 667 | } else { 668 | // Add top level flags 669 | options = append(options, topOptions...) 670 | } 671 | } 672 | return options 673 | } else { 674 | // Perform completion for sub commands. 675 | return target.CmdCompletion() 676 | } 677 | } 678 | 679 | func (a *Application) generateBashCompletion(context *ParseContext) { 680 | options := a.completionOptions(context) 681 | fmt.Printf("%s", strings.Join(options, "\n")) 682 | } 683 | 684 | func envarTransform(name string) string { 685 | return strings.ToUpper(envarTransformRegexp.ReplaceAllString(name, "_")) 686 | } 687 | -------------------------------------------------------------------------------- /vendor/github.com/alecthomas/template/parse/parse.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 parse builds parse trees for templates as defined by text/template 6 | // and html/template. Clients should use those packages to construct templates 7 | // rather than this one, which provides shared internal data structures not 8 | // intended for general use. 9 | package parse 10 | 11 | import ( 12 | "bytes" 13 | "fmt" 14 | "runtime" 15 | "strconv" 16 | "strings" 17 | ) 18 | 19 | // Tree is the representation of a single parsed template. 20 | type Tree struct { 21 | Name string // name of the template represented by the tree. 22 | ParseName string // name of the top-level template during parsing, for error messages. 23 | Root *ListNode // top-level root of the tree. 24 | text string // text parsed to create the template (or its parent) 25 | // Parsing only; cleared after parse. 26 | funcs []map[string]interface{} 27 | lex *lexer 28 | token [3]item // three-token lookahead for parser. 29 | peekCount int 30 | vars []string // variables defined at the moment. 31 | } 32 | 33 | // Copy returns a copy of the Tree. Any parsing state is discarded. 34 | func (t *Tree) Copy() *Tree { 35 | if t == nil { 36 | return nil 37 | } 38 | return &Tree{ 39 | Name: t.Name, 40 | ParseName: t.ParseName, 41 | Root: t.Root.CopyList(), 42 | text: t.text, 43 | } 44 | } 45 | 46 | // Parse returns a map from template name to parse.Tree, created by parsing the 47 | // templates described in the argument string. The top-level template will be 48 | // given the specified name. If an error is encountered, parsing stops and an 49 | // empty map is returned with the error. 50 | func Parse(name, text, leftDelim, rightDelim string, funcs ...map[string]interface{}) (treeSet map[string]*Tree, err error) { 51 | treeSet = make(map[string]*Tree) 52 | t := New(name) 53 | t.text = text 54 | _, err = t.Parse(text, leftDelim, rightDelim, treeSet, funcs...) 55 | return 56 | } 57 | 58 | // next returns the next token. 59 | func (t *Tree) next() item { 60 | if t.peekCount > 0 { 61 | t.peekCount-- 62 | } else { 63 | t.token[0] = t.lex.nextItem() 64 | } 65 | return t.token[t.peekCount] 66 | } 67 | 68 | // backup backs the input stream up one token. 69 | func (t *Tree) backup() { 70 | t.peekCount++ 71 | } 72 | 73 | // backup2 backs the input stream up two tokens. 74 | // The zeroth token is already there. 75 | func (t *Tree) backup2(t1 item) { 76 | t.token[1] = t1 77 | t.peekCount = 2 78 | } 79 | 80 | // backup3 backs the input stream up three tokens 81 | // The zeroth token is already there. 82 | func (t *Tree) backup3(t2, t1 item) { // Reverse order: we're pushing back. 83 | t.token[1] = t1 84 | t.token[2] = t2 85 | t.peekCount = 3 86 | } 87 | 88 | // peek returns but does not consume the next token. 89 | func (t *Tree) peek() item { 90 | if t.peekCount > 0 { 91 | return t.token[t.peekCount-1] 92 | } 93 | t.peekCount = 1 94 | t.token[0] = t.lex.nextItem() 95 | return t.token[0] 96 | } 97 | 98 | // nextNonSpace returns the next non-space token. 99 | func (t *Tree) nextNonSpace() (token item) { 100 | for { 101 | token = t.next() 102 | if token.typ != itemSpace { 103 | break 104 | } 105 | } 106 | return token 107 | } 108 | 109 | // peekNonSpace returns but does not consume the next non-space token. 110 | func (t *Tree) peekNonSpace() (token item) { 111 | for { 112 | token = t.next() 113 | if token.typ != itemSpace { 114 | break 115 | } 116 | } 117 | t.backup() 118 | return token 119 | } 120 | 121 | // Parsing. 122 | 123 | // New allocates a new parse tree with the given name. 124 | func New(name string, funcs ...map[string]interface{}) *Tree { 125 | return &Tree{ 126 | Name: name, 127 | funcs: funcs, 128 | } 129 | } 130 | 131 | // ErrorContext returns a textual representation of the location of the node in the input text. 132 | // The receiver is only used when the node does not have a pointer to the tree inside, 133 | // which can occur in old code. 134 | func (t *Tree) ErrorContext(n Node) (location, context string) { 135 | pos := int(n.Position()) 136 | tree := n.tree() 137 | if tree == nil { 138 | tree = t 139 | } 140 | text := tree.text[:pos] 141 | byteNum := strings.LastIndex(text, "\n") 142 | if byteNum == -1 { 143 | byteNum = pos // On first line. 144 | } else { 145 | byteNum++ // After the newline. 146 | byteNum = pos - byteNum 147 | } 148 | lineNum := 1 + strings.Count(text, "\n") 149 | context = n.String() 150 | if len(context) > 20 { 151 | context = fmt.Sprintf("%.20s...", context) 152 | } 153 | return fmt.Sprintf("%s:%d:%d", tree.ParseName, lineNum, byteNum), context 154 | } 155 | 156 | // errorf formats the error and terminates processing. 157 | func (t *Tree) errorf(format string, args ...interface{}) { 158 | t.Root = nil 159 | format = fmt.Sprintf("template: %s:%d: %s", t.ParseName, t.lex.lineNumber(), format) 160 | panic(fmt.Errorf(format, args...)) 161 | } 162 | 163 | // error terminates processing. 164 | func (t *Tree) error(err error) { 165 | t.errorf("%s", err) 166 | } 167 | 168 | // expect consumes the next token and guarantees it has the required type. 169 | func (t *Tree) expect(expected itemType, context string) item { 170 | token := t.nextNonSpace() 171 | if token.typ != expected { 172 | t.unexpected(token, context) 173 | } 174 | return token 175 | } 176 | 177 | // expectOneOf consumes the next token and guarantees it has one of the required types. 178 | func (t *Tree) expectOneOf(expected1, expected2 itemType, context string) item { 179 | token := t.nextNonSpace() 180 | if token.typ != expected1 && token.typ != expected2 { 181 | t.unexpected(token, context) 182 | } 183 | return token 184 | } 185 | 186 | // unexpected complains about the token and terminates processing. 187 | func (t *Tree) unexpected(token item, context string) { 188 | t.errorf("unexpected %s in %s", token, context) 189 | } 190 | 191 | // recover is the handler that turns panics into returns from the top level of Parse. 192 | func (t *Tree) recover(errp *error) { 193 | e := recover() 194 | if e != nil { 195 | if _, ok := e.(runtime.Error); ok { 196 | panic(e) 197 | } 198 | if t != nil { 199 | t.stopParse() 200 | } 201 | *errp = e.(error) 202 | } 203 | return 204 | } 205 | 206 | // startParse initializes the parser, using the lexer. 207 | func (t *Tree) startParse(funcs []map[string]interface{}, lex *lexer) { 208 | t.Root = nil 209 | t.lex = lex 210 | t.vars = []string{"$"} 211 | t.funcs = funcs 212 | } 213 | 214 | // stopParse terminates parsing. 215 | func (t *Tree) stopParse() { 216 | t.lex = nil 217 | t.vars = nil 218 | t.funcs = nil 219 | } 220 | 221 | // Parse parses the template definition string to construct a representation of 222 | // the template for execution. If either action delimiter string is empty, the 223 | // default ("{{" or "}}") is used. Embedded template definitions are added to 224 | // the treeSet map. 225 | func (t *Tree) Parse(text, leftDelim, rightDelim string, treeSet map[string]*Tree, funcs ...map[string]interface{}) (tree *Tree, err error) { 226 | defer t.recover(&err) 227 | t.ParseName = t.Name 228 | t.startParse(funcs, lex(t.Name, text, leftDelim, rightDelim)) 229 | t.text = text 230 | t.parse(treeSet) 231 | t.add(treeSet) 232 | t.stopParse() 233 | return t, nil 234 | } 235 | 236 | // add adds tree to the treeSet. 237 | func (t *Tree) add(treeSet map[string]*Tree) { 238 | tree := treeSet[t.Name] 239 | if tree == nil || IsEmptyTree(tree.Root) { 240 | treeSet[t.Name] = t 241 | return 242 | } 243 | if !IsEmptyTree(t.Root) { 244 | t.errorf("template: multiple definition of template %q", t.Name) 245 | } 246 | } 247 | 248 | // IsEmptyTree reports whether this tree (node) is empty of everything but space. 249 | func IsEmptyTree(n Node) bool { 250 | switch n := n.(type) { 251 | case nil: 252 | return true 253 | case *ActionNode: 254 | case *IfNode: 255 | case *ListNode: 256 | for _, node := range n.Nodes { 257 | if !IsEmptyTree(node) { 258 | return false 259 | } 260 | } 261 | return true 262 | case *RangeNode: 263 | case *TemplateNode: 264 | case *TextNode: 265 | return len(bytes.TrimSpace(n.Text)) == 0 266 | case *WithNode: 267 | default: 268 | panic("unknown node: " + n.String()) 269 | } 270 | return false 271 | } 272 | 273 | // parse is the top-level parser for a template, essentially the same 274 | // as itemList except it also parses {{define}} actions. 275 | // It runs to EOF. 276 | func (t *Tree) parse(treeSet map[string]*Tree) (next Node) { 277 | t.Root = t.newList(t.peek().pos) 278 | for t.peek().typ != itemEOF { 279 | if t.peek().typ == itemLeftDelim { 280 | delim := t.next() 281 | if t.nextNonSpace().typ == itemDefine { 282 | newT := New("definition") // name will be updated once we know it. 283 | newT.text = t.text 284 | newT.ParseName = t.ParseName 285 | newT.startParse(t.funcs, t.lex) 286 | newT.parseDefinition(treeSet) 287 | continue 288 | } 289 | t.backup2(delim) 290 | } 291 | n := t.textOrAction() 292 | if n.Type() == nodeEnd { 293 | t.errorf("unexpected %s", n) 294 | } 295 | t.Root.append(n) 296 | } 297 | return nil 298 | } 299 | 300 | // parseDefinition parses a {{define}} ... {{end}} template definition and 301 | // installs the definition in the treeSet map. The "define" keyword has already 302 | // been scanned. 303 | func (t *Tree) parseDefinition(treeSet map[string]*Tree) { 304 | const context = "define clause" 305 | name := t.expectOneOf(itemString, itemRawString, context) 306 | var err error 307 | t.Name, err = strconv.Unquote(name.val) 308 | if err != nil { 309 | t.error(err) 310 | } 311 | t.expect(itemRightDelim, context) 312 | var end Node 313 | t.Root, end = t.itemList() 314 | if end.Type() != nodeEnd { 315 | t.errorf("unexpected %s in %s", end, context) 316 | } 317 | t.add(treeSet) 318 | t.stopParse() 319 | } 320 | 321 | // itemList: 322 | // textOrAction* 323 | // Terminates at {{end}} or {{else}}, returned separately. 324 | func (t *Tree) itemList() (list *ListNode, next Node) { 325 | list = t.newList(t.peekNonSpace().pos) 326 | for t.peekNonSpace().typ != itemEOF { 327 | n := t.textOrAction() 328 | switch n.Type() { 329 | case nodeEnd, nodeElse: 330 | return list, n 331 | } 332 | list.append(n) 333 | } 334 | t.errorf("unexpected EOF") 335 | return 336 | } 337 | 338 | // textOrAction: 339 | // text | action 340 | func (t *Tree) textOrAction() Node { 341 | switch token := t.nextNonSpace(); token.typ { 342 | case itemElideNewline: 343 | return t.elideNewline() 344 | case itemText: 345 | return t.newText(token.pos, token.val) 346 | case itemLeftDelim: 347 | return t.action() 348 | default: 349 | t.unexpected(token, "input") 350 | } 351 | return nil 352 | } 353 | 354 | // elideNewline: 355 | // Remove newlines trailing rightDelim if \\ is present. 356 | func (t *Tree) elideNewline() Node { 357 | token := t.peek() 358 | if token.typ != itemText { 359 | t.unexpected(token, "input") 360 | return nil 361 | } 362 | 363 | t.next() 364 | stripped := strings.TrimLeft(token.val, "\n\r") 365 | diff := len(token.val) - len(stripped) 366 | if diff > 0 { 367 | // This is a bit nasty. We mutate the token in-place to remove 368 | // preceding newlines. 369 | token.pos += Pos(diff) 370 | token.val = stripped 371 | } 372 | return t.newText(token.pos, token.val) 373 | } 374 | 375 | // Action: 376 | // control 377 | // command ("|" command)* 378 | // Left delim is past. Now get actions. 379 | // First word could be a keyword such as range. 380 | func (t *Tree) action() (n Node) { 381 | switch token := t.nextNonSpace(); token.typ { 382 | case itemElse: 383 | return t.elseControl() 384 | case itemEnd: 385 | return t.endControl() 386 | case itemIf: 387 | return t.ifControl() 388 | case itemRange: 389 | return t.rangeControl() 390 | case itemTemplate: 391 | return t.templateControl() 392 | case itemWith: 393 | return t.withControl() 394 | } 395 | t.backup() 396 | // Do not pop variables; they persist until "end". 397 | return t.newAction(t.peek().pos, t.lex.lineNumber(), t.pipeline("command")) 398 | } 399 | 400 | // Pipeline: 401 | // declarations? command ('|' command)* 402 | func (t *Tree) pipeline(context string) (pipe *PipeNode) { 403 | var decl []*VariableNode 404 | pos := t.peekNonSpace().pos 405 | // Are there declarations? 406 | for { 407 | if v := t.peekNonSpace(); v.typ == itemVariable { 408 | t.next() 409 | // Since space is a token, we need 3-token look-ahead here in the worst case: 410 | // in "$x foo" we need to read "foo" (as opposed to ":=") to know that $x is an 411 | // argument variable rather than a declaration. So remember the token 412 | // adjacent to the variable so we can push it back if necessary. 413 | tokenAfterVariable := t.peek() 414 | if next := t.peekNonSpace(); next.typ == itemColonEquals || (next.typ == itemChar && next.val == ",") { 415 | t.nextNonSpace() 416 | variable := t.newVariable(v.pos, v.val) 417 | decl = append(decl, variable) 418 | t.vars = append(t.vars, v.val) 419 | if next.typ == itemChar && next.val == "," { 420 | if context == "range" && len(decl) < 2 { 421 | continue 422 | } 423 | t.errorf("too many declarations in %s", context) 424 | } 425 | } else if tokenAfterVariable.typ == itemSpace { 426 | t.backup3(v, tokenAfterVariable) 427 | } else { 428 | t.backup2(v) 429 | } 430 | } 431 | break 432 | } 433 | pipe = t.newPipeline(pos, t.lex.lineNumber(), decl) 434 | for { 435 | switch token := t.nextNonSpace(); token.typ { 436 | case itemRightDelim, itemRightParen: 437 | if len(pipe.Cmds) == 0 { 438 | t.errorf("missing value for %s", context) 439 | } 440 | if token.typ == itemRightParen { 441 | t.backup() 442 | } 443 | return 444 | case itemBool, itemCharConstant, itemComplex, itemDot, itemField, itemIdentifier, 445 | itemNumber, itemNil, itemRawString, itemString, itemVariable, itemLeftParen: 446 | t.backup() 447 | pipe.append(t.command()) 448 | default: 449 | t.unexpected(token, context) 450 | } 451 | } 452 | } 453 | 454 | func (t *Tree) parseControl(allowElseIf bool, context string) (pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) { 455 | defer t.popVars(len(t.vars)) 456 | line = t.lex.lineNumber() 457 | pipe = t.pipeline(context) 458 | var next Node 459 | list, next = t.itemList() 460 | switch next.Type() { 461 | case nodeEnd: //done 462 | case nodeElse: 463 | if allowElseIf { 464 | // Special case for "else if". If the "else" is followed immediately by an "if", 465 | // the elseControl will have left the "if" token pending. Treat 466 | // {{if a}}_{{else if b}}_{{end}} 467 | // as 468 | // {{if a}}_{{else}}{{if b}}_{{end}}{{end}}. 469 | // To do this, parse the if as usual and stop at it {{end}}; the subsequent{{end}} 470 | // is assumed. This technique works even for long if-else-if chains. 471 | // TODO: Should we allow else-if in with and range? 472 | if t.peek().typ == itemIf { 473 | t.next() // Consume the "if" token. 474 | elseList = t.newList(next.Position()) 475 | elseList.append(t.ifControl()) 476 | // Do not consume the next item - only one {{end}} required. 477 | break 478 | } 479 | } 480 | elseList, next = t.itemList() 481 | if next.Type() != nodeEnd { 482 | t.errorf("expected end; found %s", next) 483 | } 484 | } 485 | return pipe.Position(), line, pipe, list, elseList 486 | } 487 | 488 | // If: 489 | // {{if pipeline}} itemList {{end}} 490 | // {{if pipeline}} itemList {{else}} itemList {{end}} 491 | // If keyword is past. 492 | func (t *Tree) ifControl() Node { 493 | return t.newIf(t.parseControl(true, "if")) 494 | } 495 | 496 | // Range: 497 | // {{range pipeline}} itemList {{end}} 498 | // {{range pipeline}} itemList {{else}} itemList {{end}} 499 | // Range keyword is past. 500 | func (t *Tree) rangeControl() Node { 501 | return t.newRange(t.parseControl(false, "range")) 502 | } 503 | 504 | // With: 505 | // {{with pipeline}} itemList {{end}} 506 | // {{with pipeline}} itemList {{else}} itemList {{end}} 507 | // If keyword is past. 508 | func (t *Tree) withControl() Node { 509 | return t.newWith(t.parseControl(false, "with")) 510 | } 511 | 512 | // End: 513 | // {{end}} 514 | // End keyword is past. 515 | func (t *Tree) endControl() Node { 516 | return t.newEnd(t.expect(itemRightDelim, "end").pos) 517 | } 518 | 519 | // Else: 520 | // {{else}} 521 | // Else keyword is past. 522 | func (t *Tree) elseControl() Node { 523 | // Special case for "else if". 524 | peek := t.peekNonSpace() 525 | if peek.typ == itemIf { 526 | // We see "{{else if ... " but in effect rewrite it to {{else}}{{if ... ". 527 | return t.newElse(peek.pos, t.lex.lineNumber()) 528 | } 529 | return t.newElse(t.expect(itemRightDelim, "else").pos, t.lex.lineNumber()) 530 | } 531 | 532 | // Template: 533 | // {{template stringValue pipeline}} 534 | // Template keyword is past. The name must be something that can evaluate 535 | // to a string. 536 | func (t *Tree) templateControl() Node { 537 | var name string 538 | token := t.nextNonSpace() 539 | switch token.typ { 540 | case itemString, itemRawString: 541 | s, err := strconv.Unquote(token.val) 542 | if err != nil { 543 | t.error(err) 544 | } 545 | name = s 546 | default: 547 | t.unexpected(token, "template invocation") 548 | } 549 | var pipe *PipeNode 550 | if t.nextNonSpace().typ != itemRightDelim { 551 | t.backup() 552 | // Do not pop variables; they persist until "end". 553 | pipe = t.pipeline("template") 554 | } 555 | return t.newTemplate(token.pos, t.lex.lineNumber(), name, pipe) 556 | } 557 | 558 | // command: 559 | // operand (space operand)* 560 | // space-separated arguments up to a pipeline character or right delimiter. 561 | // we consume the pipe character but leave the right delim to terminate the action. 562 | func (t *Tree) command() *CommandNode { 563 | cmd := t.newCommand(t.peekNonSpace().pos) 564 | for { 565 | t.peekNonSpace() // skip leading spaces. 566 | operand := t.operand() 567 | if operand != nil { 568 | cmd.append(operand) 569 | } 570 | switch token := t.next(); token.typ { 571 | case itemSpace: 572 | continue 573 | case itemError: 574 | t.errorf("%s", token.val) 575 | case itemRightDelim, itemRightParen: 576 | t.backup() 577 | case itemPipe: 578 | default: 579 | t.errorf("unexpected %s in operand; missing space?", token) 580 | } 581 | break 582 | } 583 | if len(cmd.Args) == 0 { 584 | t.errorf("empty command") 585 | } 586 | return cmd 587 | } 588 | 589 | // operand: 590 | // term .Field* 591 | // An operand is a space-separated component of a command, 592 | // a term possibly followed by field accesses. 593 | // A nil return means the next item is not an operand. 594 | func (t *Tree) operand() Node { 595 | node := t.term() 596 | if node == nil { 597 | return nil 598 | } 599 | if t.peek().typ == itemField { 600 | chain := t.newChain(t.peek().pos, node) 601 | for t.peek().typ == itemField { 602 | chain.Add(t.next().val) 603 | } 604 | // Compatibility with original API: If the term is of type NodeField 605 | // or NodeVariable, just put more fields on the original. 606 | // Otherwise, keep the Chain node. 607 | // TODO: Switch to Chains always when we can. 608 | switch node.Type() { 609 | case NodeField: 610 | node = t.newField(chain.Position(), chain.String()) 611 | case NodeVariable: 612 | node = t.newVariable(chain.Position(), chain.String()) 613 | default: 614 | node = chain 615 | } 616 | } 617 | return node 618 | } 619 | 620 | // term: 621 | // literal (number, string, nil, boolean) 622 | // function (identifier) 623 | // . 624 | // .Field 625 | // $ 626 | // '(' pipeline ')' 627 | // A term is a simple "expression". 628 | // A nil return means the next item is not a term. 629 | func (t *Tree) term() Node { 630 | switch token := t.nextNonSpace(); token.typ { 631 | case itemError: 632 | t.errorf("%s", token.val) 633 | case itemIdentifier: 634 | if !t.hasFunction(token.val) { 635 | t.errorf("function %q not defined", token.val) 636 | } 637 | return NewIdentifier(token.val).SetTree(t).SetPos(token.pos) 638 | case itemDot: 639 | return t.newDot(token.pos) 640 | case itemNil: 641 | return t.newNil(token.pos) 642 | case itemVariable: 643 | return t.useVar(token.pos, token.val) 644 | case itemField: 645 | return t.newField(token.pos, token.val) 646 | case itemBool: 647 | return t.newBool(token.pos, token.val == "true") 648 | case itemCharConstant, itemComplex, itemNumber: 649 | number, err := t.newNumber(token.pos, token.val, token.typ) 650 | if err != nil { 651 | t.error(err) 652 | } 653 | return number 654 | case itemLeftParen: 655 | pipe := t.pipeline("parenthesized pipeline") 656 | if token := t.next(); token.typ != itemRightParen { 657 | t.errorf("unclosed right paren: unexpected %s", token) 658 | } 659 | return pipe 660 | case itemString, itemRawString: 661 | s, err := strconv.Unquote(token.val) 662 | if err != nil { 663 | t.error(err) 664 | } 665 | return t.newString(token.pos, token.val, s) 666 | } 667 | t.backup() 668 | return nil 669 | } 670 | 671 | // hasFunction reports if a function name exists in the Tree's maps. 672 | func (t *Tree) hasFunction(name string) bool { 673 | for _, funcMap := range t.funcs { 674 | if funcMap == nil { 675 | continue 676 | } 677 | if funcMap[name] != nil { 678 | return true 679 | } 680 | } 681 | return false 682 | } 683 | 684 | // popVars trims the variable list to the specified length 685 | func (t *Tree) popVars(n int) { 686 | t.vars = t.vars[:n] 687 | } 688 | 689 | // useVar returns a node for a variable reference. It errors if the 690 | // variable is not defined. 691 | func (t *Tree) useVar(pos Pos, name string) Node { 692 | v := t.newVariable(pos, name) 693 | for _, varName := range t.vars { 694 | if varName == v.Ident[0] { 695 | return v 696 | } 697 | } 698 | t.errorf("undefined variable %q", v.Ident[0]) 699 | return nil 700 | } 701 | --------------------------------------------------------------------------------