@@ -12,6 +12,7 @@ const (
1212 defaultEnvTag = "env"
1313 defaultFlagDivider = "-"
1414 defaultEnvDivider = "_"
15+ defaultFlatten = true
1516)
1617
1718// ValidateFunc describes a validation func,
@@ -27,9 +28,17 @@ type opts struct {
2728 envPrefix string
2829 flagDivider string
2930 envDivider string
31+ flatten bool
3032 validator ValidateFunc
3133}
3234
35+ func (o opts ) apply (optFuncs ... OptFunc ) opts {
36+ for _ , optFunc := range optFuncs {
37+ optFunc (& o )
38+ }
39+ return o
40+ }
41+
3342// OptFunc sets values in opts structure.
3443type OptFunc func (opt * opts )
3544
@@ -56,6 +65,10 @@ func EnvDivider(val string) OptFunc { return func(opt *opts) { opt.envDivider =
5665// Check existed validators in sflags/validator package.
5766func Validator (val ValidateFunc ) OptFunc { return func (opt * opts ) { opt .validator = val } }
5867
68+ // Flatten set flatten option.
69+ // Set to false if you don't want anonymous structure fields to be flatten.
70+ func Flatten (val bool ) OptFunc { return func (opt * opts ) { opt .flatten = val } }
71+
5972func copyOpts (val opts ) OptFunc { return func (opt * opts ) { * opt = val } }
6073
6174func hasOption (options []string , option string ) bool {
@@ -67,6 +80,16 @@ func hasOption(options []string, option string) bool {
6780 return false
6881}
6982
83+ func defOpts () opts {
84+ return opts {
85+ descTag : defaultDescTag ,
86+ flagTag : defaultFlagTag ,
87+ flagDivider : defaultFlagDivider ,
88+ envDivider : defaultEnvDivider ,
89+ flatten : defaultFlatten ,
90+ }
91+ }
92+
7093func parseFlagTag (field reflect.StructField , opt opts ) * Flag {
7194 flag := Flag {}
7295 ignoreFlagPrefix := false
@@ -134,92 +157,94 @@ func parseEnv(flagName string, field reflect.StructField, opt opts) string {
134157}
135158
136159// ParseStruct parses structure and returns list of flags based on this structure.
137- // This list of flags can be used by generators for
138- // flag, kingpin, cobra, pflag, urfave/cli.
160+ // This list of flags can be used by generators for flag, kingpin, cobra, pflag, urfave/cli.
139161func ParseStruct (cfg interface {}, optFuncs ... OptFunc ) ([]* Flag , error ) {
140162 // what we want is Ptr to Structure
141- if reflect . ValueOf ( cfg ). Kind () != reflect . Ptr {
142- return nil , errors .New ("cfg must be a pointer to a structure " )
163+ if cfg == nil {
164+ return nil , errors .New ("object cannot be nil " )
143165 }
144- cfgValue := reflect .Indirect ( reflect . ValueOf (cfg ) )
145- if cfgValue .Kind () != reflect .Struct {
146- return nil , errors .New ("cfg must be a pointer to a structure " )
166+ v := reflect .ValueOf (cfg )
167+ if v .Kind () != reflect .Ptr {
168+ return nil , errors .New ("object must be a pointer to struct or interface " )
147169 }
148- opt := opts {
149- descTag : defaultDescTag ,
150- flagTag : defaultFlagTag ,
151- flagDivider : defaultFlagDivider ,
152- envDivider : defaultEnvDivider ,
170+ if v .IsNil () {
171+ return nil , errors .New ("object cannot be nil" )
153172 }
154- for _ , optFunc := range optFuncs {
155- optFunc (& opt )
173+ switch e := v .Elem (); e .Kind () {
174+ case reflect .Struct :
175+ return parseStruct (e , optFuncs ... ), nil
176+ default :
177+ return nil , errors .New ("object must be a pointer to struct or interface" )
156178 }
179+ }
180+
181+ func parseVal (value reflect.Value , optFuncs ... OptFunc ) ([]* Flag , Value ) {
182+ // value is addressable, let's check if we can parse it
183+ if value .CanAddr () && value .Addr ().CanInterface () {
184+ valueInterface := value .Addr ().Interface ()
185+ val := parseGenerated (valueInterface )
186+ if val != nil {
187+ return nil , val
188+ }
189+ // check if field implements Value interface
190+ if val , casted := valueInterface .(Value ); casted {
191+ return nil , val
192+ }
193+ }
194+
195+ switch value .Kind () {
196+ case reflect .Ptr :
197+ if value .IsNil () {
198+ value .Set (reflect .New (value .Type ().Elem ()))
199+ }
200+ val := parseGeneratedPtrs (value .Addr ().Interface ())
201+ if val != nil {
202+ return nil , val
203+ }
204+ return parseVal (value .Elem (), optFuncs ... )
205+ case reflect .Struct :
206+ flags := parseStruct (value , optFuncs ... )
207+ return flags , nil
208+ }
209+ return nil , nil
210+ }
211+
212+ func parseStruct (value reflect.Value , optFuncs ... OptFunc ) []* Flag {
213+ opt := defOpts ().apply (optFuncs ... )
157214
158215 flags := []* Flag {}
159216
160- cfgType := cfgValue .Type ()
217+ valueType := value .Type ()
161218fields:
162- for i := 0 ; i < cfgType .NumField (); i ++ {
163- field := cfgType .Field (i )
164- fieldValue := cfgValue .FieldByName (field .Name )
219+ for i := 0 ; i < value .NumField (); i ++ {
220+ field := valueType .Field (i )
221+ fieldValue := value .Field (i )
222+ // skip unexported and non anonymous fields
223+ if field .PkgPath != "" && ! field .Anonymous {
224+ continue fields
225+ }
165226
166227 flag := parseFlagTag (field , opt )
167228 if flag == nil {
168229 continue fields
169230 }
170231 flag .EnvName = parseEnv (flag .Name , field , opt )
171-
172232 flag .Usage = field .Tag .Get (opt .descTag )
173-
174- if ! ( fieldValue . CanAddr () && fieldValue . Addr (). CanInterface ()) {
175- continue fields
233+ prefix := flag . Name + opt . flagDivider
234+ if field . Anonymous && opt . flatten {
235+ prefix = opt . prefix
176236 }
177-
178- fieldValueAddr := fieldValue .Addr ().Interface ()
179- kind := fieldValue .Kind ()
180-
181- // if field is Ptr but it's nil then create new value for it.
182- if kind == reflect .Ptr {
183- if fieldValue .IsNil () {
184- fieldValue .Set (reflect .New (fieldValue .Type ().Elem ()))
185- }
186- }
187- // check if field has required pointer type (**regex.Regexp f.e)
188- if val := parseGeneratedPtrs (fieldValueAddr ); val != nil {
189- if opt .validator != nil {
190- val = & validateValue {
191- Value : val ,
192- validateFunc : func (val string ) error {
193- return opt .validator (val , field , cfg )
194- },
195- }
196- }
197- flag .Value = val
198- flag .DefValue = val .String ()
199- flags = append (flags , flag )
200- continue fields
201- }
202- // check if field is pointer to a value.
203- if kind == reflect .Ptr {
204- kind = fieldValue .Type ().Elem ().Kind ()
205- fieldValueAddr = fieldValue .Interface ()
206- }
207- var val Value
208- // check if field implements Value interface
209- if fieldIsVal , casted := fieldValueAddr .(Value ); casted {
210- val = fieldIsVal
211- }
212- // check if field is from generated types
213- if val == nil {
214- val = parseGenerated (fieldValueAddr )
215- }
216-
237+ nestedFlags , val := parseVal (fieldValue ,
238+ copyOpts (opt ),
239+ Prefix (prefix ),
240+ )
241+ // field contains a simple value.
217242 if val != nil {
218243 if opt .validator != nil {
219244 val = & validateValue {
220245 Value : val ,
221246 validateFunc : func (val string ) error {
222- return opt .validator (val , field , cfg )
247+ return opt .validator (val , field , value . Interface () )
223248 },
224249 }
225250 }
@@ -228,21 +253,12 @@ fields:
228253 flags = append (flags , flag )
229254 continue fields
230255 }
231-
232- // field is a nested structure
233- switch kind {
234- case reflect .Struct :
235- subFlags , err := ParseStruct (fieldValueAddr ,
236- copyOpts (opt ),
237- Prefix (flag .Name + opt .flagDivider ),
238- )
239- if err != nil {
240- return nil , err
241- }
242- flags = append (flags , subFlags ... )
256+ // field is a structure
257+ if len (nestedFlags ) > 0 {
258+ flags = append (flags , nestedFlags ... )
243259 continue fields
244260 }
245261
246262 }
247- return flags , nil
263+ return flags
248264}
0 commit comments