From f5be74bce69f8573696f56899f7cd6f55ecf959d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joan=20L=C3=B3pez=20de=20la=20Franca=20Beltran?= Date: Thu, 4 Apr 2024 08:30:29 +0200 Subject: [PATCH] Fix support for nil marshalable pointers --- envconfig.go | 16 +++++------ envconfig_test.go | 59 +++++++++++++++++++++++++++++++++----- testdata/custom.txt | 2 ++ testdata/default_list.txt | 10 +++++++ testdata/default_table.txt | 2 ++ testdata/fault.txt | 2 ++ 6 files changed, 76 insertions(+), 15 deletions(-) diff --git a/envconfig.go b/envconfig.go index b6bfcc7..ceb9e7f 100644 --- a/envconfig.go +++ b/envconfig.go @@ -253,14 +253,6 @@ func processField(value string, field reflect.Value) error { return setter.Set(value) } - if t := textUnmarshaler(field); t != nil { - return t.UnmarshalText([]byte(value)) - } - - if b := binaryUnmarshaler(field); b != nil { - return b.UnmarshalBinary([]byte(value)) - } - if typ.Kind() == reflect.Ptr { typ = typ.Elem() if field.IsNil() { @@ -269,6 +261,14 @@ func processField(value string, field reflect.Value) error { field = field.Elem() } + if t := textUnmarshaler(field); t != nil { + return t.UnmarshalText([]byte(value)) + } + + if b := binaryUnmarshaler(field); b != nil { + return b.UnmarshalBinary([]byte(value)) + } + switch typ.Kind() { case reflect.String: field.SetString(value) diff --git a/envconfig_test.go b/envconfig_test.go index 2ca765d..a7402db 100644 --- a/envconfig_test.go +++ b/envconfig_test.go @@ -33,6 +33,37 @@ func (cu *CustomURL) UnmarshalBinary(data []byte) error { return err } +type FilePermission uint8 + +type FilePermissionSet FilePermission + +const ( + ReadPermission FilePermission = 1 << iota // 1 << 0 == 1 + WritePermission // 1 << 1 == 2 + ExecutePermission // 1 << 2 == 4 +) + +func (fps *FilePermissionSet) addPermission(permission FilePermission) { + *fps |= FilePermissionSet(permission) +} + +func (fps *FilePermissionSet) UnmarshalText(text []byte) error { + *fps = 0 // Reset permissions + for _, b := range text { + switch b { + case 'r': + fps.addPermission(ReadPermission) + case 'w': + fps.addPermission(WritePermission) + case 'x': + fps.addPermission(ExecutePermission) + default: + return fmt.Errorf("invalid permission: %c", b) + } + } + return nil +} + type Specification struct { Embedded `desc:"can we document a struct"` EmbeddedButIgnored `ignored:"true"` @@ -64,12 +95,14 @@ type Specification struct { Property string `envconfig:"inner"` PropertyWithDefault string `default:"fuzzybydefault"` } `envconfig:"outer"` - AfterNested string - DecodeStruct HonorDecodeInStruct `envconfig:"honor"` - Datetime time.Time - MapField map[string]string `default:"one:two,three:four"` - UrlValue CustomURL - UrlPointer *CustomURL + AfterNested string + DecodeStruct HonorDecodeInStruct `envconfig:"honor"` + Datetime time.Time + MapField map[string]string `default:"one:two,three:four"` + UrlValue CustomURL + UrlPointer *CustomURL + FilePermissionValue FilePermissionSet + FilePermissionPointer *FilePermissionSet } type Embedded struct { @@ -111,6 +144,8 @@ func TestProcess(t *testing.T) { os.Setenv("ENV_CONFIG_MULTI_WORD_ACR_WITH_AUTO_SPLIT", "25") os.Setenv("ENV_CONFIG_URLVALUE", "https://github.com/kelseyhightower/envconfig") os.Setenv("ENV_CONFIG_URLPOINTER", "https://github.com/kelseyhightower/envconfig") + os.Setenv("ENV_CONFIG_FILEPERMISSIONVALUE", "rwx") + os.Setenv("ENV_CONFIG_FILEPERMISSIONPOINTER", "rwx") err := Process("env_config", &s) if err != nil { t.Error(err.Error()) @@ -217,6 +252,16 @@ func TestProcess(t *testing.T) { if *s.UrlPointer.Value != *u { t.Errorf("expected %q, got %q", u, s.UrlPointer.Value.String()) } + + fps := FilePermissionSet(ReadPermission | WritePermission | ExecutePermission) + + if s.FilePermissionValue != fps { + t.Errorf("expected %q, got %q", fps, s.FilePermissionValue) + } + + if *s.FilePermissionPointer != fps { + t.Errorf("expected %q, got %q", fps, *s.FilePermissionPointer) + } } func TestParseErrorBool(t *testing.T) { @@ -794,7 +839,7 @@ func TestCheckDisallowedIgnored(t *testing.T) { func TestErrorMessageForRequiredAltVar(t *testing.T) { var s struct { - Foo string `envconfig:"BAR" required:"true"` + Foo string `envconfig:"BAR" required:"true"` } os.Clearenv() diff --git a/testdata/custom.txt b/testdata/custom.txt index 04d2f5d..9c96951 100644 --- a/testdata/custom.txt +++ b/testdata/custom.txt @@ -34,3 +34,5 @@ ENV_CONFIG_DATETIME= ENV_CONFIG_MAPFIELD= ENV_CONFIG_URLVALUE= ENV_CONFIG_URLPOINTER= +ENV_CONFIG_FILEPERMISSIONVALUE= +ENV_CONFIG_FILEPERMISSIONPOINTER= diff --git a/testdata/default_list.txt b/testdata/default_list.txt index fb0eced..8baee77 100644 --- a/testdata/default_list.txt +++ b/testdata/default_list.txt @@ -181,3 +181,13 @@ ENV_CONFIG_URLPOINTER ..[type]........CustomURL ..[default]..... ..[required].... +ENV_CONFIG_FILEPERMISSIONVALUE +..[description]. +..[type]........FilePermissionSet +..[default]..... +..[required].... +ENV_CONFIG_FILEPERMISSIONPOINTER +..[description]. +..[type]........FilePermissionSet +..[default]..... +..[required].... diff --git a/testdata/default_table.txt b/testdata/default_table.txt index 65c9b44..bfdb997 100644 --- a/testdata/default_table.txt +++ b/testdata/default_table.txt @@ -38,3 +38,5 @@ ENV_CONFIG_DATETIME..............................Time........................... ENV_CONFIG_MAPFIELD..............................Comma-separated.list.of.String:String.pairs.....one:two,three:four................ ENV_CONFIG_URLVALUE..............................CustomURL......................................................................... ENV_CONFIG_URLPOINTER............................CustomURL......................................................................... +ENV_CONFIG_FILEPERMISSIONVALUE...................FilePermissionSet................................................................. +ENV_CONFIG_FILEPERMISSIONPOINTER.................FilePermissionSet................................................................. diff --git a/testdata/fault.txt b/testdata/fault.txt index b525ff1..5932eb3 100644 --- a/testdata/fault.txt +++ b/testdata/fault.txt @@ -34,3 +34,5 @@ {.Key} {.Key} {.Key} +{.Key} +{.Key}