@@ -57,11 +57,24 @@ func Flags(flags Flagger, main interface{}) error {
57
57
return setFlags (newFlagTracker (flags ), main , "" )
58
58
}
59
59
60
+ type flagSet struct {
61
+ * flag.FlagSet
62
+ }
63
+
64
+ func (f * flagSet ) Flags () (flags []string ) {
65
+ f .VisitAll (func (f * flag.Flag ) {
66
+ flags = append (flags , f .Name )
67
+ })
68
+ return flags
69
+ }
70
+
71
+ var _ = FlagNamer (& flagSet {})
72
+
60
73
// Run runs "main" which must be a pointer to a struct which implements the
61
74
// Runner interface. It first calls Flags to set up command line flags based on
62
75
// "main" (see the documentation for Flags).
63
76
func Run (main interface {}) error {
64
- return RunArgs (flag .CommandLine , main , os .Args [1 :])
77
+ return RunArgs (& flagSet { flag .CommandLine } , main , os .Args [1 :])
65
78
}
66
79
67
80
var replacer * strings.Replacer = strings .NewReplacer ("-" , "_" , "." , "_" )
@@ -72,39 +85,35 @@ func envNorm(name string) string {
72
85
73
86
// loadEnv visits each flag in the FlagSet and sets its value based on
74
87
// OS environment.
75
- func loadEnv (flagset * flag.FlagSet , prefix string ) error {
76
- var err error
77
-
78
- flagset .VisitAll (func (f * flag.Flag ) {
79
- // skip rest if there is an error
80
- if err != nil {
81
- return
82
- }
83
- envString := envNorm (prefix + f .Name )
84
- val , ok := os .LookupEnv (envString )
85
- if ok {
86
- err = flagset .Set (f .Name , val )
87
- if err != nil {
88
- err = fmt .Errorf ("couldn't set %s to %s from env %s: %v" , f .Name , val , envString , err )
88
+ func loadEnv (flagger Flagger , prefix string ) (err error ) {
89
+ if namer , ok := flagger .(FlagNamer ); ok {
90
+ for _ , name := range namer .Flags () {
91
+ envString := envNorm (prefix + name )
92
+ val , ok := os .LookupEnv (envString )
93
+ if ok {
94
+ err = flagger .Set (name , val )
95
+ if err != nil {
96
+ return fmt .Errorf ("couldn't set %s to %s from env %s: %v" , name , val , envString , err )
97
+ }
89
98
}
90
99
}
91
- })
92
- return err
100
+ } else {
101
+ return fmt .Errorf ("unable to load flags from environment: flagger does not implement FlagNamer" )
102
+ }
103
+ return nil
93
104
}
94
105
95
106
// LoadEnv calls LoadArgsEnv with args from the command line and the
96
107
// default flag set.
97
108
func LoadEnv (main interface {}, envPrefix string , parseElsewhere func (main interface {}) error ) error {
98
- return LoadArgsEnv (flag .CommandLine , main , os .Args [1 :], envPrefix , parseElsewhere )
109
+ return LoadArgsEnv (& flagSet { flag .CommandLine } , main , os .Args [1 :], envPrefix , parseElsewhere )
99
110
}
100
111
101
112
// LoadArgsEnv uses Flags to define flags based on "main", then it
102
113
// tries setting each flag's value from the OS environment based on a
103
114
// prefix concatenated to the flag name. The flag name is normalized
104
115
// by removing any dashes or dots and replacing them with
105
- // underscores. It differs from RunArgs in that it *must* take a
106
- // stdlib *FlagSet rather than the more generic Flagger. TODO: have a
107
- // way to use alternative flag implementations such as pflag.
116
+ // underscores.
108
117
//
109
118
// One may also pass a "configElsewhere" function which can operate on
110
119
// main arbitrarily. The purpose of this is to load config values from
@@ -118,7 +127,7 @@ func LoadEnv(main interface{}, envPrefix string, parseElsewhere func(main interf
118
127
// can be configured (such as with a path to a config file). Once
119
128
// configElsewhere runs, the environment and command line args are
120
129
// re-set since they take higher precedence.
121
- func LoadArgsEnv (flags * flag. FlagSet , main interface {}, args []string , envPrefix string , configElsewhere func (main interface {}) error ) error {
130
+ func LoadArgsEnv (flags Flagger , main interface {}, args []string , envPrefix string , configElsewhere func (main interface {}) error ) error {
122
131
// setup flags
123
132
err := Flags (flags , main )
124
133
if err != nil {
@@ -247,9 +256,15 @@ func setFlags(flags *flagTracker, main interface{}, prefix string) error {
247
256
continue
248
257
case []string :
249
258
// special case support for string slice with stdlib flags
250
- if stdflag , ok := flags .flagger .(* flag.FlagSet ); ! flags .pflag && ok {
251
- stdflag .Var (stringSliceValue {value : f .Addr ().Interface ().(* []string )}, flagName , flagHelp (ft ))
252
- continue
259
+ if ! flags .pflag {
260
+ if stdflag , ok := flags .flagger .(* flag.FlagSet ); ok {
261
+ stdflag .Var (stringSliceValue {value : f .Addr ().Interface ().(* []string )}, flagName , flagHelp (ft ))
262
+ continue
263
+ }
264
+ if stdflagcom , ok := flags .flagger .(* flagSet ); ok {
265
+ stdflagcom .Var (stringSliceValue {value : f .Addr ().Interface ().(* []string )}, flagName , flagHelp (ft ))
266
+ continue
267
+ }
253
268
}
254
269
}
255
270
@@ -580,6 +595,7 @@ type Flagger interface {
580
595
Uint64Var (p * uint64 , name string , value uint64 , usage string )
581
596
Float64Var (p * float64 , name string , value float64 , usage string )
582
597
DurationVar (p * time.Duration , name string , value time.Duration , usage string )
598
+ Set (name string , value string ) error
583
599
}
584
600
585
601
// PFlagger is an extension of the Flagger interface which is implemented by the
@@ -612,6 +628,11 @@ type PFlagger interface {
612
628
DurationVarP (p * time.Duration , name string , shorthand string , value time.Duration , usage string )
613
629
}
614
630
631
+ // FlagNamer is an interface that Flaggers may use to list the available flags.
632
+ type FlagNamer interface {
633
+ Flags () []string
634
+ }
635
+
615
636
// Runner must be implemented by things passed to the Run and RunArgs methods.
616
637
type Runner interface {
617
638
Run () error
0 commit comments