github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/go/cmdflag/flag.go (about) 1 // Copyright 2017 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Package cmdflag handles flag processing common to several go tools. 6 package cmdflag 7 8 import ( 9 "errors" 10 "flag" 11 "fmt" 12 "strings" 13 ) 14 15 // The flag handling part of go commands such as test is large and distracting. 16 // We can't use the standard flag package because some of the flags from 17 // our command line are for us, and some are for the binary we're running, 18 // and some are for both. 19 20 // ErrFlagTerminator indicates the distinguished token "--", which causes the 21 // flag package to treat all subsequent arguments as non-flags. 22 var ErrFlagTerminator = errors.New("flag terminator") 23 24 // A FlagNotDefinedError indicates a flag-like argument that does not correspond 25 // to any registered flag in a FlagSet. 26 type FlagNotDefinedError struct { 27 RawArg string // the original argument, like --foo or -foo=value 28 Name string 29 HasValue bool // is this the -foo=value or --foo=value form? 30 Value string // only provided if HasValue is true 31 } 32 33 func (e FlagNotDefinedError) Error() string { 34 return fmt.Sprintf("flag provided but not defined: -%s", e.Name) 35 } 36 37 // A NonFlagError indicates an argument that is not a syntactically-valid flag. 38 type NonFlagError struct { 39 RawArg string 40 } 41 42 func (e NonFlagError) Error() string { 43 return fmt.Sprintf("not a flag: %q", e.RawArg) 44 } 45 46 // ParseOne sees if args[0] is present in the given flag set and if so, 47 // sets its value and returns the flag along with the remaining (unused) arguments. 48 // 49 // ParseOne always returns either a non-nil Flag or a non-nil error, 50 // and always consumes at least one argument (even on error). 51 // 52 // Unlike (*flag.FlagSet).Parse, ParseOne does not log its own errors. 53 func ParseOne(fs *flag.FlagSet, args []string) (f *flag.Flag, remainingArgs []string, err error) { 54 // This function is loosely derived from (*flag.FlagSet).parseOne. 55 56 raw, args := args[0], args[1:] 57 arg := raw 58 if strings.HasPrefix(arg, "--") { 59 if arg == "--" { 60 return nil, args, ErrFlagTerminator 61 } 62 arg = arg[1:] // reduce two minuses to one 63 } 64 65 switch arg { 66 case "-?", "-h", "-help": 67 return nil, args, flag.ErrHelp 68 } 69 if len(arg) < 2 || arg[0] != '-' || arg[1] == '-' || arg[1] == '=' { 70 return nil, args, NonFlagError{RawArg: raw} 71 } 72 73 name, value, hasValue := strings.Cut(arg[1:], "=") 74 75 f = fs.Lookup(name) 76 if f == nil { 77 return nil, args, FlagNotDefinedError{ 78 RawArg: raw, 79 Name: name, 80 HasValue: hasValue, 81 Value: value, 82 } 83 } 84 85 // Use fs.Set instead of f.Value.Set below so that any subsequent call to 86 // fs.Visit will correctly visit the flags that have been set. 87 88 failf := func(format string, a ...any) (*flag.Flag, []string, error) { 89 return f, args, fmt.Errorf(format, a...) 90 } 91 92 if fv, ok := f.Value.(boolFlag); ok && fv.IsBoolFlag() { // special case: doesn't need an arg 93 if hasValue { 94 if err := fs.Set(name, value); err != nil { 95 return failf("invalid boolean value %q for -%s: %v", value, name, err) 96 } 97 } else { 98 if err := fs.Set(name, "true"); err != nil { 99 return failf("invalid boolean flag %s: %v", name, err) 100 } 101 } 102 } else { 103 // It must have a value, which might be the next argument. 104 if !hasValue && len(args) > 0 { 105 // value is the next arg 106 hasValue = true 107 value, args = args[0], args[1:] 108 } 109 if !hasValue { 110 return failf("flag needs an argument: -%s", name) 111 } 112 if err := fs.Set(name, value); err != nil { 113 return failf("invalid value %q for flag -%s: %v", value, name, err) 114 } 115 } 116 117 return f, args, nil 118 } 119 120 type boolFlag interface { 121 IsBoolFlag() bool 122 }