1package command 2 3import ( 4 "errors" 5 "flag" 6 "fmt" 7 "os" 8 "sort" 9 "strconv" 10 "strings" 11 "time" 12 13 "github.com/posener/complete" 14) 15 16// FlagExample is an interface which declares an example value. 17type FlagExample interface { 18 Example() string 19} 20 21// FlagVisibility is an interface which declares whether a flag should be 22// hidden from help and completions. This is usually used for deprecations 23// on "internal-only" flags. 24type FlagVisibility interface { 25 Hidden() bool 26} 27 28// FlagBool is an interface which boolean flags implement. 29type FlagBool interface { 30 IsBoolFlag() bool 31} 32 33// BoolPtr is a bool which is aware if it has been set. 34type BoolPtr struct { 35 v *bool 36} 37 38func (b *BoolPtr) Set(v string) error { 39 val, err := strconv.ParseBool(v) 40 if err != nil { 41 return err 42 } 43 44 if b.v == nil { 45 b.v = new(bool) 46 } 47 *b.v = val 48 49 return nil 50} 51 52func (b *BoolPtr) IsSet() bool { 53 return b.v != nil 54} 55 56func (b *BoolPtr) Get() bool { 57 if b.v == nil { 58 return false 59 } 60 return *b.v 61} 62 63func (b *BoolPtr) String() string { 64 var current bool 65 if b.v != nil { 66 current = *(b.v) 67 } 68 return fmt.Sprintf("%v", current) 69} 70 71type boolPtrValue struct { 72 hidden bool 73 target *BoolPtr 74} 75 76func newBoolPtrValue(def *bool, target *BoolPtr, hidden bool) *boolPtrValue { 77 val := &boolPtrValue{ 78 hidden: hidden, 79 target: target, 80 } 81 if def != nil { 82 _ = val.target.Set(strconv.FormatBool(*def)) 83 } 84 return val 85} 86 87func (b *boolPtrValue) IsBoolFlag() bool { 88 return true 89} 90 91func (b *boolPtrValue) Set(s string) error { 92 if b.target == nil { 93 b.target = new(BoolPtr) 94 } 95 return b.target.Set(s) 96} 97 98func (b *boolPtrValue) Get() interface{} { return *b.target } 99func (b *boolPtrValue) String() string { return b.target.String() } 100func (b *boolPtrValue) Example() string { return "*bool" } 101func (b *boolPtrValue) Hidden() bool { return b.hidden } 102 103type BoolPtrVar struct { 104 Name string 105 Aliases []string 106 Usage string 107 Hidden bool 108 EnvVar string 109 Default *bool 110 Target *BoolPtr 111 Completion complete.Predictor 112} 113 114func (f *FlagSet) BoolPtrVar(i *BoolPtrVar) { 115 def := i.Default 116 if v, exist := os.LookupEnv(i.EnvVar); exist { 117 if b, err := strconv.ParseBool(v); err == nil { 118 if def == nil { 119 def = new(bool) 120 } 121 *def = b 122 } 123 } 124 125 f.VarFlag(&VarFlag{ 126 Name: i.Name, 127 Aliases: i.Aliases, 128 Usage: i.Usage, 129 Value: newBoolPtrValue(i.Default, i.Target, i.Hidden), 130 Completion: i.Completion, 131 }) 132} 133 134// -- BoolVar and boolValue 135type BoolVar struct { 136 Name string 137 Aliases []string 138 Usage string 139 Default bool 140 Hidden bool 141 EnvVar string 142 Target *bool 143 Completion complete.Predictor 144} 145 146func (f *FlagSet) BoolVar(i *BoolVar) { 147 def := i.Default 148 if v, exist := os.LookupEnv(i.EnvVar); exist { 149 if b, err := strconv.ParseBool(v); err == nil { 150 def = b 151 } 152 } 153 154 f.VarFlag(&VarFlag{ 155 Name: i.Name, 156 Aliases: i.Aliases, 157 Usage: i.Usage, 158 Default: strconv.FormatBool(i.Default), 159 EnvVar: i.EnvVar, 160 Value: newBoolValue(def, i.Target, i.Hidden), 161 Completion: i.Completion, 162 }) 163} 164 165type boolValue struct { 166 hidden bool 167 target *bool 168} 169 170func newBoolValue(def bool, target *bool, hidden bool) *boolValue { 171 *target = def 172 173 return &boolValue{ 174 hidden: hidden, 175 target: target, 176 } 177} 178 179func (b *boolValue) Set(s string) error { 180 v, err := strconv.ParseBool(s) 181 if err != nil { 182 return err 183 } 184 185 *b.target = v 186 return nil 187} 188 189func (b *boolValue) Get() interface{} { return *b.target } 190func (b *boolValue) String() string { return strconv.FormatBool(*b.target) } 191func (b *boolValue) Example() string { return "" } 192func (b *boolValue) Hidden() bool { return b.hidden } 193func (b *boolValue) IsBoolFlag() bool { return true } 194 195// -- IntVar and intValue 196type IntVar struct { 197 Name string 198 Aliases []string 199 Usage string 200 Default int 201 Hidden bool 202 EnvVar string 203 Target *int 204 Completion complete.Predictor 205} 206 207func (f *FlagSet) IntVar(i *IntVar) { 208 initial := i.Default 209 if v, exist := os.LookupEnv(i.EnvVar); exist { 210 if i, err := strconv.ParseInt(v, 0, 64); err == nil { 211 initial = int(i) 212 } 213 } 214 215 def := "" 216 if i.Default != 0 { 217 def = strconv.FormatInt(int64(i.Default), 10) 218 } 219 220 f.VarFlag(&VarFlag{ 221 Name: i.Name, 222 Aliases: i.Aliases, 223 Usage: i.Usage, 224 Default: def, 225 EnvVar: i.EnvVar, 226 Value: newIntValue(initial, i.Target, i.Hidden), 227 Completion: i.Completion, 228 }) 229} 230 231type intValue struct { 232 hidden bool 233 target *int 234} 235 236func newIntValue(def int, target *int, hidden bool) *intValue { 237 *target = def 238 return &intValue{ 239 hidden: hidden, 240 target: target, 241 } 242} 243 244func (i *intValue) Set(s string) error { 245 v, err := strconv.ParseInt(s, 0, 64) 246 if err != nil { 247 return err 248 } 249 250 *i.target = int(v) 251 return nil 252} 253 254func (i *intValue) Get() interface{} { return int(*i.target) } 255func (i *intValue) String() string { return strconv.Itoa(int(*i.target)) } 256func (i *intValue) Example() string { return "int" } 257func (i *intValue) Hidden() bool { return i.hidden } 258 259// -- Int64Var and int64Value 260type Int64Var struct { 261 Name string 262 Aliases []string 263 Usage string 264 Default int64 265 Hidden bool 266 EnvVar string 267 Target *int64 268 Completion complete.Predictor 269} 270 271func (f *FlagSet) Int64Var(i *Int64Var) { 272 initial := i.Default 273 if v, exist := os.LookupEnv(i.EnvVar); exist { 274 if i, err := strconv.ParseInt(v, 0, 64); err == nil { 275 initial = i 276 } 277 } 278 279 def := "" 280 if i.Default != 0 { 281 def = strconv.FormatInt(int64(i.Default), 10) 282 } 283 284 f.VarFlag(&VarFlag{ 285 Name: i.Name, 286 Aliases: i.Aliases, 287 Usage: i.Usage, 288 Default: def, 289 EnvVar: i.EnvVar, 290 Value: newInt64Value(initial, i.Target, i.Hidden), 291 Completion: i.Completion, 292 }) 293} 294 295type int64Value struct { 296 hidden bool 297 target *int64 298} 299 300func newInt64Value(def int64, target *int64, hidden bool) *int64Value { 301 *target = def 302 return &int64Value{ 303 hidden: hidden, 304 target: target, 305 } 306} 307 308func (i *int64Value) Set(s string) error { 309 v, err := strconv.ParseInt(s, 0, 64) 310 if err != nil { 311 return err 312 } 313 314 *i.target = v 315 return nil 316} 317 318func (i *int64Value) Get() interface{} { return int64(*i.target) } 319func (i *int64Value) String() string { return strconv.FormatInt(int64(*i.target), 10) } 320func (i *int64Value) Example() string { return "int" } 321func (i *int64Value) Hidden() bool { return i.hidden } 322 323// -- UintVar && uintValue 324type UintVar struct { 325 Name string 326 Aliases []string 327 Usage string 328 Default uint 329 Hidden bool 330 EnvVar string 331 Target *uint 332 Completion complete.Predictor 333} 334 335func (f *FlagSet) UintVar(i *UintVar) { 336 initial := i.Default 337 if v, exist := os.LookupEnv(i.EnvVar); exist { 338 if i, err := strconv.ParseUint(v, 0, 64); err == nil { 339 initial = uint(i) 340 } 341 } 342 343 def := "" 344 if i.Default != 0 { 345 def = strconv.FormatUint(uint64(i.Default), 10) 346 } 347 348 f.VarFlag(&VarFlag{ 349 Name: i.Name, 350 Aliases: i.Aliases, 351 Usage: i.Usage, 352 Default: def, 353 EnvVar: i.EnvVar, 354 Value: newUintValue(initial, i.Target, i.Hidden), 355 Completion: i.Completion, 356 }) 357} 358 359type uintValue struct { 360 hidden bool 361 target *uint 362} 363 364func newUintValue(def uint, target *uint, hidden bool) *uintValue { 365 *target = def 366 return &uintValue{ 367 hidden: hidden, 368 target: target, 369 } 370} 371 372func (i *uintValue) Set(s string) error { 373 v, err := strconv.ParseUint(s, 0, 64) 374 if err != nil { 375 return err 376 } 377 378 *i.target = uint(v) 379 return nil 380} 381 382func (i *uintValue) Get() interface{} { return uint(*i.target) } 383func (i *uintValue) String() string { return strconv.FormatUint(uint64(*i.target), 10) } 384func (i *uintValue) Example() string { return "uint" } 385func (i *uintValue) Hidden() bool { return i.hidden } 386 387// -- Uint64Var and uint64Value 388type Uint64Var struct { 389 Name string 390 Aliases []string 391 Usage string 392 Default uint64 393 Hidden bool 394 EnvVar string 395 Target *uint64 396 Completion complete.Predictor 397} 398 399func (f *FlagSet) Uint64Var(i *Uint64Var) { 400 initial := i.Default 401 if v, exist := os.LookupEnv(i.EnvVar); exist { 402 if i, err := strconv.ParseUint(v, 0, 64); err == nil { 403 initial = i 404 } 405 } 406 407 def := "" 408 if i.Default != 0 { 409 strconv.FormatUint(i.Default, 10) 410 } 411 412 f.VarFlag(&VarFlag{ 413 Name: i.Name, 414 Aliases: i.Aliases, 415 Usage: i.Usage, 416 Default: def, 417 EnvVar: i.EnvVar, 418 Value: newUint64Value(initial, i.Target, i.Hidden), 419 Completion: i.Completion, 420 }) 421} 422 423type uint64Value struct { 424 hidden bool 425 target *uint64 426} 427 428func newUint64Value(def uint64, target *uint64, hidden bool) *uint64Value { 429 *target = def 430 return &uint64Value{ 431 hidden: hidden, 432 target: target, 433 } 434} 435 436func (i *uint64Value) Set(s string) error { 437 v, err := strconv.ParseUint(s, 0, 64) 438 if err != nil { 439 return err 440 } 441 442 *i.target = v 443 return nil 444} 445 446func (i *uint64Value) Get() interface{} { return uint64(*i.target) } 447func (i *uint64Value) String() string { return strconv.FormatUint(uint64(*i.target), 10) } 448func (i *uint64Value) Example() string { return "uint" } 449func (i *uint64Value) Hidden() bool { return i.hidden } 450 451// -- StringVar and stringValue 452type StringVar struct { 453 Name string 454 Aliases []string 455 Usage string 456 Default string 457 Hidden bool 458 EnvVar string 459 Target *string 460 Completion complete.Predictor 461} 462 463func (f *FlagSet) StringVar(i *StringVar) { 464 initial := i.Default 465 if v, exist := os.LookupEnv(i.EnvVar); exist { 466 initial = v 467 } 468 469 def := "" 470 if i.Default != "" { 471 def = i.Default 472 } 473 474 f.VarFlag(&VarFlag{ 475 Name: i.Name, 476 Aliases: i.Aliases, 477 Usage: i.Usage, 478 Default: def, 479 EnvVar: i.EnvVar, 480 Value: newStringValue(initial, i.Target, i.Hidden), 481 Completion: i.Completion, 482 }) 483} 484 485type stringValue struct { 486 hidden bool 487 target *string 488} 489 490func newStringValue(def string, target *string, hidden bool) *stringValue { 491 *target = def 492 return &stringValue{ 493 hidden: hidden, 494 target: target, 495 } 496} 497 498func (s *stringValue) Set(val string) error { 499 *s.target = val 500 return nil 501} 502 503func (s *stringValue) Get() interface{} { return *s.target } 504func (s *stringValue) String() string { return *s.target } 505func (s *stringValue) Example() string { return "string" } 506func (s *stringValue) Hidden() bool { return s.hidden } 507 508// -- Float64Var and float64Value 509type Float64Var struct { 510 Name string 511 Aliases []string 512 Usage string 513 Default float64 514 Hidden bool 515 EnvVar string 516 Target *float64 517 Completion complete.Predictor 518} 519 520func (f *FlagSet) Float64Var(i *Float64Var) { 521 initial := i.Default 522 if v, exist := os.LookupEnv(i.EnvVar); exist { 523 if i, err := strconv.ParseFloat(v, 64); err == nil { 524 initial = i 525 } 526 } 527 528 def := "" 529 if i.Default != 0 { 530 def = strconv.FormatFloat(i.Default, 'e', -1, 64) 531 } 532 533 f.VarFlag(&VarFlag{ 534 Name: i.Name, 535 Aliases: i.Aliases, 536 Usage: i.Usage, 537 Default: def, 538 EnvVar: i.EnvVar, 539 Value: newFloat64Value(initial, i.Target, i.Hidden), 540 Completion: i.Completion, 541 }) 542} 543 544type float64Value struct { 545 hidden bool 546 target *float64 547} 548 549func newFloat64Value(def float64, target *float64, hidden bool) *float64Value { 550 *target = def 551 return &float64Value{ 552 hidden: hidden, 553 target: target, 554 } 555} 556 557func (f *float64Value) Set(s string) error { 558 v, err := strconv.ParseFloat(s, 64) 559 if err != nil { 560 return err 561 } 562 563 *f.target = v 564 return nil 565} 566 567func (f *float64Value) Get() interface{} { return float64(*f.target) } 568func (f *float64Value) String() string { return strconv.FormatFloat(float64(*f.target), 'g', -1, 64) } 569func (f *float64Value) Example() string { return "float" } 570func (f *float64Value) Hidden() bool { return f.hidden } 571 572// -- DurationVar and durationValue 573type DurationVar struct { 574 Name string 575 Aliases []string 576 Usage string 577 Default time.Duration 578 Hidden bool 579 EnvVar string 580 Target *time.Duration 581 Completion complete.Predictor 582} 583 584func (f *FlagSet) DurationVar(i *DurationVar) { 585 initial := i.Default 586 if v, exist := os.LookupEnv(i.EnvVar); exist { 587 if d, err := time.ParseDuration(appendDurationSuffix(v)); err == nil { 588 initial = d 589 } 590 } 591 592 def := "" 593 if i.Default != 0 { 594 def = i.Default.String() 595 } 596 597 f.VarFlag(&VarFlag{ 598 Name: i.Name, 599 Aliases: i.Aliases, 600 Usage: i.Usage, 601 Default: def, 602 EnvVar: i.EnvVar, 603 Value: newDurationValue(initial, i.Target, i.Hidden), 604 Completion: i.Completion, 605 }) 606} 607 608type durationValue struct { 609 hidden bool 610 target *time.Duration 611} 612 613func newDurationValue(def time.Duration, target *time.Duration, hidden bool) *durationValue { 614 *target = def 615 return &durationValue{ 616 hidden: hidden, 617 target: target, 618 } 619} 620 621func (d *durationValue) Set(s string) error { 622 // Maintain bc for people specifying "system" as the value. 623 if s == "system" { 624 s = "-1" 625 } 626 627 v, err := time.ParseDuration(appendDurationSuffix(s)) 628 if err != nil { 629 return err 630 } 631 *d.target = v 632 return nil 633} 634 635func (d *durationValue) Get() interface{} { return time.Duration(*d.target) } 636func (d *durationValue) String() string { return (*d.target).String() } 637func (d *durationValue) Example() string { return "duration" } 638func (d *durationValue) Hidden() bool { return d.hidden } 639 640// appendDurationSuffix is used as a backwards-compat tool for assuming users 641// meant "seconds" when they do not provide a suffixed duration value. 642func appendDurationSuffix(s string) string { 643 if strings.HasSuffix(s, "s") || strings.HasSuffix(s, "m") || strings.HasSuffix(s, "h") { 644 return s 645 } 646 return s + "s" 647} 648 649// -- StringSliceVar and stringSliceValue 650type StringSliceVar struct { 651 Name string 652 Aliases []string 653 Usage string 654 Default []string 655 Hidden bool 656 EnvVar string 657 Target *[]string 658 Completion complete.Predictor 659} 660 661func (f *FlagSet) StringSliceVar(i *StringSliceVar) { 662 initial := i.Default 663 if v, exist := os.LookupEnv(i.EnvVar); exist { 664 parts := strings.Split(v, ",") 665 for i := range parts { 666 parts[i] = strings.TrimSpace(parts[i]) 667 } 668 initial = parts 669 } 670 671 def := "" 672 if i.Default != nil { 673 def = strings.Join(i.Default, ",") 674 } 675 676 f.VarFlag(&VarFlag{ 677 Name: i.Name, 678 Aliases: i.Aliases, 679 Usage: i.Usage, 680 Default: def, 681 EnvVar: i.EnvVar, 682 Value: newStringSliceValue(initial, i.Target, i.Hidden), 683 Completion: i.Completion, 684 }) 685} 686 687type stringSliceValue struct { 688 hidden bool 689 target *[]string 690} 691 692func newStringSliceValue(def []string, target *[]string, hidden bool) *stringSliceValue { 693 *target = def 694 return &stringSliceValue{ 695 hidden: hidden, 696 target: target, 697 } 698} 699 700func (s *stringSliceValue) Set(val string) error { 701 *s.target = append(*s.target, strings.TrimSpace(val)) 702 return nil 703} 704 705func (s *stringSliceValue) Get() interface{} { return *s.target } 706func (s *stringSliceValue) String() string { return strings.Join(*s.target, ",") } 707func (s *stringSliceValue) Example() string { return "string" } 708func (s *stringSliceValue) Hidden() bool { return s.hidden } 709 710// -- StringMapVar and stringMapValue 711type StringMapVar struct { 712 Name string 713 Aliases []string 714 Usage string 715 Default map[string]string 716 Hidden bool 717 Target *map[string]string 718 Completion complete.Predictor 719} 720 721func (f *FlagSet) StringMapVar(i *StringMapVar) { 722 def := "" 723 if i.Default != nil { 724 def = mapToKV(i.Default) 725 } 726 727 f.VarFlag(&VarFlag{ 728 Name: i.Name, 729 Aliases: i.Aliases, 730 Usage: i.Usage, 731 Default: def, 732 Value: newStringMapValue(i.Default, i.Target, i.Hidden), 733 Completion: i.Completion, 734 }) 735} 736 737type stringMapValue struct { 738 hidden bool 739 target *map[string]string 740} 741 742func newStringMapValue(def map[string]string, target *map[string]string, hidden bool) *stringMapValue { 743 *target = def 744 return &stringMapValue{ 745 hidden: hidden, 746 target: target, 747 } 748} 749 750func (s *stringMapValue) Set(val string) error { 751 idx := strings.Index(val, "=") 752 if idx == -1 { 753 return fmt.Errorf("missing = in KV pair: %q", val) 754 } 755 756 if *s.target == nil { 757 *s.target = make(map[string]string) 758 } 759 760 k, v := val[0:idx], val[idx+1:] 761 (*s.target)[k] = v 762 return nil 763} 764 765func (s *stringMapValue) Get() interface{} { return *s.target } 766func (s *stringMapValue) String() string { return mapToKV(*s.target) } 767func (s *stringMapValue) Example() string { return "key=value" } 768func (s *stringMapValue) Hidden() bool { return s.hidden } 769 770func mapToKV(m map[string]string) string { 771 list := make([]string, 0, len(m)) 772 for k := range m { 773 list = append(list, k) 774 } 775 sort.Strings(list) 776 777 for i, k := range list { 778 list[i] = k + "=" + m[k] 779 } 780 781 return strings.Join(list, ",") 782} 783 784// -- VarFlag 785type VarFlag struct { 786 Name string 787 Aliases []string 788 Usage string 789 Default string 790 EnvVar string 791 Value flag.Value 792 Completion complete.Predictor 793} 794 795func (f *FlagSet) VarFlag(i *VarFlag) { 796 // If the flag is marked as hidden, just add it to the set and return to 797 // avoid unnecessary computations here. We do not want to add completions or 798 // generate help output for hidden flags. 799 if v, ok := i.Value.(FlagVisibility); ok && v.Hidden() { 800 f.Var(i.Value, i.Name, "") 801 return 802 } 803 804 // Calculate the full usage 805 usage := i.Usage 806 807 if len(i.Aliases) > 0 { 808 sentence := make([]string, len(i.Aliases)) 809 for i, a := range i.Aliases { 810 sentence[i] = fmt.Sprintf(`"-%s"`, a) 811 } 812 813 aliases := "" 814 switch len(sentence) { 815 case 0: 816 // impossible... 817 case 1: 818 aliases = sentence[0] 819 case 2: 820 aliases = sentence[0] + " and " + sentence[1] 821 default: 822 sentence[len(sentence)-1] = "and " + sentence[len(sentence)-1] 823 aliases = strings.Join(sentence, ", ") 824 } 825 826 usage += fmt.Sprintf(" This is aliased as %s.", aliases) 827 } 828 829 if i.Default != "" { 830 usage += fmt.Sprintf(" The default is %s.", i.Default) 831 } 832 833 if i.EnvVar != "" { 834 usage += fmt.Sprintf(" This can also be specified via the %s "+ 835 "environment variable.", i.EnvVar) 836 } 837 838 // Add aliases to the main set 839 for _, a := range i.Aliases { 840 f.mainSet.Var(i.Value, a, "") 841 } 842 843 f.Var(i.Value, i.Name, usage) 844 f.completions["-"+i.Name] = i.Completion 845} 846 847// Var is a lower-level API for adding something to the flags. It should be used 848// with caution, since it bypasses all validation. Consider VarFlag instead. 849func (f *FlagSet) Var(value flag.Value, name, usage string) { 850 f.mainSet.Var(value, name, usage) 851 f.flagSet.Var(value, name, usage) 852} 853 854// -- TimeVar and timeValue 855type TimeVar struct { 856 Name string 857 Aliases []string 858 Usage string 859 Default time.Time 860 Hidden bool 861 EnvVar string 862 Target *time.Time 863 Completion complete.Predictor 864 Formats TimeFormat 865} 866 867// Identify the allowable formats, identified by the minimum 868// precision accepted. 869// TODO: move this somewhere where it can be re-used for the API. 870type TimeFormat int 871 872const ( 873 TimeVar_EpochSecond TimeFormat = 1 << iota 874 TimeVar_RFC3339Nano 875 TimeVar_RFC3339Second 876 TimeVar_Day 877 TimeVar_Month 878) 879 880// Default value to use 881const TimeVar_TimeOrDay TimeFormat = TimeVar_EpochSecond | TimeVar_RFC3339Nano | TimeVar_RFC3339Second | TimeVar_Day 882 883// parseTimeAlternatives attempts several different allowable variants 884// of the time field. 885func parseTimeAlternatives(input string, allowedFormats TimeFormat) (time.Time, error) { 886 // The RFC3339 formats require the inclusion of a time zone. 887 if allowedFormats&TimeVar_RFC3339Nano != 0 { 888 t, err := time.Parse(time.RFC3339Nano, input) 889 if err == nil { 890 return t, err 891 } 892 } 893 894 if allowedFormats&TimeVar_RFC3339Second != 0 { 895 t, err := time.Parse(time.RFC3339, input) 896 if err == nil { 897 return t, err 898 } 899 } 900 901 if allowedFormats&TimeVar_Day != 0 { 902 t, err := time.Parse("2006-01-02", input) 903 if err == nil { 904 return t, err 905 } 906 } 907 908 if allowedFormats&TimeVar_Month != 0 { 909 t, err := time.Parse("2006-01", input) 910 if err == nil { 911 return t, err 912 } 913 } 914 915 if allowedFormats&TimeVar_EpochSecond != 0 { 916 i, err := strconv.ParseInt(input, 10, 64) 917 if err == nil { 918 // If a customer enters 20200101 we don't want 919 // to parse that as an epoch time. 920 // This arbitrarily-chosen cutoff is around year 2000. 921 if i > 946000000 { 922 return time.Unix(i, 0), nil 923 } 924 } 925 } 926 927 return time.Time{}, errors.New("Could not parse as absolute time.") 928} 929 930func (f *FlagSet) TimeVar(i *TimeVar) { 931 initial := i.Default 932 if v, exist := os.LookupEnv(i.EnvVar); exist { 933 if d, err := parseTimeAlternatives(v, i.Formats); err == nil { 934 initial = d 935 } 936 } 937 938 def := "" 939 if !i.Default.IsZero() { 940 def = i.Default.String() 941 } 942 943 f.VarFlag(&VarFlag{ 944 Name: i.Name, 945 Aliases: i.Aliases, 946 Usage: i.Usage, 947 Default: def, 948 EnvVar: i.EnvVar, 949 Value: newTimeValue(initial, i.Target, i.Hidden, i.Formats), 950 Completion: i.Completion, 951 }) 952} 953 954type timeValue struct { 955 hidden bool 956 target *time.Time 957 formats TimeFormat 958} 959 960func newTimeValue(def time.Time, target *time.Time, hidden bool, f TimeFormat) *timeValue { 961 *target = def 962 return &timeValue{ 963 hidden: hidden, 964 target: target, 965 formats: f, 966 } 967} 968 969func (d *timeValue) Set(s string) error { 970 v, err := parseTimeAlternatives(s, d.formats) 971 if err != nil { 972 return err 973 } 974 *d.target = v 975 return nil 976} 977 978func (d *timeValue) Get() interface{} { return *d.target } 979func (d *timeValue) String() string { return (*d.target).String() } 980func (d *timeValue) Example() string { return "time" } 981func (d *timeValue) Hidden() bool { return d.hidden } 982 983// -- helpers 984func envDefault(key, def string) string { 985 if v, exist := os.LookupEnv(key); exist { 986 return v 987 } 988 return def 989} 990 991func envBoolDefault(key string, def bool) bool { 992 if v, exist := os.LookupEnv(key); exist { 993 b, err := strconv.ParseBool(v) 994 if err != nil { 995 panic(err) 996 } 997 return b 998 } 999 return def 1000} 1001 1002func envDurationDefault(key string, def time.Duration) time.Duration { 1003 if v, exist := os.LookupEnv(key); exist { 1004 d, err := time.ParseDuration(v) 1005 if err != nil { 1006 panic(err) 1007 } 1008 return d 1009 } 1010 return def 1011} 1012