github.com/GeniusesGroup/libgo@v0.0.0-20220929090155-5ff932cb408e/command/flag.go (about) 1 /* For license and copyright information please see the LEGAL file in the code repository */ 2 3 package cmd 4 5 import ( 6 "strings" 7 8 "github.com/GeniusesGroup/libgo/protocol" 9 ) 10 11 // A FlagSet represents a set of defined fields 12 // 13 // field names must be unique within a FlagSet. An attempt to define a flag whose 14 // name is already in use will cause a panic. 15 type FlagSet struct { 16 fields []protocol.Field 17 parsed []protocol.Field 18 args []string // arguments after flags 19 } 20 21 // argument list should not include the commands name. 22 func (f *FlagSet) Init(fields []protocol.Field, arguments []string) { 23 f.fields = fields 24 f.checkFields() 25 if f.parsed == nil { 26 f.parsed = make([]protocol.Field, 0, len(f.fields)) 27 } 28 f.args = arguments 29 } 30 func (f *FlagSet) Reinit() { 31 f.fields = nil 32 f.parsed = f.parsed[:0] 33 f.args = nil 34 } 35 func (f *FlagSet) Deinit() { f.Reinit() } 36 37 // Parse parses flag definitions from f.args. 38 // It Must be called after Init called and before flags are accessed by the program. 39 func (f *FlagSet) Parse() (err protocol.Error) { 40 for len(f.args) > 0 { 41 err = f.parseOne() 42 if err != nil { 43 return 44 } 45 } 46 return 47 } 48 49 // NFlag returns the number of flags that have been set. 50 func (f *FlagSet) NFlag() int { return len(f.fields) } 51 52 // NArg is the number of arguments remaining after flags have been processed. 53 func (f *FlagSet) NArg() int { return len(f.args) } 54 55 // Args returns the non-flag arguments. 56 func (f *FlagSet) Args() []string { return f.args } 57 58 // Arg returns the i'th argument. Arg(0) is the first remaining argument 59 // after flags have been processed. Arg returns an empty string if the 60 // requested element does not exist. 61 func (f *FlagSet) Arg(i int) string { 62 if i < 0 || i >= len(f.args) { 63 return "" 64 } 65 return f.args[i] 66 } 67 68 // VisitAll visits the flags in given order, calling fn for each. 69 // It visits all flags, even those not set. 70 func (f *FlagSet) VisitAll(fn func(protocol.Field)) { 71 for _, flag := range f.fields { 72 fn(flag) 73 } 74 } 75 76 // Visit visits the flags in given order, calling fn for each. 77 // It visits only those flags that have been set. 78 func (f *FlagSet) Visit(fn func(protocol.Field)) { 79 for _, field := range f.parsed { 80 fn(field) 81 } 82 } 83 84 // Lookup returns the Field of the named flag, returning nil if none exists. 85 func (f *FlagSet) Lookup(name string) protocol.Field { 86 for _, field := range f.fields { 87 if field.Name() == name || field.Abbreviation() == name { 88 return field 89 } 90 } 91 return nil 92 } 93 94 // Set sets the value of the named flag. 95 func (f *FlagSet) Set(name, value string) (err protocol.Error) { 96 var flag = f.Lookup(name) 97 if flag == nil { 98 return &ErrFlagNotFound 99 } 100 101 var hasValue = len(value) > 0 102 103 if flag.Type() == protocol.FieldType_Boolean && !hasValue { // special case: doesn't need an arg 104 value = "true" 105 } else { 106 // It must have a value, which might be the next argument. 107 if !hasValue && len(f.args) > 0 { 108 // value is the next arg 109 hasValue = true 110 value, f.args = f.args[0], f.args[1:] 111 } 112 if !hasValue { 113 return &ErrFlagNeedsAnArgument 114 } 115 } 116 117 err = flag.FromString(value) 118 if err != nil { 119 return 120 } 121 f.parsed = append(f.parsed, flag) 122 return nil 123 } 124 125 func (f *FlagSet) checkFields() { 126 var fieldsName = make([]string, 0, len(f.fields)) 127 128 for _, field := range f.fields { 129 var fieldName = field.Name() 130 131 // Flag must not begin "-" or contain "=". 132 if strings.HasPrefix(fieldName, "-") { 133 panic(fieldName + " flag begins with -") 134 } else if strings.Contains(fieldName, "=") { 135 panic(fieldName + " flag %q contains =") 136 } 137 138 fieldsName = append(fieldsName, fieldName) 139 } 140 141 for i := 0; i < len(fieldsName); i++ { 142 for j := i + 1; j < len(fieldsName); j++ { 143 var fieldName = fieldsName[i] 144 if fieldName == fieldsName[j] { 145 // Happens only if flags are declared with identical names 146 if fieldName == "" { 147 panic(fieldName + " flag redefined.") 148 } else { 149 panic(fieldName + "flag redefined as" + fieldsName[j]) 150 } 151 } 152 } 153 } 154 } 155 156 // parseOne parses one flag 157 func (f *FlagSet) parseOne() (err protocol.Error) { 158 if len(f.args) == 0 { 159 return nil 160 } 161 162 var s = f.args[0] 163 if len(s) < 2 || s[0] != '-' { 164 return &ErrFlagBadSyntax 165 } 166 var numMinuses = 1 167 if s[1] == '-' { 168 numMinuses++ 169 if len(s) == 2 { // "--" terminates the flags 170 f.args = f.args[1:] 171 return &ErrFlagBadSyntax 172 } 173 } 174 var name = s[numMinuses:] 175 if len(name) == 0 || name[0] == '-' || name[0] == '=' { 176 return &ErrFlagBadSyntax 177 } 178 179 // it's a flag. does it have an argument? 180 f.args = f.args[1:] 181 var value string 182 for i := 1; i < len(name); i++ { // equals cannot be first 183 if name[i] == '=' { 184 value = name[i+1:] 185 name = name[0:i] 186 break 187 } 188 } 189 190 err = f.Set(name, value) 191 return 192 }