Skip to content

Commit

Permalink
implement reverse env to struct for duration in floating point second…
Browse files Browse the repository at this point in the history
…s and date/time in rfc3339
  • Loading branch information
ldemailly committed Nov 7, 2023
1 parent cf32bf5 commit 27b277f
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 5 deletions.
30 changes: 25 additions & 5 deletions env.go
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,8 @@ func setFromEnv(allErrors []error, envLookup EnvLookup, prefix string, s interfa

kind := fieldValue.Kind()

if kind == reflect.Struct {
// Handle time.Time separately a bit below after we get the value
if kind == reflect.Struct && fieldType.Type != reflect.TypeOf(time.Time{}) {
// Recurse with prefix
if fieldValue.CanAddr() { // Check if we can get the address
allErrors = setFromEnv(allErrors, envLookup, envName+"_", fieldValue.Addr().Interface())
Expand All @@ -332,14 +333,33 @@ func setFromEnv(allErrors []error, envLookup EnvLookup, prefix string, s interfa
kind = fieldValue.Type().Elem().Kind()
fieldValue = setPointer(fieldValue)
}
if fieldType.Type == reflect.TypeOf(time.Time{}) {
var timeField time.Time
timeField, err = time.Parse(time.RFC3339, envVal)
if err == nil {
fieldValue.Set(reflect.ValueOf(timeField))
} else {
allErrors = append(allErrors, err)
}
continue
}
switch kind { //nolint: exhaustive // we have default: for the other cases
case reflect.String:
fieldValue.SetString(envVal)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
var ev int64
ev, err = strconv.ParseInt(envVal, 10, fieldValue.Type().Bits())
if err == nil {
fieldValue.SetInt(ev)
// if it's a duration, parse it as a float seconds
if fieldType.Type == reflect.TypeOf(time.Duration(0)) {
var ev float64
ev, err = strconv.ParseFloat(envVal, 64)
if err == nil {
fieldValue.SetInt(int64(ev * float64(1*time.Second)))
}
} else {
var ev int64
ev, err = strconv.ParseInt(envVal, 10, fieldValue.Type().Bits())
if err == nil {
fieldValue.SetInt(ev)
}
}
case reflect.Float32, reflect.Float64:
var ev float64
Expand Down
13 changes: 13 additions & 0 deletions env_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,8 @@ func TestSetFromEnv(t *testing.T) {
"TST2_FLOAT_POINTER": "5.75",
"TST2_INT_POINTER": "73",
"TST2_SOME_BINARY": "QUJDAERFRg==",
"TST2_DUR": "123.456789",
"TST2_TS": "1998-11-05T14:30:00Z",
}
lookup := func(key string) (string, bool) {
value, found := envs[key]
Expand All @@ -229,4 +231,15 @@ func TestSetFromEnv(t *testing.T) {
if string(foo.SomeBinary) != "ABC\x00DEF" {
t.Errorf("Base64 decoding not working for []byte field: %q", string(foo.SomeBinary))
}
if foo.Dur != 123456789*time.Microsecond {
t.Errorf("Duration not set correctly: %v", foo.Dur)
}
if foo.TS != time.Date(1998, time.November, 5, 14, 30, 0, 0, time.UTC) {
t.Errorf("Time not set correctly: %v", foo.TS)
}
envs["TST2_TS"] = "not a rfc3339 time"
errors = SetFrom(lookup, "TST2_", &foo)
if len(errors) != 1 {
t.Errorf("Expected 1 error, got %v", errors)
}
}

0 comments on commit 27b277f

Please sign in to comment.