-
Notifications
You must be signed in to change notification settings - Fork 3
/
bind.go
732 lines (666 loc) · 21.9 KB
/
bind.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
// Copyright (c) 2020 Adam S Levy
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
// Package flagbind makes defining flags as simple as defining a struct type.
//
// flagbind.Bind parses the exported fields of a struct and binds them to a
// FlagSet, which can be the standard flag package or the popular pflag package
// github.com/spf13/pflag.
//
// By coupling the flag definitions with a type definition, the use of globals
// is discouraged, and flag types may be coupled with related behavior allowing
// for better organized and documented code.
//
// Flag names, usage, defaults and other options can be set using struct tags
// on exported fields. Using struct nesting, flags can be composed and assigned
// a flag name prefix. Exposing the settings of another package as flags is as
// simple as embedding the relevant types.
//
// Alternatively, any type may implement the Binder interface, which allows for
// more direct control over the FlagSet, much like json.Unmarshal passes
// control to types that implement json.Unmarshaler. Also similar to
// json.Unmarshal, Bind will initialize any fields that are nil, and leave any
// fields that are already populated, as defaults.
//
// See Bind documentation for the full details on controling flags.
//
// Bind works seamlessly with both the standard library flag package and the
// popular github.com/spf13/pflag package.
//
// If pflag is used, Bind adapts a flag.Value to a pflag.Value. The underlying
// type name of the flag.Value is used as the return value of the additional
// `Type() string` function required by the pflag.Value interface.
//
//
// Getting Started
//
// Start by declaring a struct type for your flags.
//
// var flags := struct {
// StringFlag string `flag:"flag-name;default value;Usage for string-flag"`
// Int int `flag:"integer;5"`
//
// // Flag names default to `auto-kebab-case`
// AutoKebabCase int
//
// // If pflag is used, -s is be used as the shorthand flag name,
// // otherwise it is ignored for use with the standard flag package.
// ShortName bool `flag:"short,s"`
//
// // Optionally extende the usage tag with subsequent `use` tags
// // on _ fields.
// URL string `flag:"url,u;http://www.example.com/;Start usage here"
// _ struct{} `use:"continue longer usage string for --url below it",
//
// // Nested and Embedded structs can add a flag name prefix, or not.
// Nested StructA
// NestedFlat StructB `flag:";;;flatten"`
// StructA // Flat by default
// StructB `flag:"embedded"` // Add prefix to nested field flag names.
//
// // Ignored
// ExplicitlyIgnored bool `flag:"-"`
// unexported bool
// }{
// // Default values may also be set directly to override the tag.
// StringFlag: "override tag default",
// }
//
// fs := pflag.NewFlagSet("", pflag.ContinueOnError)
// flagbind.Bind(fs, &flags)
// fs.Parse([]string{"--auto-kebab-case"})
//
package flagbind
import (
"encoding/json"
"flag"
"fmt"
"net"
"net/url"
"reflect"
"strings"
"time"
"github.com/spf13/pflag"
)
// Separator is used to separate a prefix from a flag name and as the separator
// passed to FromCamelCase.
var Separator = "-"
// Binder binds itself to a FlagSet.
//
// FlagBind should prepend the `prefix` to any flag names when adding flags to
// `fs` to avoid potential flag name conflicts and allow more portable
// implementations.
//
// Additionally the opt should be passed down to Bind to preserve original opts
// if called again by the implementation.
//
// The underlying type of `fs` is the same as the original FlagSet passed to
// Bind.
type Binder interface {
FlagBind(fs FlagSet, prefix string, opt Option) error
}
// Bind the exported fields of struct `v` to new flags in the FlagSet `fs`.
//
// Bind returns ErrorInvalidFlagSet if `fs` does not implement STDFlagSet or
// PFlagSet.
//
// Bind returns ErrorInvalidType if `v` is not a pointer to a struct.
//
// Bind recovers from FlagSet panics and instead returns the panic as an error
// if a duplicate flag name occurs.
//
// For each exported field of `v` Bind attempts to define one or more
// corresponding flags in `fs` according to the following rules.
//
// If the field is a nil pointer, it is initialized.
//
// If the field implements Binder, then only FlagBind is called on the field.
//
// If the field implements flag.Value and not Binder, then it is bound as a
// Value on the FlagSet.
//
// Otherwise, if the field is a struct, or struct pointer, then Bind is
// recursively called on a pointer to the struct field.
//
// If the field is any supported type, a new flag is defined in `fs` with the
// settings defined in the field's `flag:"..."` tag. If the field is non-zero,
// its value is used as the default for that flag instead of whatever is
// defined in the `flag:";<default>"` tag. See FlagTag Settings below.
//
// For a complete list of supported types see STDFlagSet and PFlagSet.
// Additionally, a json.RawMessage is also natively supported and is bound as a
// JSONRawMessage flag.
//
//
// Ignoring a Field
//
// Use the tag `flag:"-"` to prevent an exported field from being bound to any
// flag. If the field is a nested or embedded struct then its fields are also
// ignored.
//
//
// Flag Tag Settings
//
// The flag settings for a particular field can be customized using a struct
// field tag of the form:
//
// `flag:"[<long>][,<short>][;<default>[;<usage>[;<options>]]]"`
//
// The tag and all of its settings are [optional]. Semi-colons are used to
// distinguish subsequent settings.
//
//
// <long>[,<short>] - Explicitly set the long and short names of the flag. All
// leading dashes are trimmed from both names. The two names are sorted for
// length, and the short name must be a single character, else it is ignored.
//
// If `fs` does not implement PFlagSet, then the short name is ignored if a
// long name is defined, otherwise the short name is used as the long name.
//
// If `fs` does implement PFlagSet, and only a short flag is defined, the long
// name defaults to the field name in kebab-case.
//
// If no name is set, the long name defaults to the field name in "kebab-case".
// For example, "ThisFieldName" becomes "this-field-name". See FromCamelCase
// and Separator.
//
// If the field is a nested or embedded struct and the "flatten" option is not
// set (see below), then the name is used as a prefix for all nested field flag
// names.
//
//
// <default> - Bind attempts to parse <default> as the field's default, just
// like it would be parsed as a flag. Non-zero field values override this as
// the default.
//
//
// <usage> - The usage string for the flag. See Extended Usage below for a way
// to break longer usage strings across multiple lines.
//
//
// <options> - A comma separated list of additional options for the flag.
//
// hide-default - Do not print the default value of this flag in the usage
// output.
//
// hidden - (PFlagSet only) Do not show this flag in the usage output.
//
// flatten - (Nested/embedded structs only) Do not prefix the name of the
// struct to the names of its fields. This overrides any explicit name on
// an embedded struct which would otherwise unflatten it.
//
//
// Extended Usage
//
// Usage lines can frequently be longer than what comfortably fits in a flag
// tag on a single line. To keep line lengths shorter, use any number of blank
// identifier fields of any type with a `use` field tag to extend the usage of
// a flag. Each `use` tag is joined with a single space.
//
// type Flags struct {
// URL string `flag:"url;;Usage starts here"`
// _ struct{} `use:"and continues here"`
// _ struct{} `use:"and ends here."`
// }
//
//
// Auto-Adapt flag.Value To pflag.Value
//
// The pflag.Value interface is the flag.Value interface, but with an
// additional Type() string function. This means that flag.Value cannot be used
// directly as a pflag.Value.
//
// In order to work around this when fs implements PFlagSet, Bind wraps any
// fields that implement flag.Value but not pflag.Value in a shim adapter that
// uses the underlying type name as the Type() string. This allows you to only
// need to implement flag.Value. If the field does implement pflag.Value, it is
// used directly.
//
//
// Nested/Embedded Structs Flag Prefix
//
// If the field is a nested or embedded struct, its fields are also recursively
// parsed.
//
// In order to help avoid flag name collisions, child flag names may be
// prepended with a prefix. The prefix defaults to the parent's field name
// passed through FromCamelCase, with a trailing Separator.
//
// The prefix may be set explicitly using the <name> on the parent's Flag Tag.
//
// To allow for a distinct separator symbol to be used just for a prefix, an
// explicitly set prefix that ends in "-", "_", or "." will not have Separator
// appended.
//
// By default, flags in nested structs always have a prefix, but this can be
// omitted with Flag Tag `flatten` <option>.
//
// By default, flags in embedded structs do not given a prefix, but one can be
// added by setting an explicit Flag Tag <name>.
//
//
// Overriding Flag Settings
//
// It is not always possible to set a Flag Tag on the fields of a nested struct
// type, such as when the type is from an external package. To allow for
// setting the default value, usage, or other options, an Overriding Flag Tag
// may be specified on a blank identifier field (`_`) that occurs anywhere
// after the field that defined the overridden flag.
//
// The name specified in the Overriding Flag Tag must exactly match the flag
// name of the overridden flag, including any prefixes that were prepended due
// to nesting. Bind returns ErrorFlagOverrideUndefined if the flag name cannot
// be found.
//
// Extended Usage may also defined immediately after an Overriding Flag Tag
// field.
//
// For example, this sets the default value and usage on the flag for Timeout
// on an embedded http.Client.
//
// type Flags struct {
// http.Client // Defines the -timeout flag
// _ struct{} `flag:"timeout;5s;HTTP request timeout"`
// _ struct{} `use:"... continued usage"`
// }
func Bind(fs FlagSet, v interface{}, opts ...Option) error {
return newBind(opts...).bind(fs, v)
}
func (b bind) bind(fs FlagSet, v interface{}) (err error) {
// Hand control over to the Binder implementation.
if binder, ok := v.(Binder); ok {
return binder.FlagBind(fs, b.Prefix, b.Option())
}
// Ensure we have a non-nil pointer.
ptr := reflect.ValueOf(v)
if ptr.Kind() != reflect.Ptr {
return ErrorInvalidType{v, false}
}
if ptr.IsNil() {
return ErrorInvalidType{v, true}
}
// We must operate on the addressable value, not the pointer.
val := reflect.Indirect(ptr)
// We can only inspect structs.
if val.Kind() != reflect.Struct {
return ErrorInvalidType{v, false}
}
// The flag and pflag packages panic when a flag with a duplicate name
// is defined. This works well for identifying the offending line of
// code where the flag name is redefined, but that is just noise to
// users of this package. The only useful information from such a panic
// is the duplicate flagname included in the panic message.
defer func() {
if r := recover(); r != nil {
// Clean up the inconsistent leading space that pflag
// leaves behind if no FlagSet name was set.
r = strings.TrimSpace(fmt.Sprintf("%v", r))
err = fmt.Errorf("%v", r)
}
}()
_, usePFlag := fs.(PFlagSet)
valT := val.Type()
defaults := make(map[string]string)
// loop through all fields
for i := 0; i < val.NumField(); i++ {
structField := valT.Field(i)
// Special flag metadata may be set using the blank identifier.
isMetadata := structField.Name == "_"
// See reflect.StructField for details.
isExported := structField.PkgPath == ""
// Ignore unexported, non-metadata fields.
if !isExported && !isMetadata {
continue
}
// Parse the flagTag.
tagStr, hasTag := structField.Tag.Lookup("flag")
tag := newFlagTag(tagStr)
if tag.IsIgnored {
continue
}
// Auto populate name if it has no explicit name, or only has a
// short name.
if !tag.HasExplicitName ||
(usePFlag && tag.Name == tag.ShortName) {
tag.Name = FromCamelCase(structField.Name, Separator)
}
fieldV := val.Field(i)
i = loadExtendedUsage(i, valT, &tag)
// Update Flag with Metadata tag.
if isMetadata {
if hasTag {
if err := overrideFlag(fs, tag); err != nil {
return err
}
}
continue
}
// Ensure we are dealing with a pointer.
if structField.Type.Kind() != reflect.Ptr {
fieldV = fieldV.Addr()
}
// Obtain the underlying type of the field.
fieldT := fieldV.Type().Elem()
// Allocate the field pointer if nil.
if fieldV.IsNil() {
fieldV.Set(reflect.New(fieldT))
}
fieldI := fieldV.Interface()
_, isBinder := fieldI.(Binder)
_, isFlagValue := fieldI.(flag.Value)
_, isJSONRawMessage := fieldI.(*json.RawMessage)
_, isURL := fieldI.(*url.URL)
_, isMarshaler := fieldI.(textBidiMarshaler)
noDive := isFlagValue || isJSONRawMessage || isURL || isMarshaler
isStruct := fieldT.Kind() == reflect.Struct
// If the field implements Binder, we call Bind on the field,
// which will call its Binder implementation.
//
// If the field is a struct, and does not implement flag.Value,
// we will recursively call BindWithPrefix.
//
// Otherwise, if the field implements flag.Value or any other
// type supported, we will bind the field directly below.
if isBinder || (!noDive && isStruct) {
// Set prefix up to this point.
b := b
// If the nested field is not explicitly flattened AND
// ( auto flattening is disabled OR the field is not
// anonymous (embedded) OR has an explicit name ),
// then grow the prefix.
if !tag.Flatten &&
(b.NoAutoFlatten ||
!structField.Anonymous || tag.HasExplicitName) {
b.Prefix += tag.Name
}
b.Prefix = appendSeparator(b.Prefix)
if err := b.bind(fs, fieldI); err != nil {
return newErrorNestedStruct(structField.Name, err)
}
continue
}
tag.Name = fmt.Sprintf("%v%v", b.Prefix, tag.Name)
newFlag, err := bindField(fs, tag, fieldI, fieldT.Name())
if err != nil {
return err
}
if !newFlag {
continue
}
// If field value was zero, then set the tag default, if
// specified.
if fieldV.Elem().IsZero() && tag.DefValue != "" {
defaults[tag.Name] = tag.DefValue
if err := fs.Set(tag.Name, tag.DefValue); err != nil {
return ErrorDefaultValue{structField.Name, tag.DefValue, err}
}
}
}
return setDefaults(fs, defaults)
}
func setDefaults(fs FlagSet, defaults map[string]string) error {
switch fs := fs.(type) {
case STDFlagSet:
fs.VisitAll(func(f *flag.Flag) {
defVal, ok := defaults[f.Name]
if !ok {
return
}
f.DefValue = defVal
})
case PFlagSet:
fs.VisitAll(func(f *pflag.Flag) {
defVal, ok := defaults[f.Name]
if !ok {
return
}
f.DefValue = defVal
})
default:
return ErrorInvalidFlagSet
}
return nil
}
func loadExtendedUsage(i int, valT reflect.Type, tag *flagTag) int {
// Check for extended usage tags.
for i++; i < valT.NumField(); i++ {
// Check if next field is named "_" and has a use tag.
usageT := valT.Field(i)
if usageT.Name != "_" {
break
}
usage, ok := usageT.Tag.Lookup("use")
if !ok {
break
}
if tag.Usage != "" {
tag.Usage += " "
}
tag.Usage += usage
}
i--
return i
}
func appendSeparator(prefix string) string {
// Do not append separator to an empty prefix.
if prefix == "" {
return prefix
}
// Do not append separator when other common separators are being used.
for _, sep := range []string{"-", ".", "_"} {
if strings.HasSuffix(prefix, sep) {
return prefix
}
}
return prefix + Separator
}
func bindField(fs FlagSet, tag flagTag, p interface{}, typeName string) (bool, error) {
switch fs := fs.(type) {
case STDFlagSet:
return bindSTDFlag(fs, tag, p), nil
case PFlagSet:
return bindPFlag(fs, tag, p, typeName), nil
default:
return false, ErrorInvalidFlagSet
}
}
func bindSTDFlag(fs STDFlagSet, tag flagTag, p interface{}) bool {
switch p := p.(type) {
case flag.Value:
fs.Var(p, tag.Name, tag.Usage)
case *json.RawMessage:
fs.Var((*JSONRawMessage)(p), tag.Name, tag.Usage)
case *url.URL:
fs.Var((*URL)(p), tag.Name, tag.Usage)
case *bool:
val := *p
fs.BoolVar(p, tag.Name, val, tag.Usage)
case *time.Duration:
val := *p
fs.DurationVar(p, tag.Name, val, tag.Usage)
case *int:
val := *p
fs.IntVar(p, tag.Name, val, tag.Usage)
case *uint:
val := *p
fs.UintVar(p, tag.Name, val, tag.Usage)
case *int64:
val := *p
fs.Int64Var(p, tag.Name, val, tag.Usage)
case *uint64:
val := *p
fs.Uint64Var(p, tag.Name, val, tag.Usage)
case *float64:
val := *p
fs.Float64Var(p, tag.Name, val, tag.Usage)
case *string:
val := *p
fs.StringVar(p, tag.Name, val, tag.Usage)
case textBidiMarshaler:
// Match the interface after concrete types so that any concrete types that
// also implement the interface use the more specific implementation for
// their concrete types.
fs.Var(&pflagMarshalerValue{p, ""}, tag.Name, tag.Usage)
default:
return false
}
if tag.HideDefault {
f := fs.Lookup(tag.Name)
f.DefValue = ""
}
return true
}
func bindPFlag(fs PFlagSet, tag flagTag, p interface{}, typeName string) bool {
var f *pflag.Flag
switch p := p.(type) {
case flag.Value:
// Check if p also implements pflag.Value...
pp, ok := p.(pflag.Value)
if !ok {
// If not, use the pflagValue shim...
pp = pflagValue{p, typeName}
}
f = fs.VarPF(pp, tag.Name, tag.ShortName, tag.Usage)
case *json.RawMessage:
f = fs.VarPF((*JSONRawMessage)(p), tag.Name, tag.ShortName, tag.Usage)
case *url.URL:
f = fs.VarPF((*URL)(p), tag.Name, tag.ShortName, tag.Usage)
case *net.IP:
val := *p
fs.IPVarP(p, tag.Name, tag.ShortName, val, tag.Usage)
case *[]net.IP:
val := *p
fs.IPSliceVarP(p, tag.Name, tag.ShortName, val, tag.Usage)
case *bool:
val := *p
fs.BoolVarP(p, tag.Name, tag.ShortName, val, tag.Usage)
case *[]bool:
val := *p
fs.BoolSliceVarP(p, tag.Name, tag.ShortName, val, tag.Usage)
case *time.Duration:
val := *p
fs.DurationVarP(p, tag.Name, tag.ShortName, val, tag.Usage)
case *[]time.Duration:
val := *p
fs.DurationSliceVarP(p, tag.Name, tag.ShortName, val, tag.Usage)
case *int:
val := *p
fs.IntVarP(p, tag.Name, tag.ShortName, val, tag.Usage)
case *[]int:
val := *p
fs.IntSliceVarP(p, tag.Name, tag.ShortName, val, tag.Usage)
case *uint:
val := *p
fs.UintVarP(p, tag.Name, tag.ShortName, val, tag.Usage)
case *[]uint:
val := *p
fs.UintSliceVarP(p, tag.Name, tag.ShortName, val, tag.Usage)
case *int64:
val := *p
fs.Int64VarP(p, tag.Name, tag.ShortName, val, tag.Usage)
case *[]int64:
val := *p
fs.Int64SliceVarP(p, tag.Name, tag.ShortName, val, tag.Usage)
case *uint64:
val := *p
fs.Uint64VarP(p, tag.Name, tag.ShortName, val, tag.Usage)
case *float32:
val := *p
fs.Float32VarP(p, tag.Name, tag.ShortName, val, tag.Usage)
case *[]float32:
val := *p
fs.Float32SliceVarP(p, tag.Name, tag.ShortName, val, tag.Usage)
case *float64:
val := *p
fs.Float64VarP(p, tag.Name, tag.ShortName, val, tag.Usage)
case *[]float64:
val := *p
fs.Float64SliceVarP(p, tag.Name, tag.ShortName, val, tag.Usage)
case *string:
val := *p
fs.StringVarP(p, tag.Name, tag.ShortName, val, tag.Usage)
case *[]string:
val := *p
fs.StringSliceVarP(p, tag.Name, tag.ShortName, val, tag.Usage)
case textBidiMarshaler:
// Match the interface after concrete types so that any concrete types that
// also implement the interface use the more specific implementation for
// their concrete types.
fs.VarPF(&pflagMarshalerValue{p, typeName}, tag.Name, tag.ShortName, tag.Usage)
default:
return false
}
if !(tag.HideDefault || tag.Hidden) {
return true
}
if f == nil {
f = fs.Lookup(tag.Name)
}
if tag.HideDefault {
f.DefValue = ""
}
f.Hidden = tag.Hidden
return true
}
func overrideFlag(fs FlagSet, tag flagTag) error {
// Update flag if it exists.
switch fs := fs.(type) {
case STDFlagSet:
return overrideSTDFlag(fs, tag)
case PFlagSet:
return overridePFlag(fs, tag)
default:
return ErrorInvalidFlagSet
}
}
func overrideSTDFlag(fs STDFlagSet, tag flagTag) error {
f := fs.Lookup(tag.Name)
if f == nil {
return ErrorFlagOverrideUndefined{tag.Name}
}
if tag.DefValue != "" {
f.Value.Set(tag.DefValue)
f.DefValue = tag.DefValue
}
if tag.Usage != "" {
f.Usage = tag.Usage
}
if tag.HideDefault {
f.DefValue = ""
}
return nil
}
func overridePFlag(fs PFlagSet, tag flagTag) error {
f := fs.Lookup(tag.Name)
if f == nil {
return ErrorFlagOverrideUndefined{tag.Name}
}
if tag.DefValue != "" {
f.Value.Set(tag.DefValue)
f.DefValue = tag.DefValue
}
if tag.Usage != "" {
f.Usage = tag.Usage
}
if tag.HideDefault {
f.DefValue = ""
}
f.Hidden = tag.Hidden
return nil
}