5
5
"flag"
6
6
"fmt"
7
7
"io"
8
- "math"
9
8
"net/url"
10
9
"os"
11
10
"path/filepath"
@@ -29,18 +28,25 @@ const (
29
28
const (
30
29
// Name of the environment variable or a list of names
31
30
TagEnv = "env"
31
+
32
32
// Value parsing layout (for types like time.Time)
33
33
TagEnvLayout = "env-layout"
34
+
34
35
// Default value
35
36
TagEnvDefault = "env-default"
37
+
36
38
// Custom list and map separator
37
39
TagEnvSeparator = "env-separator"
40
+
38
41
// Environment variable description
39
42
TagEnvDescription = "env-description"
43
+
40
44
// Flag to mark a field as updatable
41
45
TagEnvUpd = "env-upd"
46
+
42
47
// Flag to mark a field as required
43
48
TagEnvRequired = "env-required"
49
+
44
50
// Flag to specify prefix for structure fields
45
51
TagEnvPrefix = "env-prefix"
46
52
)
@@ -49,15 +55,15 @@ const (
49
55
//
50
56
// To implement a custom value setter you need to add a SetValue function to your type that will receive a string raw value:
51
57
//
52
- // type MyField string
58
+ // type MyField string
53
59
//
54
- // func (f *MyField) SetValue(s string) error {
55
- // if s == "" {
56
- // return fmt.Errorf("field value can't be empty")
57
- // }
58
- // *f = MyField("my field is: " + s)
59
- // return nil
60
- // }
60
+ // func (f *MyField) SetValue(s string) error {
61
+ // if s == "" {
62
+ // return fmt.Errorf("field value can't be empty")
63
+ // }
64
+ // *f = MyField("my field is: " + s)
65
+ // return nil
66
+ // }
61
67
type Setter interface {
62
68
SetValue (string ) error
63
69
}
@@ -72,20 +78,20 @@ type Updater interface {
72
78
//
73
79
// Example:
74
80
//
75
- // type ConfigDatabase struct {
76
- // Port string `yaml:"port" env:"PORT" env-default:"5432"`
77
- // Host string `yaml:"host" env:"HOST" env-default:"localhost"`
78
- // Name string `yaml:"name" env:"NAME" env-default:"postgres"`
79
- // User string `yaml:"user" env:"USER" env-default:"user"`
80
- // Password string `yaml:"password" env:"PASSWORD"`
81
- // }
81
+ // type ConfigDatabase struct {
82
+ // Port string `yaml:"port" env:"PORT" env-default:"5432"`
83
+ // Host string `yaml:"host" env:"HOST" env-default:"localhost"`
84
+ // Name string `yaml:"name" env:"NAME" env-default:"postgres"`
85
+ // User string `yaml:"user" env:"USER" env-default:"user"`
86
+ // Password string `yaml:"password" env:"PASSWORD"`
87
+ // }
82
88
//
83
- // var cfg ConfigDatabase
89
+ // var cfg ConfigDatabase
84
90
//
85
- // err := cleanenv.ReadConfig("config.yml", &cfg)
86
- // if err != nil {
87
- // ...
88
- // }
91
+ // err := cleanenv.ReadConfig("config.yml", &cfg)
92
+ // if err != nil {
93
+ // ...
94
+ // }
89
95
func ReadConfig (path string , cfg interface {}) error {
90
96
err := parseFile (path , cfg )
91
97
if err != nil {
@@ -178,11 +184,58 @@ func parseENV(r io.Reader, _ interface{}) error {
178
184
}
179
185
180
186
for env , val := range vars {
181
- os .Setenv (env , val )
187
+ if err = os .Setenv (env , val ); err != nil {
188
+ return fmt .Errorf ("set environment: %w" , err )
189
+ }
182
190
}
191
+
183
192
return nil
184
193
}
185
194
195
+ // parseSlice parses value into a slice of given type
196
+ func parseSlice (valueType reflect.Type , value string , sep string , layout * string ) (* reflect.Value , error ) {
197
+ sliceValue := reflect .MakeSlice (valueType , 0 , 0 )
198
+ if valueType .Elem ().Kind () == reflect .Uint8 {
199
+ sliceValue = reflect .ValueOf ([]byte (value ))
200
+ } else if len (strings .TrimSpace (value )) != 0 {
201
+ values := strings .Split (value , sep )
202
+ sliceValue = reflect .MakeSlice (valueType , len (values ), len (values ))
203
+
204
+ for i , val := range values {
205
+ if err := parseValue (sliceValue .Index (i ), val , sep , layout ); err != nil {
206
+ return nil , err
207
+ }
208
+ }
209
+ }
210
+ return & sliceValue , nil
211
+ }
212
+
213
+ // parseMap parses value into a map of given type
214
+ func parseMap (valueType reflect.Type , value string , sep string , layout * string ) (* reflect.Value , error ) {
215
+ mapValue := reflect .MakeMap (valueType )
216
+ if len (strings .TrimSpace (value )) != 0 {
217
+ pairs := strings .Split (value , sep )
218
+ for _ , pair := range pairs {
219
+ kvPair := strings .SplitN (pair , ":" , 2 )
220
+ if len (kvPair ) != 2 {
221
+ return nil , fmt .Errorf ("invalid map item: %q" , pair )
222
+ }
223
+ k := reflect .New (valueType .Key ()).Elem ()
224
+ err := parseValue (k , kvPair [0 ], sep , layout )
225
+ if err != nil {
226
+ return nil , err
227
+ }
228
+ v := reflect .New (valueType .Elem ()).Elem ()
229
+ err = parseValue (v , kvPair [1 ], sep , layout )
230
+ if err != nil {
231
+ return nil , err
232
+ }
233
+ mapValue .SetMapIndex (k , v )
234
+ }
235
+ }
236
+ return & mapValue , nil
237
+ }
238
+
186
239
// structMeta is a structure metadata entity
187
240
type structMeta struct {
188
241
envList []string
@@ -198,14 +251,15 @@ type structMeta struct {
198
251
199
252
// isFieldValueZero determines if fieldValue empty or not
200
253
func (sm * structMeta ) isFieldValueZero () bool {
201
- return isZero ( sm .fieldValue )
254
+ return sm .fieldValue . IsZero ( )
202
255
}
203
256
204
257
// parseFunc custom value parser function
205
258
type parseFunc func (* reflect.Value , string , * string ) error
206
259
207
260
// Any specific supported struct can be added here
208
261
var validStructs = map [reflect.Type ]parseFunc {
262
+
209
263
reflect .TypeOf (time.Time {}): func (field * reflect.Value , value string , layout * string ) error {
210
264
var l string
211
265
if layout != nil {
@@ -220,6 +274,7 @@ var validStructs = map[reflect.Type]parseFunc{
220
274
field .Set (reflect .ValueOf (val ))
221
275
return nil
222
276
},
277
+
223
278
reflect .TypeOf (url.URL {}): func (field * reflect.Value , value string , _ * string ) error {
224
279
val , err := url .Parse (value )
225
280
if err != nil {
@@ -228,6 +283,16 @@ var validStructs = map[reflect.Type]parseFunc{
228
283
field .Set (reflect .ValueOf (* val ))
229
284
return nil
230
285
},
286
+
287
+ reflect .TypeOf (& time.Location {}): func (field * reflect.Value , value string , _ * string ) error {
288
+ loc , err := time .LoadLocation (value )
289
+ if err != nil {
290
+ return err
291
+ }
292
+
293
+ field .Set (reflect .ValueOf (loc ))
294
+ return nil
295
+ },
231
296
}
232
297
233
298
// readStructMetadata reads structure metadata (types, tags, etc.)
@@ -268,12 +333,14 @@ func readStructMetadata(cfgRoot interface{}) ([]structMeta, error) {
268
333
269
334
// process nested structure (except of supported ones)
270
335
if fld := s .Field (idx ); fld .Kind () == reflect .Struct {
336
+
271
337
// add structure to parsing stack
272
338
if _ , found := validStructs [fld .Type ()]; ! found {
273
339
prefix , _ := fType .Tag .Lookup (TagEnvPrefix )
274
340
cfgStack = append (cfgStack , cfgNode {fld .Addr ().Interface (), sPrefix + prefix })
275
341
continue
276
342
}
343
+
277
344
// process time.Time
278
345
if l , ok := fType .Tag .Lookup (TagEnvLayout ); ok {
279
346
layout = & l
@@ -357,9 +424,10 @@ func readEnvVars(cfg interface{}, update bool) error {
357
424
}
358
425
359
426
if rawValue == nil && meta .required && meta .isFieldValueZero () {
360
- err := fmt .Errorf ("field %q is required but the value is not provided" ,
361
- meta .fieldName )
362
- return err
427
+ return fmt .Errorf (
428
+ "field %q is required but the value is not provided" ,
429
+ meta .fieldName ,
430
+ )
363
431
}
364
432
365
433
if rawValue == nil && meta .isFieldValueZero () {
@@ -392,8 +460,8 @@ func parseValue(field reflect.Value, value, sep string, layout *string) error {
392
460
}
393
461
394
462
valueType := field .Type ()
395
-
396
463
switch valueType .Kind () {
464
+
397
465
// parse string value
398
466
case reflect .String :
399
467
field .SetString (value )
@@ -406,16 +474,22 @@ func parseValue(field reflect.Value, value, sep string, layout *string) error {
406
474
}
407
475
field .SetBool (b )
408
476
409
- // parse integer (or time) value
410
- case reflect .Int , reflect .Int8 , reflect .Int16 , reflect .Int32 , reflect .Int64 :
411
- if field .Kind () == reflect .Int64 && valueType .PkgPath () == "time" && valueType .Name () == "Duration" {
477
+ // parse integer
478
+ case reflect .Int , reflect .Int8 , reflect .Int16 , reflect .Int32 :
479
+ number , err := strconv .ParseInt (value , 0 , valueType .Bits ())
480
+ if err != nil {
481
+ return err
482
+ }
483
+ field .SetInt (number )
484
+
485
+ case reflect .Int64 :
486
+ if valueType == reflect .TypeOf (time .Duration (0 )) {
412
487
// try to parse time
413
488
d , err := time .ParseDuration (value )
414
489
if err != nil {
415
490
return err
416
491
}
417
492
field .SetInt (int64 (d ))
418
-
419
493
} else {
420
494
// parse regular integer
421
495
number , err := strconv .ParseInt (value , 0 , valueType .Bits ())
@@ -459,62 +533,18 @@ func parseValue(field reflect.Value, value, sep string, layout *string) error {
459
533
460
534
field .Set (* mapValue )
461
535
462
- case reflect .Struct :
536
+ default :
537
+ // look for supported struct parser
463
538
if structParser , found := validStructs [valueType ]; found {
464
539
return structParser (& field , value , layout )
465
540
}
466
541
467
- default :
468
542
return fmt .Errorf ("unsupported type %s.%s" , valueType .PkgPath (), valueType .Name ())
469
543
}
470
544
471
545
return nil
472
546
}
473
547
474
- // parseSlice parses value into a slice of given type
475
- func parseSlice (valueType reflect.Type , value string , sep string , layout * string ) (* reflect.Value , error ) {
476
- sliceValue := reflect .MakeSlice (valueType , 0 , 0 )
477
- if valueType .Elem ().Kind () == reflect .Uint8 {
478
- sliceValue = reflect .ValueOf ([]byte (value ))
479
- } else if len (strings .TrimSpace (value )) != 0 {
480
- values := strings .Split (value , sep )
481
- sliceValue = reflect .MakeSlice (valueType , len (values ), len (values ))
482
-
483
- for i , val := range values {
484
- if err := parseValue (sliceValue .Index (i ), val , sep , layout ); err != nil {
485
- return nil , err
486
- }
487
- }
488
- }
489
- return & sliceValue , nil
490
- }
491
-
492
- // parseMap parses value into a map of given type
493
- func parseMap (valueType reflect.Type , value string , sep string , layout * string ) (* reflect.Value , error ) {
494
- mapValue := reflect .MakeMap (valueType )
495
- if len (strings .TrimSpace (value )) != 0 {
496
- pairs := strings .Split (value , sep )
497
- for _ , pair := range pairs {
498
- kvPair := strings .SplitN (pair , ":" , 2 )
499
- if len (kvPair ) != 2 {
500
- return nil , fmt .Errorf ("invalid map item: %q" , pair )
501
- }
502
- k := reflect .New (valueType .Key ()).Elem ()
503
- err := parseValue (k , kvPair [0 ], sep , layout )
504
- if err != nil {
505
- return nil , err
506
- }
507
- v := reflect .New (valueType .Elem ()).Elem ()
508
- err = parseValue (v , kvPair [1 ], sep , layout )
509
- if err != nil {
510
- return nil , err
511
- }
512
- mapValue .SetMapIndex (k , v )
513
- }
514
- }
515
- return & mapValue , nil
516
- }
517
-
518
548
// GetDescription returns a description of environment variables.
519
549
// You can provide a custom header text.
520
550
func GetDescription (cfg interface {}, headerText * string ) (string , error ) {
@@ -583,42 +613,3 @@ func FUsage(w io.Writer, cfg interface{}, headerText *string, usageFuncs ...func
583
613
fmt .Fprintln (w , text )
584
614
}
585
615
}
586
-
587
- // isZero is a backport of reflect.Value.IsZero()
588
- func isZero (v reflect.Value ) bool {
589
- switch v .Kind () {
590
- case reflect .Bool :
591
- return ! v .Bool ()
592
- case reflect .Int , reflect .Int8 , reflect .Int16 , reflect .Int32 , reflect .Int64 :
593
- return v .Int () == 0
594
- case reflect .Uint , reflect .Uint8 , reflect .Uint16 , reflect .Uint32 , reflect .Uint64 , reflect .Uintptr :
595
- return v .Uint () == 0
596
- case reflect .Float32 , reflect .Float64 :
597
- return math .Float64bits (v .Float ()) == 0
598
- case reflect .Complex64 , reflect .Complex128 :
599
- c := v .Complex ()
600
- return math .Float64bits (real (c )) == 0 && math .Float64bits (imag (c )) == 0
601
- case reflect .Array :
602
- for i := 0 ; i < v .Len (); i ++ {
603
- if ! isZero (v .Index (i )) {
604
- return false
605
- }
606
- }
607
- return true
608
- case reflect .Chan , reflect .Func , reflect .Interface , reflect .Map , reflect .Ptr , reflect .Slice , reflect .UnsafePointer :
609
- return v .IsNil ()
610
- case reflect .String :
611
- return v .Len () == 0
612
- case reflect .Struct :
613
- for i := 0 ; i < v .NumField (); i ++ {
614
- if ! isZero (v .Field (i )) {
615
- return false
616
- }
617
- }
618
- return true
619
- default :
620
- // This should never happens, but will act as a safeguard for
621
- // later, as a default value doesn't makes sense here.
622
- panic (fmt .Sprintf ("Value.IsZero: %v" , v .Kind ()))
623
- }
624
- }
0 commit comments