diff --git a/vendor/manifest b/vendor/manifest index 2cc75e575..72055ad0c 100644 --- a/vendor/manifest +++ b/vendor/manifest @@ -27,6 +27,12 @@ "branch": "master", "path": "/spew" }, + { + "importpath": "github.com/docopt/docopt-go", + "repository": "https://github.com/docopt/docopt-go", + "revision": "784ddc588536785e7299f7272f39101f7faccc3f", + "branch": "master" + }, { "importpath": "github.com/eapache/go-resiliency/breaker", "repository": "https://github.com/eapache/go-resiliency", diff --git a/vendor/src/github.com/docopt/docopt-go/LICENSE b/vendor/src/github.com/docopt/docopt-go/LICENSE new file mode 100644 index 000000000..8841af161 --- /dev/null +++ b/vendor/src/github.com/docopt/docopt-go/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2013 Keith Batten + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/src/github.com/docopt/docopt-go/README.md b/vendor/src/github.com/docopt/docopt-go/README.md new file mode 100644 index 000000000..71c92aae0 --- /dev/null +++ b/vendor/src/github.com/docopt/docopt-go/README.md @@ -0,0 +1,88 @@ +docopt-go +========= + +[![Build Status](https://travis-ci.org/docopt/docopt.go.svg?branch=master)](https://travis-ci.org/docopt/docopt.go) +[![Coverage Status](https://coveralls.io/repos/docopt/docopt.go/badge.png)](https://coveralls.io/r/docopt/docopt.go) +[![GoDoc](https://godoc.org/github.com/docopt/docopt.go?status.png)](https://godoc.org/github.com/docopt/docopt.go) + +An implementation of [docopt](http://docopt.org/) in the +[Go](http://golang.org/) programming language. + +**docopt** helps you create *beautiful* command-line interfaces easily: + +```go +package main + +import ( + "fmt" + "github.com/docopt/docopt-go" +) + +func main() { + usage := `Naval Fate. + +Usage: + naval_fate ship new ... + naval_fate ship move [--speed=] + naval_fate ship shoot + naval_fate mine (set|remove) [--moored|--drifting] + naval_fate -h | --help + naval_fate --version + +Options: + -h --help Show this screen. + --version Show version. + --speed= Speed in knots [default: 10]. + --moored Moored (anchored) mine. + --drifting Drifting mine.` + + arguments, _ := docopt.Parse(usage, nil, true, "Naval Fate 2.0", false) + fmt.Println(arguments) +} +``` + +**docopt** parses command-line arguments based on a help message. Don't +write parser code: a good help message already has all the necessary +information in it. + +## Installation + +⚠ Use the alias “docopt-go”. To use docopt in your Go code: + +```go +import "github.com/docopt/docopt-go" +``` + +To install docopt according to your `$GOPATH`: + +```console +$ go get github.com/docopt/docopt-go +``` + +## API + +```go +func Parse(doc string, argv []string, help bool, version string, + optionsFirst bool, exit ...bool) (map[string]interface{}, error) +``` +Parse `argv` based on the command-line interface described in `doc`. + +Given a conventional command-line help message, docopt creates a parser and +processes the arguments. See +https://github.com/docopt/docopt#help-message-format for a description of the +help message format. If `argv` is `nil`, `os.Args[1:]` is used. + +docopt returns a map of option names to the values parsed from `argv`, and an +error or `nil`. + +More documentation for docopt is available at +[GoDoc.org](https://godoc.org/github.com/docopt/docopt.go). + +## Testing + +All tests from the Python version are implemented and passing +at [Travis CI](https://travis-ci.org/docopt/docopt.go). New +language-agnostic tests have been added +to [test_golang.docopt](test_golang.docopt). + +To run tests for docopt-go, use `go test`. diff --git a/vendor/src/github.com/docopt/docopt-go/docopt.go b/vendor/src/github.com/docopt/docopt-go/docopt.go new file mode 100644 index 000000000..d929fc39b --- /dev/null +++ b/vendor/src/github.com/docopt/docopt-go/docopt.go @@ -0,0 +1,1239 @@ +// Licensed under terms of MIT license (see LICENSE-MIT) +// Copyright (c) 2013 Keith Batten, kbatten@gmail.com + +/* +Package docopt parses command-line arguments based on a help message. + +⚠ Use the alias “docopt-go”: + import "github.com/docopt/docopt-go" +or + $ go get github.com/docopt/docopt-go +*/ +package docopt + +import ( + "fmt" + "os" + "reflect" + "regexp" + "strings" + "unicode" +) + +/* +Parse `argv` based on the command-line interface described in `doc`. + +Given a conventional command-line help message, docopt creates a parser and +processes the arguments. See +https://github.com/docopt/docopt#help-message-format for a description of the +help message format. If `argv` is `nil`, `os.Args[1:]` is used. + +docopt returns a map of option names to the values parsed from `argv`, and an +error or `nil`. + +Set `help` to `false` to disable automatic help messages on `-h` or `--help`. +If `version` is a non-empty string, it will be printed when `--version` is +specified. Set `optionsFirst` to `true` to require that options always come +before positional arguments; otherwise they can overlap. + +By default, docopt calls `os.Exit(0)` if it handled a built-in option such as +`-h` or `--version`. If the user errored with a wrong command or options, +docopt exits with a return code of 1. To stop docopt from calling `os.Exit()` +and to handle your own return codes, pass an optional last parameter of `false` +for `exit`. +*/ +func Parse(doc string, argv []string, help bool, version string, + optionsFirst bool, exit ...bool) (map[string]interface{}, error) { + // if "false" was the (optional) last arg, don't call os.Exit() + exitOk := true + if len(exit) > 0 { + exitOk = exit[0] + } + args, output, err := parse(doc, argv, help, version, optionsFirst) + if _, ok := err.(*UserError); ok { + // the user gave us bad input + fmt.Fprintln(os.Stderr, output) + if exitOk { + os.Exit(1) + } + } else if len(output) > 0 && err == nil { + // the user asked for help or `--version` + fmt.Println(output) + if exitOk { + os.Exit(0) + } + } + return args, err +} + +// parse and return a map of args, output and all errors +func parse(doc string, argv []string, help bool, version string, optionsFirst bool) (args map[string]interface{}, output string, err error) { + if argv == nil && len(os.Args) > 1 { + argv = os.Args[1:] + } + + usageSections := parseSection("usage:", doc) + + if len(usageSections) == 0 { + err = newLanguageError("\"usage:\" (case-insensitive) not found.") + return + } + if len(usageSections) > 1 { + err = newLanguageError("More than one \"usage:\" (case-insensitive).") + return + } + usage := usageSections[0] + + options := parseDefaults(doc) + formal, err := formalUsage(usage) + if err != nil { + output = handleError(err, usage) + return + } + + pat, err := parsePattern(formal, &options) + if err != nil { + output = handleError(err, usage) + return + } + + patternArgv, err := parseArgv(newTokenList(argv, errorUser), &options, optionsFirst) + if err != nil { + output = handleError(err, usage) + return + } + patFlat, err := pat.flat(patternOption) + if err != nil { + output = handleError(err, usage) + return + } + patternOptions := patFlat.unique() + + patFlat, err = pat.flat(patternOptionSSHORTCUT) + if err != nil { + output = handleError(err, usage) + return + } + for _, optionsShortcut := range patFlat { + docOptions := parseDefaults(doc) + optionsShortcut.children = docOptions.unique().diff(patternOptions) + } + + if output = extras(help, version, patternArgv, doc); len(output) > 0 { + return + } + + err = pat.fix() + if err != nil { + output = handleError(err, usage) + return + } + matched, left, collected := pat.match(&patternArgv, nil) + if matched && len(*left) == 0 { + patFlat, err = pat.flat(patternDefault) + if err != nil { + output = handleError(err, usage) + return + } + args = append(patFlat, *collected...).dictionary() + return + } + + err = newUserError("") + output = handleError(err, usage) + return +} + +func handleError(err error, usage string) string { + if _, ok := err.(*UserError); ok { + return strings.TrimSpace(fmt.Sprintf("%s\n%s", err, usage)) + } + return "" +} + +func parseSection(name, source string) []string { + p := regexp.MustCompile(`(?im)^([^\n]*` + name + `[^\n]*\n?(?:[ \t].*?(?:\n|$))*)`) + s := p.FindAllString(source, -1) + if s == nil { + s = []string{} + } + for i, v := range s { + s[i] = strings.TrimSpace(v) + } + return s +} + +func parseDefaults(doc string) patternList { + defaults := patternList{} + p := regexp.MustCompile(`\n[ \t]*(-\S+?)`) + for _, s := range parseSection("options:", doc) { + // FIXME corner case "bla: options: --foo" + _, _, s = stringPartition(s, ":") // get rid of "options:" + split := p.Split("\n"+s, -1)[1:] + match := p.FindAllStringSubmatch("\n"+s, -1) + for i := range split { + optionDescription := match[i][1] + split[i] + if strings.HasPrefix(optionDescription, "-") { + defaults = append(defaults, parseOption(optionDescription)) + } + } + } + return defaults +} + +func parsePattern(source string, options *patternList) (*pattern, error) { + tokens := tokenListFromPattern(source) + result, err := parseExpr(tokens, options) + if err != nil { + return nil, err + } + if tokens.current() != nil { + return nil, tokens.errorFunc("unexpected ending: %s" + strings.Join(tokens.tokens, " ")) + } + return newRequired(result...), nil +} + +func parseArgv(tokens *tokenList, options *patternList, optionsFirst bool) (patternList, error) { + /* + Parse command-line argument vector. + + If options_first: + argv ::= [ long | shorts ]* [ argument ]* [ '--' [ argument ]* ] ; + else: + argv ::= [ long | shorts | argument ]* [ '--' [ argument ]* ] ; + */ + parsed := patternList{} + for tokens.current() != nil { + if tokens.current().eq("--") { + for _, v := range tokens.tokens { + parsed = append(parsed, newArgument("", v)) + } + return parsed, nil + } else if tokens.current().hasPrefix("--") { + pl, err := parseLong(tokens, options) + if err != nil { + return nil, err + } + parsed = append(parsed, pl...) + } else if tokens.current().hasPrefix("-") && !tokens.current().eq("-") { + ps, err := parseShorts(tokens, options) + if err != nil { + return nil, err + } + parsed = append(parsed, ps...) + } else if optionsFirst { + for _, v := range tokens.tokens { + parsed = append(parsed, newArgument("", v)) + } + return parsed, nil + } else { + parsed = append(parsed, newArgument("", tokens.move().String())) + } + } + return parsed, nil +} + +func parseOption(optionDescription string) *pattern { + optionDescription = strings.TrimSpace(optionDescription) + options, _, description := stringPartition(optionDescription, " ") + options = strings.Replace(options, ",", " ", -1) + options = strings.Replace(options, "=", " ", -1) + + short := "" + long := "" + argcount := 0 + var value interface{} + value = false + + reDefault := regexp.MustCompile(`(?i)\[default: (.*)\]`) + for _, s := range strings.Fields(options) { + if strings.HasPrefix(s, "--") { + long = s + } else if strings.HasPrefix(s, "-") { + short = s + } else { + argcount = 1 + } + if argcount > 0 { + matched := reDefault.FindAllStringSubmatch(description, -1) + if len(matched) > 0 { + value = matched[0][1] + } else { + value = nil + } + } + } + return newOption(short, long, argcount, value) +} + +func parseExpr(tokens *tokenList, options *patternList) (patternList, error) { + // expr ::= seq ( '|' seq )* ; + seq, err := parseSeq(tokens, options) + if err != nil { + return nil, err + } + if !tokens.current().eq("|") { + return seq, nil + } + var result patternList + if len(seq) > 1 { + result = patternList{newRequired(seq...)} + } else { + result = seq + } + for tokens.current().eq("|") { + tokens.move() + seq, err = parseSeq(tokens, options) + if err != nil { + return nil, err + } + if len(seq) > 1 { + result = append(result, newRequired(seq...)) + } else { + result = append(result, seq...) + } + } + if len(result) > 1 { + return patternList{newEither(result...)}, nil + } + return result, nil +} + +func parseSeq(tokens *tokenList, options *patternList) (patternList, error) { + // seq ::= ( atom [ '...' ] )* ; + result := patternList{} + for !tokens.current().match(true, "]", ")", "|") { + atom, err := parseAtom(tokens, options) + if err != nil { + return nil, err + } + if tokens.current().eq("...") { + atom = patternList{newOneOrMore(atom...)} + tokens.move() + } + result = append(result, atom...) + } + return result, nil +} + +func parseAtom(tokens *tokenList, options *patternList) (patternList, error) { + // atom ::= '(' expr ')' | '[' expr ']' | 'options' | long | shorts | argument | command ; + tok := tokens.current() + result := patternList{} + if tokens.current().match(false, "(", "[") { + tokens.move() + var matching string + pl, err := parseExpr(tokens, options) + if err != nil { + return nil, err + } + if tok.eq("(") { + matching = ")" + result = patternList{newRequired(pl...)} + } else if tok.eq("[") { + matching = "]" + result = patternList{newOptional(pl...)} + } + moved := tokens.move() + if !moved.eq(matching) { + return nil, tokens.errorFunc("unmatched '%s', expected: '%s' got: '%s'", tok, matching, moved) + } + return result, nil + } else if tok.eq("options") { + tokens.move() + return patternList{newOptionsShortcut()}, nil + } else if tok.hasPrefix("--") && !tok.eq("--") { + return parseLong(tokens, options) + } else if tok.hasPrefix("-") && !tok.eq("-") && !tok.eq("--") { + return parseShorts(tokens, options) + } else if tok.hasPrefix("<") && tok.hasSuffix(">") || tok.isUpper() { + return patternList{newArgument(tokens.move().String(), nil)}, nil + } + return patternList{newCommand(tokens.move().String(), false)}, nil +} + +func parseLong(tokens *tokenList, options *patternList) (patternList, error) { + // long ::= '--' chars [ ( ' ' | '=' ) chars ] ; + long, eq, v := stringPartition(tokens.move().String(), "=") + var value interface{} + var opt *pattern + if eq == "" && v == "" { + value = nil + } else { + value = v + } + + if !strings.HasPrefix(long, "--") { + return nil, newError("long option '%s' doesn't start with --", long) + } + similar := patternList{} + for _, o := range *options { + if o.long == long { + similar = append(similar, o) + } + } + if tokens.err == errorUser && len(similar) == 0 { // if no exact match + similar = patternList{} + for _, o := range *options { + if strings.HasPrefix(o.long, long) { + similar = append(similar, o) + } + } + } + if len(similar) > 1 { // might be simply specified ambiguously 2+ times? + similarLong := make([]string, len(similar)) + for i, s := range similar { + similarLong[i] = s.long + } + return nil, tokens.errorFunc("%s is not a unique prefix: %s?", long, strings.Join(similarLong, ", ")) + } else if len(similar) < 1 { + argcount := 0 + if eq == "=" { + argcount = 1 + } + opt = newOption("", long, argcount, false) + *options = append(*options, opt) + if tokens.err == errorUser { + var val interface{} + if argcount > 0 { + val = value + } else { + val = true + } + opt = newOption("", long, argcount, val) + } + } else { + opt = newOption(similar[0].short, similar[0].long, similar[0].argcount, similar[0].value) + if opt.argcount == 0 { + if value != nil { + return nil, tokens.errorFunc("%s must not have an argument", opt.long) + } + } else { + if value == nil { + if tokens.current().match(true, "--") { + return nil, tokens.errorFunc("%s requires argument", opt.long) + } + moved := tokens.move() + if moved != nil { + value = moved.String() // only set as string if not nil + } + } + } + if tokens.err == errorUser { + if value != nil { + opt.value = value + } else { + opt.value = true + } + } + } + + return patternList{opt}, nil +} + +func parseShorts(tokens *tokenList, options *patternList) (patternList, error) { + // shorts ::= '-' ( chars )* [ [ ' ' ] chars ] ; + tok := tokens.move() + if !tok.hasPrefix("-") || tok.hasPrefix("--") { + return nil, newError("short option '%s' doesn't start with -", tok) + } + left := strings.TrimLeft(tok.String(), "-") + parsed := patternList{} + for left != "" { + var opt *pattern + short := "-" + left[0:1] + left = left[1:] + similar := patternList{} + for _, o := range *options { + if o.short == short { + similar = append(similar, o) + } + } + if len(similar) > 1 { + return nil, tokens.errorFunc("%s is specified ambiguously %d times", short, len(similar)) + } else if len(similar) < 1 { + opt = newOption(short, "", 0, false) + *options = append(*options, opt) + if tokens.err == errorUser { + opt = newOption(short, "", 0, true) + } + } else { // why copying is necessary here? + opt = newOption(short, similar[0].long, similar[0].argcount, similar[0].value) + var value interface{} + if opt.argcount > 0 { + if left == "" { + if tokens.current().match(true, "--") { + return nil, tokens.errorFunc("%s requires argument", short) + } + value = tokens.move().String() + } else { + value = left + left = "" + } + } + if tokens.err == errorUser { + if value != nil { + opt.value = value + } else { + opt.value = true + } + } + } + parsed = append(parsed, opt) + } + return parsed, nil +} + +func newTokenList(source []string, err errorType) *tokenList { + errorFunc := newError + if err == errorUser { + errorFunc = newUserError + } else if err == errorLanguage { + errorFunc = newLanguageError + } + return &tokenList{source, errorFunc, err} +} + +func tokenListFromString(source string) *tokenList { + return newTokenList(strings.Fields(source), errorUser) +} + +func tokenListFromPattern(source string) *tokenList { + p := regexp.MustCompile(`([\[\]\(\)\|]|\.\.\.)`) + source = p.ReplaceAllString(source, ` $1 `) + p = regexp.MustCompile(`\s+|(\S*<.*?>)`) + split := p.Split(source, -1) + match := p.FindAllStringSubmatch(source, -1) + var result []string + l := len(split) + for i := 0; i < l; i++ { + if len(split[i]) > 0 { + result = append(result, split[i]) + } + if i < l-1 && len(match[i][1]) > 0 { + result = append(result, match[i][1]) + } + } + return newTokenList(result, errorLanguage) +} + +func formalUsage(section string) (string, error) { + _, _, section = stringPartition(section, ":") // drop "usage:" + pu := strings.Fields(section) + + if len(pu) == 0 { + return "", newLanguageError("no fields found in usage (perhaps a spacing error).") + } + + result := "( " + for _, s := range pu[1:] { + if s == pu[0] { + result += ") | ( " + } else { + result += s + " " + } + } + result += ")" + + return result, nil +} + +func extras(help bool, version string, options patternList, doc string) string { + if help { + for _, o := range options { + if (o.name == "-h" || o.name == "--help") && o.value == true { + return strings.Trim(doc, "\n") + } + } + } + if version != "" { + for _, o := range options { + if (o.name == "--version") && o.value == true { + return version + } + } + } + return "" +} + +type errorType int + +const ( + errorUser errorType = iota + errorLanguage +) + +func (e errorType) String() string { + switch e { + case errorUser: + return "errorUser" + case errorLanguage: + return "errorLanguage" + } + return "" +} + +// UserError records an error with program arguments. +type UserError struct { + msg string + Usage string +} + +func (e UserError) Error() string { + return e.msg +} +func newUserError(msg string, f ...interface{}) error { + return &UserError{fmt.Sprintf(msg, f...), ""} +} + +// LanguageError records an error with the doc string. +type LanguageError struct { + msg string +} + +func (e LanguageError) Error() string { + return e.msg +} +func newLanguageError(msg string, f ...interface{}) error { + return &LanguageError{fmt.Sprintf(msg, f...)} +} + +var newError = fmt.Errorf + +type tokenList struct { + tokens []string + errorFunc func(string, ...interface{}) error + err errorType +} +type token string + +func (t *token) eq(s string) bool { + if t == nil { + return false + } + return string(*t) == s +} +func (t *token) match(matchNil bool, tokenStrings ...string) bool { + if t == nil && matchNil { + return true + } else if t == nil && !matchNil { + return false + } + + for _, tok := range tokenStrings { + if tok == string(*t) { + return true + } + } + return false +} +func (t *token) hasPrefix(prefix string) bool { + if t == nil { + return false + } + return strings.HasPrefix(string(*t), prefix) +} +func (t *token) hasSuffix(suffix string) bool { + if t == nil { + return false + } + return strings.HasSuffix(string(*t), suffix) +} +func (t *token) isUpper() bool { + if t == nil { + return false + } + return isStringUppercase(string(*t)) +} +func (t *token) String() string { + if t == nil { + return "" + } + return string(*t) +} + +func (tl *tokenList) current() *token { + if len(tl.tokens) > 0 { + return (*token)(&(tl.tokens[0])) + } + return nil +} + +func (tl *tokenList) length() int { + return len(tl.tokens) +} + +func (tl *tokenList) move() *token { + if len(tl.tokens) > 0 { + t := tl.tokens[0] + tl.tokens = tl.tokens[1:] + return (*token)(&t) + } + return nil +} + +type patternType uint + +const ( + // leaf + patternArgument patternType = 1 << iota + patternCommand + patternOption + + // branch + patternRequired + patternOptionAL + patternOptionSSHORTCUT // Marker/placeholder for [options] shortcut. + patternOneOrMore + patternEither + + patternLeaf = patternArgument + + patternCommand + + patternOption + patternBranch = patternRequired + + patternOptionAL + + patternOptionSSHORTCUT + + patternOneOrMore + + patternEither + patternAll = patternLeaf + patternBranch + patternDefault = 0 +) + +func (pt patternType) String() string { + switch pt { + case patternArgument: + return "argument" + case patternCommand: + return "command" + case patternOption: + return "option" + case patternRequired: + return "required" + case patternOptionAL: + return "optional" + case patternOptionSSHORTCUT: + return "optionsshortcut" + case patternOneOrMore: + return "oneormore" + case patternEither: + return "either" + case patternLeaf: + return "leaf" + case patternBranch: + return "branch" + case patternAll: + return "all" + case patternDefault: + return "default" + } + return "" +} + +type pattern struct { + t patternType + + children patternList + + name string + value interface{} + + short string + long string + argcount int +} + +type patternList []*pattern + +func newBranchPattern(t patternType, pl ...*pattern) *pattern { + var p pattern + p.t = t + p.children = make(patternList, len(pl)) + copy(p.children, pl) + return &p +} + +func newRequired(pl ...*pattern) *pattern { + return newBranchPattern(patternRequired, pl...) +} + +func newEither(pl ...*pattern) *pattern { + return newBranchPattern(patternEither, pl...) +} + +func newOneOrMore(pl ...*pattern) *pattern { + return newBranchPattern(patternOneOrMore, pl...) +} + +func newOptional(pl ...*pattern) *pattern { + return newBranchPattern(patternOptionAL, pl...) +} + +func newOptionsShortcut() *pattern { + var p pattern + p.t = patternOptionSSHORTCUT + return &p +} + +func newLeafPattern(t patternType, name string, value interface{}) *pattern { + // default: value=nil + var p pattern + p.t = t + p.name = name + p.value = value + return &p +} + +func newArgument(name string, value interface{}) *pattern { + // default: value=nil + return newLeafPattern(patternArgument, name, value) +} + +func newCommand(name string, value interface{}) *pattern { + // default: value=false + var p pattern + p.t = patternCommand + p.name = name + p.value = value + return &p +} + +func newOption(short, long string, argcount int, value interface{}) *pattern { + // default: "", "", 0, false + var p pattern + p.t = patternOption + p.short = short + p.long = long + if long != "" { + p.name = long + } else { + p.name = short + } + p.argcount = argcount + if value == false && argcount > 0 { + p.value = nil + } else { + p.value = value + } + return &p +} + +func (p *pattern) flat(types patternType) (patternList, error) { + if p.t&patternLeaf != 0 { + if types == patternDefault { + types = patternAll + } + if p.t&types != 0 { + return patternList{p}, nil + } + return patternList{}, nil + } + + if p.t&patternBranch != 0 { + if p.t&types != 0 { + return patternList{p}, nil + } + result := patternList{} + for _, child := range p.children { + childFlat, err := child.flat(types) + if err != nil { + return nil, err + } + result = append(result, childFlat...) + } + return result, nil + } + return nil, newError("unknown pattern type: %d, %d", p.t, types) +} + +func (p *pattern) fix() error { + err := p.fixIdentities(nil) + if err != nil { + return err + } + p.fixRepeatingArguments() + return nil +} + +func (p *pattern) fixIdentities(uniq patternList) error { + // Make pattern-tree tips point to same object if they are equal. + if p.t&patternBranch == 0 { + return nil + } + if uniq == nil { + pFlat, err := p.flat(patternDefault) + if err != nil { + return err + } + uniq = pFlat.unique() + } + for i, child := range p.children { + if child.t&patternBranch == 0 { + ind, err := uniq.index(child) + if err != nil { + return err + } + p.children[i] = uniq[ind] + } else { + err := child.fixIdentities(uniq) + if err != nil { + return err + } + } + } + return nil +} + +func (p *pattern) fixRepeatingArguments() { + // Fix elements that should accumulate/increment values. + var either []patternList + + for _, child := range p.transform().children { + either = append(either, child.children) + } + for _, cas := range either { + casMultiple := patternList{} + for _, e := range cas { + if cas.count(e) > 1 { + casMultiple = append(casMultiple, e) + } + } + for _, e := range casMultiple { + if e.t == patternArgument || e.t == patternOption && e.argcount > 0 { + switch e.value.(type) { + case string: + e.value = strings.Fields(e.value.(string)) + case []string: + default: + e.value = []string{} + } + } + if e.t == patternCommand || e.t == patternOption && e.argcount == 0 { + e.value = 0 + } + } + } +} + +func (p *pattern) match(left *patternList, collected *patternList) (bool, *patternList, *patternList) { + if collected == nil { + collected = &patternList{} + } + if p.t&patternRequired != 0 { + l := left + c := collected + for _, p := range p.children { + var matched bool + matched, l, c = p.match(l, c) + if !matched { + return false, left, collected + } + } + return true, l, c + } else if p.t&patternOptionAL != 0 || p.t&patternOptionSSHORTCUT != 0 { + for _, p := range p.children { + _, left, collected = p.match(left, collected) + } + return true, left, collected + } else if p.t&patternOneOrMore != 0 { + if len(p.children) != 1 { + panic("OneOrMore.match(): assert len(p.children) == 1") + } + l := left + c := collected + var lAlt *patternList + matched := true + times := 0 + for matched { + // could it be that something didn't match but changed l or c? + matched, l, c = p.children[0].match(l, c) + if matched { + times++ + } + if lAlt == l { + break + } + lAlt = l + } + if times >= 1 { + return true, l, c + } + return false, left, collected + } else if p.t&patternEither != 0 { + type outcomeStruct struct { + matched bool + left *patternList + collected *patternList + length int + } + outcomes := []outcomeStruct{} + for _, p := range p.children { + matched, l, c := p.match(left, collected) + outcome := outcomeStruct{matched, l, c, len(*l)} + if matched { + outcomes = append(outcomes, outcome) + } + } + if len(outcomes) > 0 { + minLen := outcomes[0].length + minIndex := 0 + for i, v := range outcomes { + if v.length < minLen { + minIndex = i + } + } + return outcomes[minIndex].matched, outcomes[minIndex].left, outcomes[minIndex].collected + } + return false, left, collected + } else if p.t&patternLeaf != 0 { + pos, match := p.singleMatch(left) + var increment interface{} + if match == nil { + return false, left, collected + } + leftAlt := make(patternList, len((*left)[:pos]), len((*left)[:pos])+len((*left)[pos+1:])) + copy(leftAlt, (*left)[:pos]) + leftAlt = append(leftAlt, (*left)[pos+1:]...) + sameName := patternList{} + for _, a := range *collected { + if a.name == p.name { + sameName = append(sameName, a) + } + } + + switch p.value.(type) { + case int, []string: + switch p.value.(type) { + case int: + increment = 1 + case []string: + switch match.value.(type) { + case string: + increment = []string{match.value.(string)} + default: + increment = match.value + } + } + if len(sameName) == 0 { + match.value = increment + collectedMatch := make(patternList, len(*collected), len(*collected)+1) + copy(collectedMatch, *collected) + collectedMatch = append(collectedMatch, match) + return true, &leftAlt, &collectedMatch + } + switch sameName[0].value.(type) { + case int: + sameName[0].value = sameName[0].value.(int) + increment.(int) + case []string: + sameName[0].value = append(sameName[0].value.([]string), increment.([]string)...) + } + return true, &leftAlt, collected + } + collectedMatch := make(patternList, len(*collected), len(*collected)+1) + copy(collectedMatch, *collected) + collectedMatch = append(collectedMatch, match) + return true, &leftAlt, &collectedMatch + } + panic("unmatched type") +} + +func (p *pattern) singleMatch(left *patternList) (int, *pattern) { + if p.t&patternArgument != 0 { + for n, pat := range *left { + if pat.t&patternArgument != 0 { + return n, newArgument(p.name, pat.value) + } + } + return -1, nil + } else if p.t&patternCommand != 0 { + for n, pat := range *left { + if pat.t&patternArgument != 0 { + if pat.value == p.name { + return n, newCommand(p.name, true) + } + break + } + } + return -1, nil + } else if p.t&patternOption != 0 { + for n, pat := range *left { + if p.name == pat.name { + return n, pat + } + } + return -1, nil + } + panic("unmatched type") +} + +func (p *pattern) String() string { + if p.t&patternOption != 0 { + return fmt.Sprintf("%s(%s, %s, %d, %+v)", p.t, p.short, p.long, p.argcount, p.value) + } else if p.t&patternLeaf != 0 { + return fmt.Sprintf("%s(%s, %+v)", p.t, p.name, p.value) + } else if p.t&patternBranch != 0 { + result := "" + for i, child := range p.children { + if i > 0 { + result += ", " + } + result += child.String() + } + return fmt.Sprintf("%s(%s)", p.t, result) + } + panic("unmatched type") +} + +func (p *pattern) transform() *pattern { + /* + Expand pattern into an (almost) equivalent one, but with single Either. + + Example: ((-a | -b) (-c | -d)) => (-a -c | -a -d | -b -c | -b -d) + Quirks: [-a] => (-a), (-a...) => (-a -a) + */ + result := []patternList{} + groups := []patternList{patternList{p}} + parents := patternRequired + + patternOptionAL + + patternOptionSSHORTCUT + + patternEither + + patternOneOrMore + for len(groups) > 0 { + children := groups[0] + groups = groups[1:] + var child *pattern + for _, c := range children { + if c.t&parents != 0 { + child = c + break + } + } + if child != nil { + children.remove(child) + if child.t&patternEither != 0 { + for _, c := range child.children { + r := patternList{} + r = append(r, c) + r = append(r, children...) + groups = append(groups, r) + } + } else if child.t&patternOneOrMore != 0 { + r := patternList{} + r = append(r, child.children.double()...) + r = append(r, children...) + groups = append(groups, r) + } else { + r := patternList{} + r = append(r, child.children...) + r = append(r, children...) + groups = append(groups, r) + } + } else { + result = append(result, children) + } + } + either := patternList{} + for _, e := range result { + either = append(either, newRequired(e...)) + } + return newEither(either...) +} + +func (p *pattern) eq(other *pattern) bool { + return reflect.DeepEqual(p, other) +} + +func (pl patternList) unique() patternList { + table := make(map[string]bool) + result := patternList{} + for _, v := range pl { + if !table[v.String()] { + table[v.String()] = true + result = append(result, v) + } + } + return result +} + +func (pl patternList) index(p *pattern) (int, error) { + for i, c := range pl { + if c.eq(p) { + return i, nil + } + } + return -1, newError("%s not in list", p) +} + +func (pl patternList) count(p *pattern) int { + count := 0 + for _, c := range pl { + if c.eq(p) { + count++ + } + } + return count +} + +func (pl patternList) diff(l patternList) patternList { + lAlt := make(patternList, len(l)) + copy(lAlt, l) + result := make(patternList, 0, len(pl)) + for _, v := range pl { + if v != nil { + match := false + for i, w := range lAlt { + if w.eq(v) { + match = true + lAlt[i] = nil + break + } + } + if match == false { + result = append(result, v) + } + } + } + return result +} + +func (pl patternList) double() patternList { + l := len(pl) + result := make(patternList, l*2) + copy(result, pl) + copy(result[l:2*l], pl) + return result +} + +func (pl *patternList) remove(p *pattern) { + (*pl) = pl.diff(patternList{p}) +} + +func (pl patternList) dictionary() map[string]interface{} { + dict := make(map[string]interface{}) + for _, a := range pl { + dict[a.name] = a.value + } + return dict +} + +func stringPartition(s, sep string) (string, string, string) { + sepPos := strings.Index(s, sep) + if sepPos == -1 { // no seperator found + return s, "", "" + } + split := strings.SplitN(s, sep, 2) + return split[0], sep, split[1] +} + +// returns true if all cased characters in the string are uppercase +// and there are there is at least one cased charcter +func isStringUppercase(s string) bool { + if strings.ToUpper(s) != s { + return false + } + for _, c := range []rune(s) { + if unicode.IsUpper(c) { + return true + } + } + return false +} diff --git a/vendor/src/github.com/docopt/docopt-go/docopt_test.go b/vendor/src/github.com/docopt/docopt-go/docopt_test.go new file mode 100644 index 000000000..945eab522 --- /dev/null +++ b/vendor/src/github.com/docopt/docopt-go/docopt_test.go @@ -0,0 +1,1536 @@ +/* +Based of off docopt.py: https://github.com/docopt/docopt + +Licensed under terms of MIT license (see LICENSE-MIT) +Copyright (c) 2013 Keith Batten, kbatten@gmail.com +*/ + +package docopt + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "os" + "reflect" + "regexp" + "strings" + "testing" +) + +func TestPatternFlat(t *testing.T) { + q := patternList{ + newArgument("N", nil), + newOption("-a", "", 0, false), + newArgument("M", nil)} + p, err := newRequired( + newOneOrMore(newArgument("N", nil)), + newOption("-a", "", 0, false), + newArgument("M", nil)).flat(patternDefault) + if reflect.DeepEqual(p, q) != true { + t.Error(err) + } + + q = patternList{newOptionsShortcut()} + p, err = newRequired( + newOptional(newOptionsShortcut()), + newOptional(newOption("-a", "", 0, false))).flat(patternOptionSSHORTCUT) + if reflect.DeepEqual(p, q) != true { + t.Error(err) + } + return +} + +func TestOption(t *testing.T) { + if !parseOption("-h").eq(newOption("-h", "", 0, false)) { + t.Fail() + } + if !parseOption("--help").eq(newOption("", "--help", 0, false)) { + t.Fail() + } + if !parseOption("-h --help").eq(newOption("-h", "--help", 0, false)) { + t.Fail() + } + if !parseOption("-h, --help").eq(newOption("-h", "--help", 0, false)) { + t.Fail() + } + + if !parseOption("-h TOPIC").eq(newOption("-h", "", 1, false)) { + t.Fail() + } + if !parseOption("--help TOPIC").eq(newOption("", "--help", 1, false)) { + t.Fail() + } + if !parseOption("-h TOPIC --help TOPIC").eq(newOption("-h", "--help", 1, false)) { + t.Fail() + } + if !parseOption("-h TOPIC, --help TOPIC").eq(newOption("-h", "--help", 1, false)) { + t.Fail() + } + if !parseOption("-h TOPIC, --help=TOPIC").eq(newOption("-h", "--help", 1, false)) { + t.Fail() + } + + if !parseOption("-h Description...").eq(newOption("-h", "", 0, false)) { + t.Fail() + } + if !parseOption("-h --help Description...").eq(newOption("-h", "--help", 0, false)) { + t.Fail() + } + if !parseOption("-h TOPIC Description...").eq(newOption("-h", "", 1, false)) { + t.Fail() + } + + if !parseOption(" -h").eq(newOption("-h", "", 0, false)) { + t.Fail() + } + + if !parseOption("-h TOPIC Description... [default: 2]").eq(newOption("-h", "", 1, "2")) { + t.Fail() + } + if !parseOption("-h TOPIC Descripton... [default: topic-1]").eq(newOption("-h", "", 1, "topic-1")) { + t.Fail() + } + if !parseOption("--help=TOPIC ... [default: 3.14]").eq(newOption("", "--help", 1, "3.14")) { + t.Fail() + } + if !parseOption("-h, --help=DIR ... [default: ./]").eq(newOption("-h", "--help", 1, "./")) { + t.Fail() + } + if !parseOption("-h TOPIC Descripton... [dEfAuLt: 2]").eq(newOption("-h", "", 1, "2")) { + t.Fail() + } + return +} + +func TestOptionName(t *testing.T) { + if newOption("-h", "", 0, false).name != "-h" { + t.Fail() + } + if newOption("-h", "--help", 0, false).name != "--help" { + t.Fail() + } + if newOption("", "--help", 0, false).name != "--help" { + t.Fail() + } + return +} + +func TestCommands(t *testing.T) { + if v, err := Parse("Usage: prog add", []string{"add"}, true, "", false, false); reflect.DeepEqual(v, map[string]interface{}{"add": true}) != true { + t.Error(err) + } + if v, err := Parse("Usage: prog [add]", []string{}, true, "", false, false); reflect.DeepEqual(v, map[string]interface{}{"add": false}) != true { + t.Error(err) + } + if v, err := Parse("Usage: prog [add]", []string{"add"}, true, "", false, false); reflect.DeepEqual(v, map[string]interface{}{"add": true}) != true { + t.Error(err) + } + if v, err := Parse("Usage: prog (add|rm)", []string{"add"}, true, "", false, false); reflect.DeepEqual(v, map[string]interface{}{"add": true, "rm": false}) != true { + t.Error(err) + } + if v, err := Parse("Usage: prog (add|rm)", []string{"rm"}, true, "", false, false); reflect.DeepEqual(v, map[string]interface{}{"add": false, "rm": true}) != true { + t.Error(err) + } + if v, err := Parse("Usage: prog a b", []string{"a", "b"}, true, "", false, false); reflect.DeepEqual(v, map[string]interface{}{"a": true, "b": true}) != true { + t.Error(err) + } + _, err := Parse("Usage: prog a b", []string{"b", "a"}, true, "", false, false) + if _, ok := err.(*UserError); !ok { + t.Error(err) + } + return +} + +func TestFormalUsage(t *testing.T) { + doc := ` + Usage: prog [-hv] ARG + prog N M + + prog is a program` + usage := parseSection("usage:", doc)[0] + if usage != "Usage: prog [-hv] ARG\n prog N M" { + t.FailNow() + } + formal, err := formalUsage(usage) + if err != nil { + t.Fatal(err) + } + if formal != "( [-hv] ARG ) | ( N M )" { + t.Fail() + } + return +} + +func TestParseArgv(t *testing.T) { + o := patternList{ + newOption("-h", "", 0, false), + newOption("-v", "--verbose", 0, false), + newOption("-f", "--file", 1, false), + } + + p, err := parseArgv(tokenListFromString(""), &o, false) + q := patternList{} + if reflect.DeepEqual(p, q) != true { + t.Error(err) + } + + p, err = parseArgv(tokenListFromString("-h"), &o, false) + q = patternList{newOption("-h", "", 0, true)} + if reflect.DeepEqual(p, q) != true { + t.Error(err) + } + + p, err = parseArgv(tokenListFromString("-h --verbose"), &o, false) + q = patternList{ + newOption("-h", "", 0, true), + newOption("-v", "--verbose", 0, true), + } + if reflect.DeepEqual(p, q) != true { + t.Error(err) + } + + p, err = parseArgv(tokenListFromString("-h --file f.txt"), &o, false) + q = patternList{ + newOption("-h", "", 0, true), + newOption("-f", "--file", 1, "f.txt"), + } + if reflect.DeepEqual(p, q) != true { + t.Error(err) + } + + p, err = parseArgv(tokenListFromString("-h --file f.txt arg"), &o, false) + q = patternList{ + newOption("-h", "", 0, true), + newOption("-f", "--file", 1, "f.txt"), + newArgument("", "arg"), + } + if reflect.DeepEqual(p, q) != true { + t.Error(err) + } + + p, err = parseArgv(tokenListFromString("-h --file f.txt arg arg2"), &o, false) + q = patternList{ + newOption("-h", "", 0, true), + newOption("-f", "--file", 1, "f.txt"), + newArgument("", "arg"), + newArgument("", "arg2"), + } + if reflect.DeepEqual(p, q) != true { + t.Error(err) + } + + p, err = parseArgv(tokenListFromString("-h arg -- -v"), &o, false) + q = patternList{ + newOption("-h", "", 0, true), + newArgument("", "arg"), + newArgument("", "--"), + newArgument("", "-v"), + } + if reflect.DeepEqual(p, q) != true { + t.Error(err) + } +} + +func TestParsePattern(t *testing.T) { + o := patternList{ + newOption("-h", "", 0, false), + newOption("-v", "--verbose", 0, false), + newOption("-f", "--file", 1, false), + } + + p, err := parsePattern("[ -h ]", &o) + q := newRequired(newOptional(newOption("-h", "", 0, false))) + if p.eq(q) != true { + t.Error(err) + } + + p, err = parsePattern("[ ARG ... ]", &o) + q = newRequired(newOptional( + newOneOrMore( + newArgument("ARG", nil)))) + if p.eq(q) != true { + t.Error(err) + } + + p, err = parsePattern("[ -h | -v ]", &o) + q = newRequired( + newOptional( + newEither( + newOption("-h", "", 0, false), + newOption("-v", "--verbose", 0, false)))) + if p.eq(q) != true { + t.Error(err) + } + + p, err = parsePattern("( -h | -v [ --file ] )", &o) + q = newRequired( + newRequired( + newEither( + newOption("-h", "", 0, false), + newRequired( + newOption("-v", "--verbose", 0, false), + newOptional( + newOption("-f", "--file", 1, nil)))))) + if p.eq(q) != true { + t.Error(err) + } + + p, err = parsePattern("(-h|-v[--file=]N...)", &o) + q = newRequired( + newRequired( + newEither( + newOption("-h", "", 0, false), + newRequired( + newOption("-v", "--verbose", 0, false), + newOptional( + newOption("-f", "--file", 1, nil)), + newOneOrMore( + newArgument("N", nil)))))) + if p.eq(q) != true { + t.Error(err) + } + + p, err = parsePattern("(N [M | (K | L)] | O P)", &o) + q = newRequired( + newRequired( + newEither( + newRequired( + newArgument("N", nil), + newOptional( + newEither( + newArgument("M", nil), + newRequired( + newEither( + newArgument("K", nil), + newArgument("L", nil)))))), + newRequired( + newArgument("O", nil), + newArgument("P", nil))))) + if p.eq(q) != true { + t.Error(err) + } + + p, err = parsePattern("[ -h ] [N]", &o) + q = newRequired( + newOptional( + newOption("-h", "", 0, false)), + newOptional( + newArgument("N", nil))) + if p.eq(q) != true { + t.Error(err) + } + + p, err = parsePattern("[options]", &o) + q = newRequired( + newOptional( + newOptionsShortcut())) + if p.eq(q) != true { + t.Error(err) + } + + p, err = parsePattern("[options] A", &o) + q = newRequired( + newOptional( + newOptionsShortcut()), + newArgument("A", nil)) + if p.eq(q) != true { + t.Error(err) + } + + p, err = parsePattern("-v [options]", &o) + q = newRequired( + newOption("-v", "--verbose", 0, false), + newOptional( + newOptionsShortcut())) + if p.eq(q) != true { + t.Error(err) + } + + p, err = parsePattern("ADD", &o) + q = newRequired(newArgument("ADD", nil)) + if p.eq(q) != true { + t.Error(err) + } + + p, err = parsePattern("", &o) + q = newRequired(newArgument("", nil)) + if p.eq(q) != true { + t.Error(err) + } + + p, err = parsePattern("add", &o) + q = newRequired(newCommand("add", false)) + if p.eq(q) != true { + t.Error(err) + } +} + +func TestOptionMatch(t *testing.T) { + v, w, x := newOption("-a", "", 0, false).match( + &patternList{newOption("-a", "", 0, true)}, nil) + y := patternList{newOption("-a", "", 0, true)} + if v != true || + reflect.DeepEqual(*w, patternList{}) != true || + reflect.DeepEqual(*x, y) != true { + t.Fail() + } + + v, w, x = newOption("-a", "", 0, false).match( + &patternList{newOption("-x", "", 0, false)}, nil) + y = patternList{newOption("-x", "", 0, false)} + if v != false || + reflect.DeepEqual(*w, y) != true || + reflect.DeepEqual(*x, patternList{}) != true { + t.Fail() + } + + v, w, x = newOption("-a", "", 0, false).match( + &patternList{newOption("-x", "", 0, false)}, nil) + y = patternList{newOption("-x", "", 0, false)} + if v != false || + reflect.DeepEqual(*w, y) != true || + reflect.DeepEqual(*x, patternList{}) != true { + t.Fail() + } + v, w, x = newOption("-a", "", 0, false).match( + &patternList{newArgument("N", nil)}, nil) + y = patternList{newArgument("N", nil)} + if v != false || + reflect.DeepEqual(*w, y) != true || + reflect.DeepEqual(*x, patternList{}) != true { + t.Fail() + } + + v, w, x = newOption("-a", "", 0, false).match( + &patternList{ + newOption("-x", "", 0, false), + newOption("-a", "", 0, false), + newArgument("N", nil)}, nil) + y = patternList{ + newOption("-x", "", 0, false), + newArgument("N", nil)} + z := patternList{newOption("-a", "", 0, false)} + if v != true || + reflect.DeepEqual(*w, y) != true || + reflect.DeepEqual(*x, z) != true { + t.Fail() + } + + v, w, x = newOption("-a", "", 0, false).match( + &patternList{ + newOption("-a", "", 0, true), + newOption("-a", "", 0, false)}, nil) + y = patternList{newOption("-a", "", 0, false)} + z = patternList{newOption("-a", "", 0, true)} + if v != true || + reflect.DeepEqual(*w, y) != true || + reflect.DeepEqual(*x, z) != true { + t.Fail() + } +} + +func TestArgumentMatch(t *testing.T) { + v, w, x := newArgument("N", nil).match( + &patternList{newArgument("N", 9)}, nil) + y := patternList{newArgument("N", 9)} + if v != true || + reflect.DeepEqual(*w, patternList{}) != true || + reflect.DeepEqual(*x, y) != true { + t.Fail() + } + + v, w, x = newArgument("N", nil).match( + &patternList{newOption("-x", "", 0, false)}, nil) + y = patternList{newOption("-x", "", 0, false)} + if v != false || + reflect.DeepEqual(*w, y) != true || + reflect.DeepEqual(*x, patternList{}) != true { + t.Fail() + } + + v, w, x = newArgument("N", nil).match( + &patternList{newOption("-x", "", 0, false), + newOption("-a", "", 0, false), + newArgument("", 5)}, nil) + y = patternList{newOption("-x", "", 0, false), + newOption("-a", "", 0, false)} + z := patternList{newArgument("N", 5)} + if v != true || + reflect.DeepEqual(*w, y) != true || + reflect.DeepEqual(*x, z) != true { + t.Fail() + } + + v, w, x = newArgument("N", nil).match( + &patternList{newArgument("", 9), + newArgument("", 0)}, nil) + y = patternList{newArgument("", 0)} + z = patternList{newArgument("N", 9)} + if v != true || + reflect.DeepEqual(*w, y) != true || + reflect.DeepEqual(*x, z) != true { + t.Fail() + } +} + +func TestCommandMatch(t *testing.T) { + v, w, x := newCommand("c", false).match( + &patternList{newArgument("", "c")}, nil) + y := patternList{newCommand("c", true)} + if v != true || + reflect.DeepEqual(*w, patternList{}) != true || + reflect.DeepEqual(*x, y) != true { + t.Fail() + } + + v, w, x = newCommand("c", false).match( + &patternList{newOption("-x", "", 0, false)}, nil) + y = patternList{newOption("-x", "", 0, false)} + if v != false || + reflect.DeepEqual(*w, y) != true || + reflect.DeepEqual(*x, patternList{}) != true { + t.Fail() + } + + v, w, x = newCommand("c", false).match( + &patternList{ + newOption("-x", "", 0, false), + newOption("-a", "", 0, false), + newArgument("", "c")}, nil) + y = patternList{newOption("-x", "", 0, false), + newOption("-a", "", 0, false)} + z := patternList{newCommand("c", true)} + if v != true || + reflect.DeepEqual(*w, y) != true || + reflect.DeepEqual(*x, z) != true { + t.Fail() + } + + v, w, x = newEither( + newCommand("add", false), + newCommand("rm", false)).match( + &patternList{newArgument("", "rm")}, nil) + y = patternList{newCommand("rm", true)} + if v != true || + reflect.DeepEqual(*w, patternList{}) != true || + reflect.DeepEqual(*x, y) != true { + t.Fail() + } +} + +func TestOptionalMatch(t *testing.T) { + v, w, x := newOptional(newOption("-a", "", 0, false)).match( + &patternList{newOption("-a", "", 0, false)}, nil) + y := patternList{newOption("-a", "", 0, false)} + if v != true || + reflect.DeepEqual(*w, patternList{}) != true || + reflect.DeepEqual(*x, y) != true { + t.Fail() + } + + v, w, x = newOptional(newOption("-a", "", 0, false)).match( + &patternList{}, nil) + if v != true || + reflect.DeepEqual(*w, patternList{}) != true || + reflect.DeepEqual(*x, patternList{}) != true { + t.Fail() + } + + v, w, x = newOptional(newOption("-a", "", 0, false)).match( + &patternList{newOption("-x", "", 0, false)}, nil) + y = patternList{newOption("-x", "", 0, false)} + if v != true || + reflect.DeepEqual(*w, y) != true || + reflect.DeepEqual(*x, patternList{}) != true { + t.Fail() + } + + v, w, x = newOptional(newOption("-a", "", 0, false), + newOption("-b", "", 0, false)).match( + &patternList{newOption("-a", "", 0, false)}, nil) + y = patternList{newOption("-a", "", 0, false)} + if v != true || + reflect.DeepEqual(*w, patternList{}) != true || + reflect.DeepEqual(*x, y) != true { + t.Fail() + } + + v, w, x = newOptional(newOption("-a", "", 0, false), + newOption("-b", "", 0, false)).match( + &patternList{newOption("-b", "", 0, false)}, nil) + y = patternList{newOption("-b", "", 0, false)} + if v != true || + reflect.DeepEqual(*w, patternList{}) != true || + reflect.DeepEqual(*x, y) != true { + t.Fail() + } + + v, w, x = newOptional(newOption("-a", "", 0, false), + newOption("-b", "", 0, false)).match( + &patternList{newOption("-x", "", 0, false)}, nil) + y = patternList{newOption("-x", "", 0, false)} + if v != true || + reflect.DeepEqual(*w, y) != true || + reflect.DeepEqual(*x, patternList{}) != true { + t.Fail() + } + + v, w, x = newOptional(newArgument("N", nil)).match( + &patternList{newArgument("", 9)}, nil) + y = patternList{newArgument("N", 9)} + if v != true || + reflect.DeepEqual(*w, patternList{}) != true || + reflect.DeepEqual(*x, y) != true { + t.Fail() + } + + v, w, x = newOptional(newOption("-a", "", 0, false), + newOption("-b", "", 0, false)).match( + &patternList{newOption("-b", "", 0, false), + newOption("-x", "", 0, false), + newOption("-a", "", 0, false)}, nil) + y = patternList{newOption("-x", "", 0, false)} + z := patternList{newOption("-a", "", 0, false), + newOption("-b", "", 0, false)} + if v != true || + reflect.DeepEqual(*w, y) != true || + reflect.DeepEqual(*x, z) != true { + t.Fail() + } +} + +func TestRequiredMatch(t *testing.T) { + v, w, x := newRequired(newOption("-a", "", 0, false)).match( + &patternList{newOption("-a", "", 0, false)}, nil) + y := patternList{newOption("-a", "", 0, false)} + if v != true || + reflect.DeepEqual(*w, patternList{}) != true || + reflect.DeepEqual(*x, y) != true { + t.Fail() + } + + v, w, x = newRequired(newOption("-a", "", 0, false)).match(&patternList{}, nil) + if v != false || + reflect.DeepEqual(*w, patternList{}) != true || + reflect.DeepEqual(*x, patternList{}) != true { + t.Fail() + } + + v, w, x = newRequired(newOption("-a", "", 0, false)).match( + &patternList{newOption("-x", "", 0, false)}, nil) + y = patternList{newOption("-x", "", 0, false)} + if v != false || + reflect.DeepEqual(*w, y) != true || + reflect.DeepEqual(*x, patternList{}) != true { + t.Fail() + } + v, w, x = newRequired(newOption("-a", "", 0, false), + newOption("-b", "", 0, false)).match( + &patternList{newOption("-a", "", 0, false)}, nil) + y = patternList{newOption("-a", "", 0, false)} + if v != false || + reflect.DeepEqual(*w, y) != true || + reflect.DeepEqual(*x, patternList{}) != true { + t.Fail() + } +} + +func TestEitherMatch(t *testing.T) { + v, w, x := newEither( + newOption("-a", "", 0, false), + newOption("-b", "", 0, false)).match( + &patternList{newOption("-a", "", 0, false)}, nil) + y := patternList{newOption("-a", "", 0, false)} + if v != true || + reflect.DeepEqual(*w, patternList{}) != true || + reflect.DeepEqual(*x, y) != true { + t.Fail() + } + + v, w, x = newEither( + newOption("-a", "", 0, false), + newOption("-b", "", 0, false)).match(&patternList{ + newOption("-a", "", 0, false), + newOption("-b", "", 0, false)}, nil) + y = patternList{newOption("-b", "", 0, false)} + z := patternList{newOption("-a", "", 0, false)} + if v != true || + reflect.DeepEqual(*w, y) != true || + reflect.DeepEqual(*x, z) != true { + t.Fail() + } + + v, w, x = newEither( + newOption("-a", "", 0, false), + newOption("-b", "", 0, false)).match(&patternList{ + newOption("-x", "", 0, false)}, nil) + y = patternList{newOption("-x", "", 0, false)} + z = patternList{} + if v != false || + reflect.DeepEqual(*w, y) != true || + reflect.DeepEqual(*x, z) != true { + t.Fail() + } + + v, w, x = newEither( + newOption("-a", "", 0, false), + newOption("-b", "", 0, false), + newOption("-c", "", 0, false)).match(&patternList{ + newOption("-x", "", 0, false), + newOption("-b", "", 0, false)}, nil) + y = patternList{newOption("-x", "", 0, false)} + z = patternList{newOption("-b", "", 0, false)} + if v != true || + reflect.DeepEqual(*w, y) != true || + reflect.DeepEqual(*x, z) != true { + t.Fail() + } + v, w, x = newEither( + newArgument("M", nil), + newRequired(newArgument("N", nil), + newArgument("M", nil))).match(&patternList{ + newArgument("", 1), + newArgument("", 2)}, nil) + y = patternList{} + z = patternList{newArgument("N", 1), newArgument("M", 2)} + if v != true || + reflect.DeepEqual(*w, y) != true || + reflect.DeepEqual(*x, z) != true { + t.Fail() + } +} + +func TestOneOrMoreMatch(t *testing.T) { + v, w, x := newOneOrMore(newArgument("N", nil)).match( + &patternList{newArgument("", 9)}, nil) + y := patternList{newArgument("N", 9)} + if v != true || + reflect.DeepEqual(*w, patternList{}) != true || + reflect.DeepEqual(*x, y) != true { + t.Fail() + } + + v, w, x = newOneOrMore(newArgument("N", nil)).match( + &patternList{}, nil) + y = patternList{} + z := patternList{} + if v != false || + reflect.DeepEqual(*w, y) != true || + reflect.DeepEqual(*x, z) != true { + t.Fail() + } + + v, w, x = newOneOrMore(newArgument("N", nil)).match( + &patternList{newOption("-x", "", 0, false)}, nil) + y = patternList{newOption("-x", "", 0, false)} + z = patternList{} + if v != false || + reflect.DeepEqual(*w, y) != true || + reflect.DeepEqual(*x, z) != true { + t.Fail() + } + + v, w, x = newOneOrMore(newArgument("N", nil)).match( + &patternList{newArgument("", 9), newArgument("", 8)}, nil) + y = patternList{} + z = patternList{newArgument("N", 9), newArgument("N", 8)} + if v != true || + reflect.DeepEqual(*w, y) != true || + reflect.DeepEqual(*x, z) != true { + t.Fail() + } + + v, w, x = newOneOrMore(newArgument("N", nil)).match(&patternList{ + newArgument("", 9), + newOption("-x", "", 0, false), + newArgument("", 8)}, nil) + y = patternList{newOption("-x", "", 0, false)} + z = patternList{newArgument("N", 9), newArgument("N", 8)} + if v != true || + reflect.DeepEqual(*w, y) != true || + reflect.DeepEqual(*x, z) != true { + t.Fail() + } + + v, w, x = newOneOrMore(newOption("-a", "", 0, false)).match(&patternList{ + newOption("-a", "", 0, false), + newArgument("", 8), + newOption("-a", "", 0, false)}, nil) + y = patternList{newArgument("", 8)} + z = patternList{newOption("-a", "", 0, false), newOption("-a", "", 0, false)} + if v != true || + reflect.DeepEqual(*w, y) != true || + reflect.DeepEqual(*x, z) != true { + t.Fail() + } + + v, w, x = newOneOrMore(newOption("-a", "", 0, false)).match(&patternList{ + newArgument("", 8), + newOption("-x", "", 0, false)}, nil) + y = patternList{newArgument("", 8), newOption("-x", "", 0, false)} + z = patternList{} + if v != false || + reflect.DeepEqual(*w, y) != true || + reflect.DeepEqual(*x, z) != true { + t.Fail() + } + + v, w, x = newOneOrMore(newRequired(newOption("-a", "", 0, false), + newArgument("N", nil))).match(&patternList{ + newOption("-a", "", 0, false), + newArgument("", 1), + newOption("-x", "", 0, false), + newOption("-a", "", 0, false), + newArgument("", 2)}, nil) + y = patternList{newOption("-x", "", 0, false)} + z = patternList{newOption("-a", "", 0, false), + newArgument("N", 1), + newOption("-a", "", 0, false), + newArgument("N", 2)} + if v != true || + reflect.DeepEqual(*w, y) != true || + reflect.DeepEqual(*x, z) != true { + t.Fail() + } + + v, w, x = newOneOrMore(newOptional(newArgument("N", nil))).match( + &patternList{newArgument("", 9)}, nil) + y = patternList{} + z = patternList{newArgument("N", 9)} + if v != true || + reflect.DeepEqual(*w, y) != true || + reflect.DeepEqual(*x, z) != true { + t.Fail() + } +} + +func TestListArgumentMatch(t *testing.T) { + p := newRequired( + newArgument("N", nil), + newArgument("N", nil)) + p.fix() + v, w, x := p.match(&patternList{newArgument("", "1"), + newArgument("", "2")}, nil) + y := patternList{newArgument("N", []string{"1", "2"})} + if v != true || + reflect.DeepEqual(*w, patternList{}) != true || + reflect.DeepEqual(*x, y) != true { + t.Fail() + } + + p = newOneOrMore(newArgument("N", nil)) + p.fix() + v, w, x = p.match(&patternList{newArgument("", "1"), + newArgument("", "2"), newArgument("", "3")}, nil) + y = patternList{newArgument("N", []string{"1", "2", "3"})} + if v != true || + reflect.DeepEqual(*w, patternList{}) != true || + reflect.DeepEqual(*x, y) != true { + t.Fail() + } + + p = newRequired(newArgument("N", nil), + newOneOrMore(newArgument("N", nil))) + p.fix() + v, w, x = p.match(&patternList{ + newArgument("", "1"), + newArgument("", "2"), + newArgument("", "3")}, nil) + y = patternList{newArgument("N", []string{"1", "2", "3"})} + if v != true || + reflect.DeepEqual(*w, patternList{}) != true || + reflect.DeepEqual(*x, y) != true { + t.Fail() + } + + p = newRequired(newArgument("N", nil), + newRequired(newArgument("N", nil))) + p.fix() + v, w, x = p.match(&patternList{ + newArgument("", "1"), + newArgument("", "2")}, nil) + y = patternList{newArgument("N", []string{"1", "2"})} + if v != true || + reflect.DeepEqual(*w, patternList{}) != true || + reflect.DeepEqual(*x, y) != true { + t.Fail() + } +} + +func TestBasicPatternMatching(t *testing.T) { + // ( -a N [ -x Z ] ) + p := newRequired( + newOption("-a", "", 0, false), + newArgument("N", nil), + newOptional( + newOption("-x", "", 0, false), + newArgument("Z", nil))) + + // -a N + q := patternList{newOption("-a", "", 0, false), newArgument("", 9)} + y := patternList{newOption("-a", "", 0, false), newArgument("N", 9)} + v, w, x := p.match(&q, nil) + if v != true || + reflect.DeepEqual(*w, patternList{}) != true || + reflect.DeepEqual(*x, y) != true { + t.Fail() + } + + // -a -x N Z + q = patternList{newOption("-a", "", 0, false), + newOption("-x", "", 0, false), + newArgument("", 9), newArgument("", 5)} + y = patternList{} + z := patternList{newOption("-a", "", 0, false), newArgument("N", 9), + newOption("-x", "", 0, false), newArgument("Z", 5)} + v, w, x = p.match(&q, nil) + if v != true || + reflect.DeepEqual(*w, y) != true || + reflect.DeepEqual(*x, z) != true { + t.Fail() + } + + // -x N Z # BZZ! + q = patternList{newOption("-x", "", 0, false), + newArgument("", 9), newArgument("", 5)} + y = patternList{newOption("-x", "", 0, false), + newArgument("", 9), newArgument("", 5)} + z = patternList{} + v, w, x = p.match(&q, nil) + if v != false || + reflect.DeepEqual(*w, y) != true || + reflect.DeepEqual(*x, z) != true { + t.Fail() + } +} + +func TestPatternEither(t *testing.T) { + p := newOption("-a", "", 0, false).transform() + q := newEither(newRequired( + newOption("-a", "", 0, false))) + if p.eq(q) != true { + t.Fail() + } + + p = newArgument("A", nil).transform() + q = newEither(newRequired( + newArgument("A", nil))) + if p.eq(q) != true { + t.Fail() + } + + p = newRequired( + newEither( + newOption("-a", "", 0, false), + newOption("-b", "", 0, false)), + newOption("-c", "", 0, false)).transform() + q = newEither( + newRequired( + newOption("-a", "", 0, false), + newOption("-c", "", 0, false)), + newRequired( + newOption("-b", "", 0, false), + newOption("-c", "", 0, false))) + if p.eq(q) != true { + t.Fail() + } + + p = newOptional(newOption("-a", "", 0, false), + newEither(newOption("-b", "", 0, false), + newOption("-c", "", 0, false))).transform() + q = newEither( + newRequired( + newOption("-b", "", 0, false), newOption("-a", "", 0, false)), + newRequired( + newOption("-c", "", 0, false), newOption("-a", "", 0, false))) + if p.eq(q) != true { + t.Fail() + } + + p = newEither(newOption("-x", "", 0, false), + newEither(newOption("-y", "", 0, false), + newOption("-z", "", 0, false))).transform() + q = newEither( + newRequired(newOption("-x", "", 0, false)), + newRequired(newOption("-y", "", 0, false)), + newRequired(newOption("-z", "", 0, false))) + if p.eq(q) != true { + t.Fail() + } + + p = newOneOrMore(newArgument("N", nil), + newArgument("M", nil)).transform() + q = newEither( + newRequired(newArgument("N", nil), newArgument("M", nil), + newArgument("N", nil), newArgument("M", nil))) + if p.eq(q) != true { + t.Fail() + } +} + +func TestPatternFixRepeatingArguments(t *testing.T) { + p := newOption("-a", "", 0, false) + p.fixRepeatingArguments() + if p.eq(newOption("-a", "", 0, false)) != true { + t.Fail() + } + + p = newArgument("N", nil) + p.fixRepeatingArguments() + if p.eq(newArgument("N", nil)) != true { + t.Fail() + } + + p = newRequired( + newArgument("N", nil), + newArgument("N", nil)) + q := newRequired( + newArgument("N", []string{}), + newArgument("N", []string{})) + p.fixRepeatingArguments() + if p.eq(q) != true { + t.Fail() + } + + p = newEither( + newArgument("N", nil), + newOneOrMore(newArgument("N", nil))) + q = newEither( + newArgument("N", []string{}), + newOneOrMore(newArgument("N", []string{}))) + p.fix() + if p.eq(q) != true { + t.Fail() + } +} + +func TestSet(t *testing.T) { + p := newArgument("N", nil) + q := newArgument("N", nil) + if reflect.DeepEqual(p, q) != true { + t.Fail() + } + pl := patternList{newArgument("N", nil), newArgument("N", nil)} + ql := patternList{newArgument("N", nil)} + if reflect.DeepEqual(pl.unique(), ql.unique()) != true { + t.Fail() + } +} + +func TestPatternFixIdentities1(t *testing.T) { + p := newRequired( + newArgument("N", nil), + newArgument("N", nil)) + if len(p.children) < 2 { + t.FailNow() + } + if p.children[0].eq(p.children[1]) != true { + t.Fail() + } + if p.children[0] == p.children[1] { + t.Fail() + } + p.fixIdentities(nil) + if p.children[0] != p.children[1] { + t.Fail() + } +} + +func TestPatternFixIdentities2(t *testing.T) { + p := newRequired( + newOptional( + newArgument("X", nil), + newArgument("N", nil)), + newArgument("N", nil)) + if len(p.children) < 2 { + t.FailNow() + } + if len(p.children[0].children) < 2 { + t.FailNow() + } + if p.children[0].children[1].eq(p.children[1]) != true { + t.Fail() + } + if p.children[0].children[1] == p.children[1] { + t.Fail() + } + p.fixIdentities(nil) + if p.children[0].children[1] != p.children[1] { + t.Fail() + } +} + +func TestLongOptionsErrorHandling(t *testing.T) { + _, err := Parse("Usage: prog", []string{"--non-existent"}, true, "", false, false) + if _, ok := err.(*UserError); !ok { + t.Error(fmt.Sprintf("(%s) %s", reflect.TypeOf(err), err)) + } + _, err = Parse("Usage: prog [--version --verbose]\nOptions: --version\n --verbose", + []string{"--ver"}, true, "", false, false) + if _, ok := err.(*UserError); !ok { + t.Error(err) + } + _, err = Parse("Usage: prog --long\nOptions: --long ARG", []string{}, true, "", false, false) + if _, ok := err.(*LanguageError); !ok { + t.Error(err) + } + _, err = Parse("Usage: prog --long ARG\nOptions: --long ARG", + []string{"--long"}, true, "", false, false) + if _, ok := err.(*UserError); !ok { + t.Error(fmt.Sprintf("(%s) %s", reflect.TypeOf(err), err)) + } + _, err = Parse("Usage: prog --long=ARG\nOptions: --long", []string{}, true, "", false, false) + if _, ok := err.(*LanguageError); !ok { + t.Error(err) + } + _, err = Parse("Usage: prog --long\nOptions: --long", + []string{}, true, "--long=ARG", false, false) + if _, ok := err.(*UserError); !ok { + t.Error(err) + } +} + +func TestShortOptionsErrorHandling(t *testing.T) { + _, err := Parse("Usage: prog -x\nOptions: -x this\n -x that", []string{}, true, "", false, false) + if _, ok := err.(*LanguageError); !ok { + t.Error(fmt.Sprintf("(%s) %s", reflect.TypeOf(err), err)) + } + _, err = Parse("Usage: prog", []string{"-x"}, true, "", false, false) + if _, ok := err.(*UserError); !ok { + t.Error(err) + } + _, err = Parse("Usage: prog -o\nOptions: -o ARG", []string{}, true, "", false, false) + if _, ok := err.(*LanguageError); !ok { + t.Error(err) + } + _, err = Parse("Usage: prog -o ARG\nOptions: -o ARG", []string{"-o"}, true, "", false, false) + if _, ok := err.(*UserError); !ok { + t.Error(err) + } +} + +func TestMatchingParen(t *testing.T) { + _, err := Parse("Usage: prog [a [b]", []string{}, true, "", false, false) + if _, ok := err.(*LanguageError); !ok { + t.Error(err) + } + _, err = Parse("Usage: prog [a [b] ] c )", []string{}, true, "", false, false) + if _, ok := err.(*LanguageError); !ok { + t.Error(err) + } +} + +func TestAllowDoubleDash(t *testing.T) { + if v, err := Parse("usage: prog [-o] [--] \noptions: -o", []string{"--", "-o"}, true, "", false, false); reflect.DeepEqual(v, map[string]interface{}{"-o": false, "": "-o", "--": true}) != true { + t.Error(err) + } + if v, err := Parse("usage: prog [-o] [--] \noptions: -o", []string{"-o", "1"}, true, "", false, false); reflect.DeepEqual(v, map[string]interface{}{"-o": true, "": "1", "--": false}) != true { + t.Error(err) + } + _, err := Parse("usage: prog [-o] \noptions:-o", []string{"-o"}, true, "", false, false) + if _, ok := err.(*UserError); !ok { //"--" is not allowed; FIXME? + t.Error(err) + } +} + +func TestDocopt(t *testing.T) { + doc := `Usage: prog [-v] A + + Options: -v Be verbose.` + if v, err := Parse(doc, []string{"arg"}, true, "", false, false); reflect.DeepEqual(v, map[string]interface{}{"-v": false, "A": "arg"}) != true { + t.Error(err) + } + if v, err := Parse(doc, []string{"-v", "arg"}, true, "", false, false); reflect.DeepEqual(v, map[string]interface{}{"-v": true, "A": "arg"}) != true { + t.Error(err) + } + + doc = `Usage: prog [-vqr] [FILE] + prog INPUT OUTPUT + prog --help + + Options: + -v print status messages + -q report only file names + -r show all occurrences of the same error + --help + + ` + if v, err := Parse(doc, []string{"-v", "file.py"}, true, "", false, false); reflect.DeepEqual(v, map[string]interface{}{"-v": true, "-q": false, "-r": false, "--help": false, "FILE": "file.py", "INPUT": nil, "OUTPUT": nil}) != true { + t.Error(err) + } + if v, err := Parse(doc, []string{"-v"}, true, "", false, false); reflect.DeepEqual(v, map[string]interface{}{"-v": true, "-q": false, "-r": false, "--help": false, "FILE": nil, "INPUT": nil, "OUTPUT": nil}) != true { + t.Error(err) + } + + _, err := Parse(doc, []string{"-v", "input.py", "output.py"}, true, "", false, false) // does not match + if _, ok := err.(*UserError); !ok { + t.Error(err) + } + _, err = Parse(doc, []string{"--fake"}, true, "", false, false) + if _, ok := err.(*UserError); !ok { + t.Error(err) + } + _, output, err := parseOutput(doc, []string{"--hel"}, true, "", false) + if err != nil || len(output) == 0 { + t.Error(err) + } +} + +func TestLanguageErrors(t *testing.T) { + _, err := Parse("no usage with colon here", []string{}, true, "", false, false) + if _, ok := err.(*LanguageError); !ok { + t.Error(err) + } + _, err = Parse("usage: here \n\n and again usage: here", []string{}, true, "", false, false) + if _, ok := err.(*LanguageError); !ok { + t.Error(err) + } +} + +func TestIssue40(t *testing.T) { + _, output, err := parseOutput("usage: prog --help-commands | --help", []string{"--help"}, true, "", false) + if err != nil || len(output) == 0 { + t.Error(err) + } + if v, err := Parse("usage: prog --aabb | --aa", []string{"--aa"}, true, "", false, false); reflect.DeepEqual(v, map[string]interface{}{"--aabb": false, "--aa": true}) != true { + t.Error(err) + } +} + +func TestIssue34UnicodeStrings(t *testing.T) { + // TODO: see if applicable +} + +func TestCountMultipleFlags(t *testing.T) { + if v, err := Parse("usage: prog [-v]", []string{"-v"}, true, "", false, false); reflect.DeepEqual(v, map[string]interface{}{"-v": true}) != true { + t.Error(err) + } + if v, err := Parse("usage: prog [-vv]", []string{}, true, "", false, false); reflect.DeepEqual(v, map[string]interface{}{"-v": 0}) != true { + t.Error(err) + } + if v, err := Parse("usage: prog [-vv]", []string{"-v"}, true, "", false, false); reflect.DeepEqual(v, map[string]interface{}{"-v": 1}) != true { + t.Error(err) + } + if v, err := Parse("usage: prog [-vv]", []string{"-vv"}, true, "", false, false); reflect.DeepEqual(v, map[string]interface{}{"-v": 2}) != true { + t.Error(err) + } + _, err := Parse("usage: prog [-vv]", []string{"-vvv"}, true, "", false, false) + if _, ok := err.(*UserError); !ok { + t.Error(err) + } + if v, err := Parse("usage: prog [-v | -vv | -vvv]", []string{"-vvv"}, true, "", false, false); reflect.DeepEqual(v, map[string]interface{}{"-v": 3}) != true { + t.Error(err) + } + if v, err := Parse("usage: prog [-v...]", []string{"-vvvvvv"}, true, "", false, false); reflect.DeepEqual(v, map[string]interface{}{"-v": 6}) != true { + t.Error(err) + } + if v, err := Parse("usage: prog [--ver --ver]", []string{"--ver", "--ver"}, true, "", false, false); reflect.DeepEqual(v, map[string]interface{}{"--ver": 2}) != true { + t.Error(err) + } +} + +func TestAnyOptionsParameter(t *testing.T) { + _, err := Parse("usage: prog [options]", + []string{"-foo", "--bar", "--spam=eggs"}, true, "", false, false) + if _, ok := err.(*UserError); !ok { + t.Fail() + } + + _, err = Parse("usage: prog [options]", + []string{"--foo", "--bar", "--bar"}, true, "", false, false) + if _, ok := err.(*UserError); !ok { + t.Fail() + } + _, err = Parse("usage: prog [options]", + []string{"--bar", "--bar", "--bar", "-ffff"}, true, "", false, false) + if _, ok := err.(*UserError); !ok { + t.Fail() + } + _, err = Parse("usage: prog [options]", + []string{"--long=arg", "--long=another"}, true, "", false, false) + if _, ok := err.(*UserError); !ok { + t.Fail() + } +} + +func TestDefaultValueForPositionalArguments(t *testing.T) { + doc := "Usage: prog [--data=...]\nOptions:\n\t-d --data= Input data [default: x]" + if v, err := Parse(doc, []string{}, true, "", false, false); reflect.DeepEqual(v, map[string]interface{}{"--data": []string{"x"}}) != true { + t.Error(err) + } + + doc = "Usage: prog [--data=...]\nOptions:\n\t-d --data= Input data [default: x y]" + if v, err := Parse(doc, []string{}, true, "", false, false); reflect.DeepEqual(v, map[string]interface{}{"--data": []string{"x", "y"}}) != true { + t.Error(err) + } + + doc = "Usage: prog [--data=...]\nOptions:\n\t-d --data= Input data [default: x y]" + if v, err := Parse(doc, []string{"--data=this"}, true, "", false, false); reflect.DeepEqual(v, map[string]interface{}{"--data": []string{"this"}}) != true { + t.Error(err) + } +} + +func TestIssue59(t *testing.T) { + if v, err := Parse("usage: prog --long=", []string{"--long="}, true, "", false, false); reflect.DeepEqual(v, map[string]interface{}{"--long": ""}) != true { + t.Error(err) + } + + if v, err := Parse("usage: prog -l \noptions: -l ", []string{"-l", ""}, true, "", false, false); reflect.DeepEqual(v, map[string]interface{}{"-l": ""}) != true { + t.Error(err) + } +} + +func TestOptionsFirst(t *testing.T) { + if v, err := Parse("usage: prog [--opt] [...]", []string{"--opt", "this", "that"}, true, "", false, false); reflect.DeepEqual(v, map[string]interface{}{"--opt": true, "": []string{"this", "that"}}) != true { + t.Error(err) + } + + if v, err := Parse("usage: prog [--opt] [...]", []string{"this", "that", "--opt"}, true, "", false, false); reflect.DeepEqual(v, map[string]interface{}{"--opt": true, "": []string{"this", "that"}}) != true { + t.Error(err) + } + + if v, err := Parse("usage: prog [--opt] [...]", []string{"this", "that", "--opt"}, true, "", true, false); reflect.DeepEqual(v, map[string]interface{}{"--opt": false, "": []string{"this", "that", "--opt"}}) != true { + t.Error(err) + } +} + +func TestIssue68OptionsShortcutDoesNotIncludeOptionsInUsagePattern(t *testing.T) { + args, err := Parse("usage: prog [-ab] [options]\noptions: -x\n -y", []string{"-ax"}, true, "", false, false) + + if args["-a"] != true { + t.Error(err) + } + if args["-b"] != false { + t.Error(err) + } + if args["-x"] != true { + t.Error(err) + } + if args["-y"] != false { + t.Error(err) + } +} + +func TestIssue65EvaluateArgvWhenCalledNotWhenImported(t *testing.T) { + os.Args = strings.Fields("prog -a") + v, err := Parse("usage: prog [-ab]", nil, true, "", false, false) + w := map[string]interface{}{"-a": true, "-b": false} + if reflect.DeepEqual(v, w) != true { + t.Error(err) + } + + os.Args = strings.Fields("prog -b") + v, err = Parse("usage: prog [-ab]", nil, true, "", false, false) + w = map[string]interface{}{"-a": false, "-b": true} + if reflect.DeepEqual(v, w) != true { + t.Error(err) + } +} + +func TestIssue71DoubleDashIsNotAValidOptionArgument(t *testing.T) { + _, err := Parse("usage: prog [--log=LEVEL] [--] ...", + []string{"--log", "--", "1", "2"}, true, "", false, false) + if _, ok := err.(*UserError); !ok { + t.Fail() + } + + _, err = Parse(`usage: prog [-l LEVEL] [--] ... + options: -l LEVEL`, []string{"-l", "--", "1", "2"}, true, "", false, false) + if _, ok := err.(*UserError); !ok { + t.Fail() + } +} + +func TestParseSection(t *testing.T) { + v := parseSection("usage:", "foo bar fizz buzz") + w := []string{} + if reflect.DeepEqual(v, w) != true { + t.Fail() + } + + v = parseSection("usage:", "usage: prog") + w = []string{"usage: prog"} + if reflect.DeepEqual(v, w) != true { + t.Fail() + } + + v = parseSection("usage:", "usage: -x\n -y") + w = []string{"usage: -x\n -y"} + if reflect.DeepEqual(v, w) != true { + t.Fail() + } + + usage := `usage: this + +usage:hai +usage: this that + +usage: foo + bar + +PROGRAM USAGE: + foo + bar +usage: +` + "\t" + `too +` + "\t" + `tar +Usage: eggs spam +BAZZ +usage: pit stop` + + v = parseSection("usage:", usage) + w = []string{"usage: this", + "usage:hai", + "usage: this that", + "usage: foo\n bar", + "PROGRAM USAGE:\n foo\n bar", + "usage:\n\ttoo\n\ttar", + "Usage: eggs spam", + "usage: pit stop", + } + if reflect.DeepEqual(v, w) != true { + t.Fail() + } +} + +func TestIssue126DefaultsNotParsedCorrectlyWhenTabs(t *testing.T) { + section := "Options:\n\t--foo= [default: bar]" + v := patternList{newOption("", "--foo", 1, "bar")} + if reflect.DeepEqual(parseDefaults(section), v) != true { + t.Fail() + } +} + +// conf file based test cases +func TestFileTestcases(t *testing.T) { + filenames := []string{"testcases.docopt", "test_golang.docopt"} + for _, filename := range filenames { + raw, err := ioutil.ReadFile(filename) + if err != nil { + t.Fatal(err) + } + + tests, err := parseTest(raw) + if err != nil { + t.Fatal(err) + } + for _, c := range tests { + result, err := Parse(c.doc, c.argv, true, "", false, false) + if _, ok := err.(*UserError); c.userError && !ok { + // expected a user-error + t.Error("testcase:", c.id, "result:", result) + } else if _, ok := err.(*UserError); !c.userError && ok { + // unexpected user-error + t.Error("testcase:", c.id, "error:", err, "result:", result) + } else if reflect.DeepEqual(c.expect, result) != true { + t.Error("testcase:", c.id, "result:", result, "expect:", c.expect) + } + } + } +} + +type testcase struct { + id int + doc string + prog string + argv []string + expect map[string]interface{} + userError bool +} + +func parseTest(raw []byte) ([]testcase, error) { + var res []testcase + commentPattern := regexp.MustCompile("#.*") + raw = commentPattern.ReplaceAll(raw, []byte("")) + raw = bytes.TrimSpace(raw) + if bytes.HasPrefix(raw, []byte(`"""`)) { + raw = raw[3:] + } + + id := 0 + for _, fixture := range bytes.Split(raw, []byte(`r"""`)) { + doc, _, body := stringPartition(string(fixture), `"""`) + for _, cas := range strings.Split(body, "$")[1:] { + argvString, _, expectString := stringPartition(strings.TrimSpace(cas), "\n") + prog, _, argvString := stringPartition(strings.TrimSpace(argvString), " ") + argv := []string{} + if len(argvString) > 0 { + argv = strings.Fields(argvString) + } + var expectUntyped interface{} + err := json.Unmarshal([]byte(expectString), &expectUntyped) + if err != nil { + return nil, err + } + switch expect := expectUntyped.(type) { + case string: // user-error + res = append(res, testcase{id, doc, prog, argv, nil, true}) + case map[string]interface{}: + // convert []interface{} values to []string + // convert float64 values to int + for k, vUntyped := range expect { + switch v := vUntyped.(type) { + case []interface{}: + itemList := make([]string, len(v)) + for i, itemUntyped := range v { + if item, ok := itemUntyped.(string); ok { + itemList[i] = item + } + } + expect[k] = itemList + case float64: + expect[k] = int(v) + } + } + res = append(res, testcase{id, doc, prog, argv, expect, false}) + default: + return nil, fmt.Errorf("unhandled json data type") + } + id++ + } + } + return res, nil +} + +// parseOutput wraps the Parse() function to also return stdout +func parseOutput(doc string, argv []string, help bool, version string, + optionsFirst bool) (map[string]interface{}, string, error) { + stdout := os.Stdout + r, w, _ := os.Pipe() + os.Stdout = w + + args, err := Parse(doc, argv, help, version, optionsFirst, false) + + outChan := make(chan string) + go func() { + var buf bytes.Buffer + io.Copy(&buf, r) + outChan <- buf.String() + }() + + w.Close() + os.Stdout = stdout + output := <-outChan + + return args, output, err +} + +var debugEnabled = false + +func debugOn(l ...interface{}) { + debugEnabled = true + debug(l...) +} +func debugOff(l ...interface{}) { + debug(l...) + debugEnabled = false +} + +func debug(l ...interface{}) { + if debugEnabled { + fmt.Println(l...) + } +} diff --git a/vendor/src/github.com/docopt/docopt-go/example_test.go b/vendor/src/github.com/docopt/docopt-go/example_test.go new file mode 100644 index 000000000..b87a149a2 --- /dev/null +++ b/vendor/src/github.com/docopt/docopt-go/example_test.go @@ -0,0 +1,37 @@ +package docopt + +import ( + "fmt" + "sort" +) + +func ExampleParse() { + usage := `Usage: + config_example tcp [] [--force] [--timeout=] + config_example serial [--baud=] [--timeout=] + config_example -h | --help | --version` + // parse the command line `comfig_example tcp 127.0.0.1 --force` + argv := []string{"tcp", "127.0.0.1", "--force"} + arguments, _ := Parse(usage, argv, true, "0.1.1rc", false) + // sort the keys of the arguments map + var keys []string + for k := range arguments { + keys = append(keys, k) + } + sort.Strings(keys) + // print the argument keys and values + for _, k := range keys { + fmt.Printf("%9s %v\n", k, arguments[k]) + } + // output: + // --baud + // --force true + // --help false + // --timeout + // --version false + // -h false + // 127.0.0.1 + // + // serial false + // tcp true +} diff --git a/vendor/src/github.com/docopt/docopt-go/examples/arguments/arguments_example.go b/vendor/src/github.com/docopt/docopt-go/examples/arguments/arguments_example.go new file mode 100644 index 000000000..7555b99f0 --- /dev/null +++ b/vendor/src/github.com/docopt/docopt-go/examples/arguments/arguments_example.go @@ -0,0 +1,29 @@ +package main + +import ( + "fmt" + "github.com/docopt/docopt-go" +) + +func main() { + usage := `Usage: arguments_example [-vqrh] [FILE] ... + arguments_example (--left | --right) CORRECTION FILE + +Process FILE and optionally apply correction to either left-hand side or +right-hand side. + +Arguments: + FILE optional input file + CORRECTION correction angle, needs FILE, --left or --right to be present + +Options: + -h --help + -v verbose mode + -q quiet mode + -r make report + --left use left-hand side + --right use right-hand side` + + arguments, _ := docopt.Parse(usage, nil, true, "", false) + fmt.Println(arguments) +} diff --git a/vendor/src/github.com/docopt/docopt-go/examples/calculator/calculator_example.go b/vendor/src/github.com/docopt/docopt-go/examples/calculator/calculator_example.go new file mode 100644 index 000000000..26c3b32e5 --- /dev/null +++ b/vendor/src/github.com/docopt/docopt-go/examples/calculator/calculator_example.go @@ -0,0 +1,26 @@ +package main + +import ( + "fmt" + "github.com/docopt/docopt-go" +) + +func main() { + usage := `Not a serious example. + +Usage: + calculator_example ( ( + | - | * | / ) )... + calculator_example [( , )]... + calculator_example (-h | --help) + +Examples: + calculator_example 1 + 2 + 3 + 4 + 5 + calculator_example 1 + 2 '*' 3 / 4 - 5 # note quotes around '*' + calculator_example sum 10 , 20 , 30 , 40 + +Options: + -h, --help +` + arguments, _ := docopt.Parse(usage, nil, true, "", false) + fmt.Println(arguments) +} diff --git a/vendor/src/github.com/docopt/docopt-go/examples/config_file/config_file_example.go b/vendor/src/github.com/docopt/docopt-go/examples/config_file/config_file_example.go new file mode 100644 index 000000000..b0b1c1807 --- /dev/null +++ b/vendor/src/github.com/docopt/docopt-go/examples/config_file/config_file_example.go @@ -0,0 +1,76 @@ +package main + +import ( + "encoding/json" + "fmt" + "github.com/docopt/docopt-go" + "strings" +) + +func loadJSONConfig() map[string]interface{} { + var result map[string]interface{} + jsonData := []byte(`{"--force": true, "--timeout": "10", "--baud": "9600"}`) + json.Unmarshal(jsonData, &result) + return result +} + +func loadIniConfig() map[string]interface{} { + iniData := ` +[default-arguments] +--force +--baud=19200 +=localhost` + // trivial ini parser + // default value for an item is bool: true (for --force) + // otherwise the value is a string + iniParsed := make(map[string]map[string]interface{}) + var section string + for _, line := range strings.Split(iniData, "\n") { + if strings.HasPrefix(line, "[") { + section = line + iniParsed[section] = make(map[string]interface{}) + } else if section != "" { + kv := strings.SplitN(line, "=", 2) + if len(kv) == 1 { + iniParsed[section][kv[0]] = true + } else if len(kv) == 2 { + iniParsed[section][kv[0]] = kv[1] + } + } + } + return iniParsed["[default-arguments]"] +} + +// merge combines two maps. +// truthiness takes priority over falsiness +// mapA takes priority over mapB +func merge(mapA, mapB map[string]interface{}) map[string]interface{} { + result := make(map[string]interface{}) + for k, v := range mapA { + result[k] = v + } + for k, v := range mapB { + if _, ok := result[k]; !ok || result[k] == nil || result[k] == false { + result[k] = v + } + } + return result +} + +func main() { + usage := `Usage: + config_file_example tcp [] [--force] [--timeout=] + config_file_example serial [--baud=] [--timeout=] + config_file_example -h | --help | --version` + + jsonConfig := loadJSONConfig() + iniConfig := loadIniConfig() + arguments, _ := docopt.Parse(usage, nil, true, "0.1.1rc", false) + + // Arguments take priority over INI, INI takes priority over JSON + result := merge(arguments, merge(iniConfig, jsonConfig)) + + fmt.Println("JSON config: ", jsonConfig) + fmt.Println("INI config: ", iniConfig) + fmt.Println("Result: ", result) +} diff --git a/vendor/src/github.com/docopt/docopt-go/examples/counted/counted_example.go b/vendor/src/github.com/docopt/docopt-go/examples/counted/counted_example.go new file mode 100644 index 000000000..c1da06f21 --- /dev/null +++ b/vendor/src/github.com/docopt/docopt-go/examples/counted/counted_example.go @@ -0,0 +1,22 @@ +package main + +import ( + "fmt" + "github.com/docopt/docopt-go" +) + +func main() { + usage := `Usage: counted_example --help + counted_example -v... + counted_example go [go] + counted_example (--path=)... + counted_example + +Try: counted_example -vvvvvvvvvv + counted_example go go + counted_example --path ./here --path ./there + counted_example this.txt that.txt` + + arguments, _ := docopt.Parse(usage, nil, true, "", false) + fmt.Println(arguments) +} diff --git a/vendor/src/github.com/docopt/docopt-go/examples/git/branch/git_branch.go b/vendor/src/github.com/docopt/docopt-go/examples/git/branch/git_branch.go new file mode 100644 index 000000000..47a488415 --- /dev/null +++ b/vendor/src/github.com/docopt/docopt-go/examples/git/branch/git_branch.go @@ -0,0 +1,38 @@ +package git + +import ( + "fmt" + "github.com/docopt/docopt-go" +) + +func main() { + usage := `usage: git branch [options] [-r | -a] [--merged= | --no-merged=] + git branch [options] [-l] [-f] [] + git branch [options] [-r] (-d | -D) + git branch [options] (-m | -M) [] + +Generic options: + -h, --help + -v, --verbose show hash and subject, give twice for upstream branch + -t, --track set up tracking mode (see git-pull(1)) + --set-upstream change upstream info + --color= use colored output + -r act on remote-tracking branches + --contains= print only branches that contain the commit + --abbrev= use digits to display SHA-1s + +Specific git-branch actions: + -a list both remote-tracking and local branches + -d delete fully merged branch + -D delete branch (even if not merged) + -m move/rename a branch and its reflog + -M move/rename a branch, even if target exists + -l create the branch's reflog + -f, --force force creation (when already exists) + --no-merged= print only not merged branches + --merged= print only merged branches +` + + args, _ := docopt.Parse(usage, nil, true, "", false) + fmt.Println(args) +} diff --git a/vendor/src/github.com/docopt/docopt-go/examples/git/checkout/git_checkout.go b/vendor/src/github.com/docopt/docopt-go/examples/git/checkout/git_checkout.go new file mode 100644 index 000000000..00fe71ce8 --- /dev/null +++ b/vendor/src/github.com/docopt/docopt-go/examples/git/checkout/git_checkout.go @@ -0,0 +1,30 @@ +package git + +import ( + "fmt" + "github.com/docopt/docopt-go" +) + +func main() { + usage := `usage: git checkout [options] + git checkout [options] -- ... + +options: + -q, --quiet suppress progress reporting + -b create and checkout a new branch + -B create/reset and checkout a branch + -l create reflog for new branch + -t, --track set upstream info for new branch + --orphan + new unparented branch + -2, --ours checkout our version for unmerged files + -3, --theirs checkout their version for unmerged files + -f, --force force checkout (throw away local modifications) + -m, --merge perform a 3-way merge with the new branch + --conflict