1package flags 2 3import ( 4 "bytes" 5 "fmt" 6 "os" 7 "reflect" 8 "strings" 9 "unicode/utf8" 10) 11 12// Option flag information. Contains a description of the option, short and 13// long name as well as a default value and whether an argument for this 14// flag is optional. 15type Option struct { 16 // The description of the option flag. This description is shown 17 // automatically in the built-in help. 18 Description string 19 20 // The short name of the option (a single character). If not 0, the 21 // option flag can be 'activated' using -<ShortName>. Either ShortName 22 // or LongName needs to be non-empty. 23 ShortName rune 24 25 // The long name of the option. If not "", the option flag can be 26 // activated using --<LongName>. Either ShortName or LongName needs 27 // to be non-empty. 28 LongName string 29 30 // The default value of the option. 31 Default []string 32 33 // The optional environment default value key name. 34 EnvDefaultKey string 35 36 // The optional delimiter string for EnvDefaultKey values. 37 EnvDefaultDelim string 38 39 // If true, specifies that the argument to an option flag is optional. 40 // When no argument to the flag is specified on the command line, the 41 // value of OptionalValue will be set in the field this option represents. 42 // This is only valid for non-boolean options. 43 OptionalArgument bool 44 45 // The optional value of the option. The optional value is used when 46 // the option flag is marked as having an OptionalArgument. This means 47 // that when the flag is specified, but no option argument is given, 48 // the value of the field this option represents will be set to 49 // OptionalValue. This is only valid for non-boolean options. 50 OptionalValue []string 51 52 // If true, the option _must_ be specified on the command line. If the 53 // option is not specified, the parser will generate an ErrRequired type 54 // error. 55 Required bool 56 57 // A name for the value of an option shown in the Help as --flag [ValueName] 58 ValueName string 59 60 // A mask value to show in the help instead of the default value. This 61 // is useful for hiding sensitive information in the help, such as 62 // passwords. 63 DefaultMask string 64 65 // If non empty, only a certain set of values is allowed for an option. 66 Choices []string 67 68 // If true, the option is not displayed in the help or man page 69 Hidden bool 70 71 // The group which the option belongs to 72 group *Group 73 74 // The struct field which the option represents. 75 field reflect.StructField 76 77 // The struct field value which the option represents. 78 value reflect.Value 79 80 // Determines if the option will be always quoted in the INI output 81 iniQuote bool 82 83 tag multiTag 84 isSet bool 85 isSetDefault bool 86 preventDefault bool 87 clearReferenceBeforeSet bool 88 89 defaultLiteral string 90} 91 92// LongNameWithNamespace returns the option's long name with the group namespaces 93// prepended by walking up the option's group tree. Namespaces and the long name 94// itself are separated by the parser's namespace delimiter. If the long name is 95// empty an empty string is returned. 96func (option *Option) LongNameWithNamespace() string { 97 if len(option.LongName) == 0 { 98 return "" 99 } 100 101 // fetch the namespace delimiter from the parser which is always at the 102 // end of the group hierarchy 103 namespaceDelimiter := "" 104 g := option.group 105 106 for { 107 if p, ok := g.parent.(*Parser); ok { 108 namespaceDelimiter = p.NamespaceDelimiter 109 110 break 111 } 112 113 switch i := g.parent.(type) { 114 case *Command: 115 g = i.Group 116 case *Group: 117 g = i 118 } 119 } 120 121 // concatenate long name with namespace 122 longName := option.LongName 123 g = option.group 124 125 for g != nil { 126 if g.Namespace != "" { 127 longName = g.Namespace + namespaceDelimiter + longName 128 } 129 130 switch i := g.parent.(type) { 131 case *Command: 132 g = i.Group 133 case *Group: 134 g = i 135 case *Parser: 136 g = nil 137 } 138 } 139 140 return longName 141} 142 143// EnvKeyWithNamespace returns the option's env key with the group namespaces 144// prepended by walking up the option's group tree. Namespaces and the env key 145// itself are separated by the parser's namespace delimiter. If the env key is 146// empty an empty string is returned. 147func (option *Option) EnvKeyWithNamespace() string { 148 if len(option.EnvDefaultKey) == 0 { 149 return "" 150 } 151 152 // fetch the namespace delimiter from the parser which is always at the 153 // end of the group hierarchy 154 namespaceDelimiter := "" 155 g := option.group 156 157 for { 158 if p, ok := g.parent.(*Parser); ok { 159 namespaceDelimiter = p.EnvNamespaceDelimiter 160 161 break 162 } 163 164 switch i := g.parent.(type) { 165 case *Command: 166 g = i.Group 167 case *Group: 168 g = i 169 } 170 } 171 172 // concatenate long name with namespace 173 key := option.EnvDefaultKey 174 g = option.group 175 176 for g != nil { 177 if g.EnvNamespace != "" { 178 key = g.EnvNamespace + namespaceDelimiter + key 179 } 180 181 switch i := g.parent.(type) { 182 case *Command: 183 g = i.Group 184 case *Group: 185 g = i 186 case *Parser: 187 g = nil 188 } 189 } 190 191 return key 192} 193 194// String converts an option to a human friendly readable string describing the 195// option. 196func (option *Option) String() string { 197 var s string 198 var short string 199 200 if option.ShortName != 0 { 201 data := make([]byte, utf8.RuneLen(option.ShortName)) 202 utf8.EncodeRune(data, option.ShortName) 203 short = string(data) 204 205 if len(option.LongName) != 0 { 206 s = fmt.Sprintf("%s%s, %s%s", 207 string(defaultShortOptDelimiter), short, 208 defaultLongOptDelimiter, option.LongNameWithNamespace()) 209 } else { 210 s = fmt.Sprintf("%s%s", string(defaultShortOptDelimiter), short) 211 } 212 } else if len(option.LongName) != 0 { 213 s = fmt.Sprintf("%s%s", defaultLongOptDelimiter, option.LongNameWithNamespace()) 214 } 215 216 return s 217} 218 219// Value returns the option value as an interface{}. 220func (option *Option) Value() interface{} { 221 return option.value.Interface() 222} 223 224// Field returns the reflect struct field of the option. 225func (option *Option) Field() reflect.StructField { 226 return option.field 227} 228 229// IsSet returns true if option has been set 230func (option *Option) IsSet() bool { 231 return option.isSet 232} 233 234// IsSetDefault returns true if option has been set via the default option tag 235func (option *Option) IsSetDefault() bool { 236 return option.isSetDefault 237} 238 239// Set the value of an option to the specified value. An error will be returned 240// if the specified value could not be converted to the corresponding option 241// value type. 242func (option *Option) set(value *string) error { 243 kind := option.value.Type().Kind() 244 245 if (kind == reflect.Map || kind == reflect.Slice) && option.clearReferenceBeforeSet { 246 option.empty() 247 } 248 249 option.isSet = true 250 option.preventDefault = true 251 option.clearReferenceBeforeSet = false 252 253 if len(option.Choices) != 0 { 254 found := false 255 256 for _, choice := range option.Choices { 257 if choice == *value { 258 found = true 259 break 260 } 261 } 262 263 if !found { 264 allowed := strings.Join(option.Choices[0:len(option.Choices)-1], ", ") 265 266 if len(option.Choices) > 1 { 267 allowed += " or " + option.Choices[len(option.Choices)-1] 268 } 269 270 return newErrorf(ErrInvalidChoice, 271 "Invalid value `%s' for option `%s'. Allowed values are: %s", 272 *value, option, allowed) 273 } 274 } 275 276 if option.isFunc() { 277 return option.call(value) 278 } else if value != nil { 279 return convert(*value, option.value, option.tag) 280 } 281 282 return convert("", option.value, option.tag) 283} 284 285func (option *Option) setDefault(value *string) error { 286 if option.preventDefault { 287 return nil 288 } 289 290 if err := option.set(value); err != nil { 291 return err 292 } 293 294 option.isSetDefault = true 295 option.preventDefault = false 296 297 return nil 298} 299 300func (option *Option) showInHelp() bool { 301 return !option.Hidden && (option.ShortName != 0 || len(option.LongName) != 0) 302} 303 304func (option *Option) canArgument() bool { 305 if u := option.isUnmarshaler(); u != nil { 306 return true 307 } 308 309 return !option.isBool() 310} 311 312func (option *Option) emptyValue() reflect.Value { 313 tp := option.value.Type() 314 315 if tp.Kind() == reflect.Map { 316 return reflect.MakeMap(tp) 317 } 318 319 return reflect.Zero(tp) 320} 321 322func (option *Option) empty() { 323 if !option.isFunc() { 324 option.value.Set(option.emptyValue()) 325 } 326} 327 328func (option *Option) clearDefault() error { 329 if option.preventDefault { 330 return nil 331 } 332 333 usedDefault := option.Default 334 335 if envKey := option.EnvKeyWithNamespace(); envKey != "" { 336 if value, ok := os.LookupEnv(envKey); ok { 337 if option.EnvDefaultDelim != "" { 338 usedDefault = strings.Split(value, option.EnvDefaultDelim) 339 } else { 340 usedDefault = []string{value} 341 } 342 } 343 } 344 345 option.isSetDefault = true 346 347 if len(usedDefault) > 0 { 348 option.empty() 349 350 for _, d := range usedDefault { 351 err := option.setDefault(&d) 352 353 if err != nil { 354 return err 355 } 356 } 357 } else { 358 tp := option.value.Type() 359 360 switch tp.Kind() { 361 case reflect.Map: 362 if option.value.IsNil() { 363 option.empty() 364 } 365 case reflect.Slice: 366 if option.value.IsNil() { 367 option.empty() 368 } 369 } 370 } 371 372 return nil 373} 374 375func (option *Option) valueIsDefault() bool { 376 // Check if the value of the option corresponds to its 377 // default value 378 emptyval := option.emptyValue() 379 380 checkvalptr := reflect.New(emptyval.Type()) 381 checkval := reflect.Indirect(checkvalptr) 382 383 checkval.Set(emptyval) 384 385 if len(option.Default) != 0 { 386 for _, v := range option.Default { 387 convert(v, checkval, option.tag) 388 } 389 } 390 391 return reflect.DeepEqual(option.value.Interface(), checkval.Interface()) 392} 393 394func (option *Option) isUnmarshaler() Unmarshaler { 395 v := option.value 396 397 for { 398 if !v.CanInterface() { 399 break 400 } 401 402 i := v.Interface() 403 404 if u, ok := i.(Unmarshaler); ok { 405 return u 406 } 407 408 if !v.CanAddr() { 409 break 410 } 411 412 v = v.Addr() 413 } 414 415 return nil 416} 417 418func (option *Option) isValueValidator() ValueValidator { 419 v := option.value 420 421 for { 422 if !v.CanInterface() { 423 break 424 } 425 426 i := v.Interface() 427 428 if u, ok := i.(ValueValidator); ok { 429 return u 430 } 431 432 if !v.CanAddr() { 433 break 434 } 435 436 v = v.Addr() 437 } 438 439 return nil 440} 441 442func (option *Option) isBool() bool { 443 tp := option.value.Type() 444 445 for { 446 switch tp.Kind() { 447 case reflect.Slice, reflect.Ptr: 448 tp = tp.Elem() 449 case reflect.Bool: 450 return true 451 case reflect.Func: 452 return tp.NumIn() == 0 453 default: 454 return false 455 } 456 } 457} 458 459func (option *Option) isSignedNumber() bool { 460 tp := option.value.Type() 461 462 for { 463 switch tp.Kind() { 464 case reflect.Slice, reflect.Ptr: 465 tp = tp.Elem() 466 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Float32, reflect.Float64: 467 return true 468 default: 469 return false 470 } 471 } 472} 473 474func (option *Option) isFunc() bool { 475 return option.value.Type().Kind() == reflect.Func 476} 477 478func (option *Option) call(value *string) error { 479 var retval []reflect.Value 480 481 if value == nil { 482 retval = option.value.Call(nil) 483 } else { 484 tp := option.value.Type().In(0) 485 486 val := reflect.New(tp) 487 val = reflect.Indirect(val) 488 489 if err := convert(*value, val, option.tag); err != nil { 490 return err 491 } 492 493 retval = option.value.Call([]reflect.Value{val}) 494 } 495 496 if len(retval) == 1 && retval[0].Type() == reflect.TypeOf((*error)(nil)).Elem() { 497 if retval[0].Interface() == nil { 498 return nil 499 } 500 501 return retval[0].Interface().(error) 502 } 503 504 return nil 505} 506 507func (option *Option) updateDefaultLiteral() { 508 defs := option.Default 509 def := "" 510 511 if len(defs) == 0 && option.canArgument() { 512 var showdef bool 513 514 switch option.field.Type.Kind() { 515 case reflect.Func, reflect.Ptr: 516 showdef = !option.value.IsNil() 517 case reflect.Slice, reflect.String, reflect.Array: 518 showdef = option.value.Len() > 0 519 case reflect.Map: 520 showdef = !option.value.IsNil() && option.value.Len() > 0 521 default: 522 zeroval := reflect.Zero(option.field.Type) 523 showdef = !reflect.DeepEqual(zeroval.Interface(), option.value.Interface()) 524 } 525 526 if showdef { 527 def, _ = convertToString(option.value, option.tag) 528 } 529 } else if len(defs) != 0 { 530 l := len(defs) - 1 531 532 for i := 0; i < l; i++ { 533 def += quoteIfNeeded(defs[i]) + ", " 534 } 535 536 def += quoteIfNeeded(defs[l]) 537 } 538 539 option.defaultLiteral = def 540} 541 542func (option *Option) shortAndLongName() string { 543 ret := &bytes.Buffer{} 544 545 if option.ShortName != 0 { 546 ret.WriteRune(defaultShortOptDelimiter) 547 ret.WriteRune(option.ShortName) 548 } 549 550 if len(option.LongName) != 0 { 551 if option.ShortName != 0 { 552 ret.WriteRune('/') 553 } 554 555 ret.WriteString(option.LongName) 556 } 557 558 return ret.String() 559} 560 561func (option *Option) isValidValue(arg string) error { 562 if validator := option.isValueValidator(); validator != nil { 563 return validator.IsValidValue(arg) 564 } 565 if argumentIsOption(arg) && !(option.isSignedNumber() && len(arg) > 1 && arg[0] == '-' && arg[1] >= '0' && arg[1] <= '9') { 566 return fmt.Errorf("expected argument for flag `%s', but got option `%s'", option, arg) 567 } 568 return nil 569} 570