1
2
3
4
5
6 package cmdflag
7
8 import (
9 "errors"
10 "flag"
11 "fmt"
12 "strings"
13 )
14
15
16
17
18
19
20
21
22 var ErrFlagTerminator = errors.New("flag terminator")
23
24
25
26 type FlagNotDefinedError struct {
27 RawArg string
28 Name string
29 HasValue bool
30 Value string
31 }
32
33 func (e FlagNotDefinedError) Error() string {
34 return fmt.Sprintf("flag provided but not defined: -%s", e.Name)
35 }
36
37
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
47
48
49
50
51
52
53 func ParseOne(fs *flag.FlagSet, args []string) (f *flag.Flag, remainingArgs []string, err error) {
54
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:]
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
86
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() {
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
104 if !hasValue && len(args) > 0 {
105
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 }
123
View as plain text