| // Copyright 2017 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| // Package cmdflag handles flag processing common to several go tools. |
| package cmdflag |
| |
| import ( |
| "errors" |
| "flag" |
| "fmt" |
| "strings" |
| ) |
| |
| // The flag handling part of go commands such as test is large and distracting. |
| // We can't use the standard flag package because some of the flags from |
| // our command line are for us, and some are for the binary we're running, |
| // and some are for both. |
| |
| // ErrFlagTerminator indicates the distinguished token "--", which causes the |
| // flag package to treat all subsequent arguments as non-flags. |
| var ErrFlagTerminator = errors.New("flag terminator") |
| |
| // A FlagNotDefinedError indicates a flag-like argument that does not correspond |
| // to any registered flag in a FlagSet. |
| type FlagNotDefinedError struct { |
| RawArg string // the original argument, like --foo or -foo=value |
| Name string |
| HasValue bool // is this the -foo=value or --foo=value form? |
| Value string // only provided if HasValue is true |
| } |
| |
| func (e FlagNotDefinedError) Error() string { |
| return fmt.Sprintf("flag provided but not defined: -%s", e.Name) |
| } |
| |
| // A NonFlagError indicates an argument that is not a syntactically-valid flag. |
| type NonFlagError struct { |
| RawArg string |
| } |
| |
| func (e NonFlagError) Error() string { |
| return fmt.Sprintf("not a flag: %q", e.RawArg) |
| } |
| |
| // ParseOne sees if args[0] is present in the given flag set and if so, |
| // sets its value and returns the flag along with the remaining (unused) arguments. |
| // |
| // ParseOne always returns either a non-nil Flag or a non-nil error, |
| // and always consumes at least one argument (even on error). |
| // |
| // Unlike (*flag.FlagSet).Parse, ParseOne does not log its own errors. |
| func ParseOne(fs *flag.FlagSet, args []string) (f *flag.Flag, remainingArgs []string, err error) { |
| // This function is loosely derived from (*flag.FlagSet).parseOne. |
| |
| raw, args := args[0], args[1:] |
| arg := raw |
| if strings.HasPrefix(arg, "--") { |
| if arg == "--" { |
| return nil, args, ErrFlagTerminator |
| } |
| arg = arg[1:] // reduce two minuses to one |
| } |
| |
| switch arg { |
| case "-?", "-h", "-help": |
| return nil, args, flag.ErrHelp |
| } |
| if len(arg) < 2 || arg[0] != '-' || arg[1] == '-' || arg[1] == '=' { |
| return nil, args, NonFlagError{RawArg: raw} |
| } |
| |
| name := arg[1:] |
| hasValue := false |
| value := "" |
| if i := strings.Index(name, "="); i >= 0 { |
| value = name[i+1:] |
| hasValue = true |
| name = name[0:i] |
| } |
| |
| f = fs.Lookup(name) |
| if f == nil { |
| return nil, args, FlagNotDefinedError{ |
| RawArg: raw, |
| Name: name, |
| HasValue: hasValue, |
| Value: value, |
| } |
| } |
| |
| // Use fs.Set instead of f.Value.Set below so that any subsequent call to |
| // fs.Visit will correctly visit the flags that have been set. |
| |
| failf := func(format string, a ...interface{}) (*flag.Flag, []string, error) { |
| return f, args, fmt.Errorf(format, a...) |
| } |
| |
| if fv, ok := f.Value.(boolFlag); ok && fv.IsBoolFlag() { // special case: doesn't need an arg |
| if hasValue { |
| if err := fs.Set(name, value); err != nil { |
| return failf("invalid boolean value %q for -%s: %v", value, name, err) |
| } |
| } else { |
| if err := fs.Set(name, "true"); err != nil { |
| return failf("invalid boolean flag %s: %v", name, err) |
| } |
| } |
| } else { |
| // It must have a value, which might be the next argument. |
| if !hasValue && len(args) > 0 { |
| // value is the next arg |
| hasValue = true |
| value, args = args[0], args[1:] |
| } |
| if !hasValue { |
| return failf("flag needs an argument: -%s", name) |
| } |
| if err := fs.Set(name, value); err != nil { |
| return failf("invalid value %q for flag -%s: %v", value, name, err) |
| } |
| } |
| |
| return f, args, nil |
| } |
| |
| type boolFlag interface { |
| IsBoolFlag() bool |
| } |