Switch to dep for dependency management

This commit is contained in:
Dan Sosedoff
2018-08-31 21:49:24 -05:00
parent 847f47c5d3
commit ad81f666a5
537 changed files with 167100 additions and 34410 deletions

View File

@@ -5,8 +5,13 @@
package flags
import (
"bytes"
"fmt"
"os"
"path"
"sort"
"strings"
"unicode/utf8"
)
// A Parser provides command line option parsing. It can contain several
@@ -32,6 +37,21 @@ type Parser struct {
// or an error to indicate a parse failure.
UnknownOptionHandler func(option string, arg SplitArgument, args []string) ([]string, error)
// CompletionHandler is a function gets called to handle the completion of
// items. By default, the items are printed and the application is exited.
// You can override this default behavior by specifying a custom CompletionHandler.
CompletionHandler func(items []Completion)
// CommandHandler is a function that gets called to handle execution of a
// command. By default, the command will simply be executed. This can be
// overridden to perform certain actions (such as applying global flags)
// just before the command is executed. Note that if you override the
// handler it is your responsibility to call the command.Execute function.
//
// The command passed into CommandHandler may be nil in case there is no
// command to be executed when parsing has finished.
CommandHandler func(command Commander, args []string) error
internalError error
}
@@ -67,7 +87,7 @@ const (
// -h and --help options. When either -h or --help is specified on the
// command line, the parser will return the special error of type
// ErrHelp. When PrintErrors is also specified, then the help message
// will also be automatically printed to os.Stderr.
// will also be automatically printed to os.Stdout.
HelpFlag = 1 << iota
// PassDoubleDash passes all arguments after a double dash, --, as
@@ -80,7 +100,8 @@ const (
IgnoreUnknown
// PrintErrors prints any errors which occurred during parsing to
// os.Stderr.
// os.Stderr. In the special case of ErrHelp, the message will be printed
// to os.Stdout.
PrintErrors
// PassAfterNonOption passes all arguments after the first non option
@@ -93,6 +114,17 @@ const (
Default = HelpFlag | PrintErrors | PassDoubleDash
)
type parseState struct {
arg string
args []string
retargs []string
positional []*Arg
err error
command *Command
lookup lookup
}
// Parse is a convenience function to parse command line options with default
// settings. The provided data is a pointer to a struct representing the
// default option group (named "Application Options"). For more control, use
@@ -162,14 +194,19 @@ func (p *Parser) Parse() ([]string, error) {
//
// When the common help group has been added (AddHelp) and either -h or --help
// was specified in the command line arguments, a help message will be
// automatically printed. Furthermore, the special error type ErrHelp is returned.
// automatically printed if the PrintErrors option is enabled.
// Furthermore, the special error type ErrHelp is returned.
// It is up to the caller to exit the program if so desired.
func (p *Parser) ParseArgs(args []string) ([]string, error) {
if p.internalError != nil {
return nil, p.internalError
}
p.clearIsSet()
p.eachOption(func(c *Command, g *Group, option *Option) {
option.isSet = false
option.isSetDefault = false
option.updateDefaultLiteral()
})
// Add built-in help group to all commands if necessary
if (p.Options & HelpFlag) != None {
@@ -180,13 +217,15 @@ func (p *Parser) ParseArgs(args []string) ([]string, error) {
if len(compval) != 0 {
comp := &completion{parser: p}
items := comp.complete(args)
if compval == "verbose" {
comp.ShowDescriptions = true
if p.CompletionHandler != nil {
p.CompletionHandler(items)
} else {
comp.print(items, compval == "verbose")
os.Exit(0)
}
comp.execute(args)
return nil, nil
}
@@ -253,17 +292,13 @@ func (p *Parser) ParseArgs(args []string) ([]string, error) {
}
if s.err == nil {
p.eachCommand(func(c *Command) {
c.eachGroup(func(g *Group) {
for _, option := range g.options {
if option.isSet {
continue
}
p.eachOption(func(c *Command, g *Group, option *Option) {
if option.preventDefault {
return
}
option.clearDefault()
}
})
}, true)
option.clearDefault()
})
s.checkRequired(p)
}
@@ -275,12 +310,391 @@ func (p *Parser) ParseArgs(args []string) ([]string, error) {
} else if len(s.command.commands) != 0 && !s.command.SubcommandsOptional {
reterr = s.estimateCommand()
} else if cmd, ok := s.command.data.(Commander); ok {
reterr = cmd.Execute(s.retargs)
if p.CommandHandler != nil {
reterr = p.CommandHandler(cmd, s.retargs)
} else {
reterr = cmd.Execute(s.retargs)
}
} else if p.CommandHandler != nil {
reterr = p.CommandHandler(nil, s.retargs)
}
if reterr != nil {
return append([]string{s.arg}, s.args...), p.printError(reterr)
var retargs []string
if ourErr, ok := reterr.(*Error); !ok || ourErr.Type != ErrHelp {
retargs = append([]string{s.arg}, s.args...)
} else {
retargs = s.args
}
return retargs, p.printError(reterr)
}
return s.retargs, nil
}
func (p *parseState) eof() bool {
return len(p.args) == 0
}
func (p *parseState) pop() string {
if p.eof() {
return ""
}
p.arg = p.args[0]
p.args = p.args[1:]
return p.arg
}
func (p *parseState) peek() string {
if p.eof() {
return ""
}
return p.args[0]
}
func (p *parseState) checkRequired(parser *Parser) error {
c := parser.Command
var required []*Option
for c != nil {
c.eachGroup(func(g *Group) {
for _, option := range g.options {
if !option.isSet && option.Required {
required = append(required, option)
}
}
})
c = c.Active
}
if len(required) == 0 {
if len(p.positional) > 0 {
var reqnames []string
for _, arg := range p.positional {
argRequired := (!arg.isRemaining() && p.command.ArgsRequired) || arg.Required != -1 || arg.RequiredMaximum != -1
if !argRequired {
continue
}
if arg.isRemaining() {
if arg.value.Len() < arg.Required {
var arguments string
if arg.Required > 1 {
arguments = "arguments, but got only " + fmt.Sprintf("%d", arg.value.Len())
} else {
arguments = "argument"
}
reqnames = append(reqnames, "`"+arg.Name+" (at least "+fmt.Sprintf("%d", arg.Required)+" "+arguments+")`")
} else if arg.RequiredMaximum != -1 && arg.value.Len() > arg.RequiredMaximum {
if arg.RequiredMaximum == 0 {
reqnames = append(reqnames, "`"+arg.Name+" (zero arguments)`")
} else {
var arguments string
if arg.RequiredMaximum > 1 {
arguments = "arguments, but got " + fmt.Sprintf("%d", arg.value.Len())
} else {
arguments = "argument"
}
reqnames = append(reqnames, "`"+arg.Name+" (at most "+fmt.Sprintf("%d", arg.RequiredMaximum)+" "+arguments+")`")
}
}
} else {
reqnames = append(reqnames, "`"+arg.Name+"`")
}
}
if len(reqnames) == 0 {
return nil
}
var msg string
if len(reqnames) == 1 {
msg = fmt.Sprintf("the required argument %s was not provided", reqnames[0])
} else {
msg = fmt.Sprintf("the required arguments %s and %s were not provided",
strings.Join(reqnames[:len(reqnames)-1], ", "), reqnames[len(reqnames)-1])
}
p.err = newError(ErrRequired, msg)
return p.err
}
return nil
}
names := make([]string, 0, len(required))
for _, k := range required {
names = append(names, "`"+k.String()+"'")
}
sort.Strings(names)
var msg string
if len(names) == 1 {
msg = fmt.Sprintf("the required flag %s was not specified", names[0])
} else {
msg = fmt.Sprintf("the required flags %s and %s were not specified",
strings.Join(names[:len(names)-1], ", "), names[len(names)-1])
}
p.err = newError(ErrRequired, msg)
return p.err
}
func (p *parseState) estimateCommand() error {
commands := p.command.sortedVisibleCommands()
cmdnames := make([]string, len(commands))
for i, v := range commands {
cmdnames[i] = v.Name
}
var msg string
var errtype ErrorType
if len(p.retargs) != 0 {
c, l := closestChoice(p.retargs[0], cmdnames)
msg = fmt.Sprintf("Unknown command `%s'", p.retargs[0])
errtype = ErrUnknownCommand
if float32(l)/float32(len(c)) < 0.5 {
msg = fmt.Sprintf("%s, did you mean `%s'?", msg, c)
} else if len(cmdnames) == 1 {
msg = fmt.Sprintf("%s. You should use the %s command",
msg,
cmdnames[0])
} else if len(cmdnames) > 1 {
msg = fmt.Sprintf("%s. Please specify one command of: %s or %s",
msg,
strings.Join(cmdnames[:len(cmdnames)-1], ", "),
cmdnames[len(cmdnames)-1])
}
} else {
errtype = ErrCommandRequired
if len(cmdnames) == 1 {
msg = fmt.Sprintf("Please specify the %s command", cmdnames[0])
} else if len(cmdnames) > 1 {
msg = fmt.Sprintf("Please specify one command of: %s or %s",
strings.Join(cmdnames[:len(cmdnames)-1], ", "),
cmdnames[len(cmdnames)-1])
}
}
return newError(errtype, msg)
}
func (p *Parser) parseOption(s *parseState, name string, option *Option, canarg bool, argument *string) (err error) {
if !option.canArgument() {
if argument != nil {
return newErrorf(ErrNoArgumentForBool, "bool flag `%s' cannot have an argument", option)
}
err = option.set(nil)
} else if argument != nil || (canarg && !s.eof()) {
var arg string
if argument != nil {
arg = *argument
} else {
arg = s.pop()
if argumentIsOption(arg) && !(option.isSignedNumber() && len(arg) > 1 && arg[0] == '-' && arg[1] >= '0' && arg[1] <= '9') {
return newErrorf(ErrExpectedArgument, "expected argument for flag `%s', but got option `%s'", option, arg)
} else if p.Options&PassDoubleDash != 0 && arg == "--" {
return newErrorf(ErrExpectedArgument, "expected argument for flag `%s', but got double dash `--'", option)
}
}
if option.tag.Get("unquote") != "false" {
arg, err = unquoteIfPossible(arg)
}
if err == nil {
err = option.set(&arg)
}
} else if option.OptionalArgument {
option.empty()
for _, v := range option.OptionalValue {
err = option.set(&v)
if err != nil {
break
}
}
} else {
err = newErrorf(ErrExpectedArgument, "expected argument for flag `%s'", option)
}
if err != nil {
if _, ok := err.(*Error); !ok {
err = newErrorf(ErrMarshal, "invalid argument for flag `%s' (expected %s): %s",
option,
option.value.Type(),
err.Error())
}
}
return err
}
func (p *Parser) parseLong(s *parseState, name string, argument *string) error {
if option := s.lookup.longNames[name]; option != nil {
// Only long options that are required can consume an argument
// from the argument list
canarg := !option.OptionalArgument
return p.parseOption(s, name, option, canarg, argument)
}
return newErrorf(ErrUnknownFlag, "unknown flag `%s'", name)
}
func (p *Parser) splitShortConcatArg(s *parseState, optname string) (string, *string) {
c, n := utf8.DecodeRuneInString(optname)
if n == len(optname) {
return optname, nil
}
first := string(c)
if option := s.lookup.shortNames[first]; option != nil && option.canArgument() {
arg := optname[n:]
return first, &arg
}
return optname, nil
}
func (p *Parser) parseShort(s *parseState, optname string, argument *string) error {
if argument == nil {
optname, argument = p.splitShortConcatArg(s, optname)
}
for i, c := range optname {
shortname := string(c)
if option := s.lookup.shortNames[shortname]; option != nil {
// Only the last short argument can consume an argument from
// the arguments list, and only if it's non optional
canarg := (i+utf8.RuneLen(c) == len(optname)) && !option.OptionalArgument
if err := p.parseOption(s, shortname, option, canarg, argument); err != nil {
return err
}
} else {
return newErrorf(ErrUnknownFlag, "unknown flag `%s'", shortname)
}
// Only the first option can have a concatted argument, so just
// clear argument here
argument = nil
}
return nil
}
func (p *parseState) addArgs(args ...string) error {
for len(p.positional) > 0 && len(args) > 0 {
arg := p.positional[0]
if err := convert(args[0], arg.value, arg.tag); err != nil {
p.err = err
return err
}
if !arg.isRemaining() {
p.positional = p.positional[1:]
}
args = args[1:]
}
p.retargs = append(p.retargs, args...)
return nil
}
func (p *Parser) parseNonOption(s *parseState) error {
if len(s.positional) > 0 {
return s.addArgs(s.arg)
}
if len(s.command.commands) > 0 && len(s.retargs) == 0 {
if cmd := s.lookup.commands[s.arg]; cmd != nil {
s.command.Active = cmd
cmd.fillParseState(s)
return nil
} else if !s.command.SubcommandsOptional {
s.addArgs(s.arg)
return newErrorf(ErrUnknownCommand, "Unknown command `%s'", s.arg)
}
}
if (p.Options & PassAfterNonOption) != None {
// If PassAfterNonOption is set then all remaining arguments
// are considered positional
if err := s.addArgs(s.arg); err != nil {
return err
}
if err := s.addArgs(s.args...); err != nil {
return err
}
s.args = []string{}
} else {
return s.addArgs(s.arg)
}
return nil
}
func (p *Parser) showBuiltinHelp() error {
var b bytes.Buffer
p.WriteHelp(&b)
return newError(ErrHelp, b.String())
}
func (p *Parser) printError(err error) error {
if err != nil && (p.Options&PrintErrors) != None {
flagsErr, ok := err.(*Error)
if ok && flagsErr.Type == ErrHelp {
fmt.Fprintln(os.Stdout, err)
} else {
fmt.Fprintln(os.Stderr, err)
}
}
return err
}
func (p *Parser) clearIsSet() {
p.eachCommand(func(c *Command) {
c.eachGroup(func(g *Group) {
for _, option := range g.options {
option.isSet = false
}
})
}, true)
}