From a6269efa2abcc460a0f958a0a6335f26aeb337ea Mon Sep 17 00:00:00 2001 From: Alexey Kamenskiy Date: Sun, 29 Jan 2023 15:48:11 +1100 Subject: [PATCH] * Fix slice parsing for non struct types * Derive envvar name from field name as backup --- envparse.go | 11 ++++++++--- envparse_test.go | 34 ++++++++++++++++++++++++++++++++++ tag.go | 5 ++--- tag_test.go | 32 +++++++------------------------- 4 files changed, 51 insertions(+), 31 deletions(-) diff --git a/envparse.go b/envparse.go index 867d761..008f1c8 100644 --- a/envparse.go +++ b/envparse.go @@ -62,7 +62,7 @@ func parseStruct(structType reflect.Type, env envMap, depth int, errorList *Erro continue } - tag, err := parseTag(tagString) + tag, err := parseTag(tagString, fieldType.Name) if err != nil { errorList.Append(err) continue @@ -77,7 +77,7 @@ func parseStruct(structType reflect.Type, env envMap, depth int, errorList *Erro } if tag.required { - if fieldValue.Type().Kind() == reflect.Struct { + if fieldValue.Type().Kind() == reflect.Struct || fieldValue.Type().Kind() == reflect.Slice { if !env.PrefixExists(tag.name) { // if field marked as required, but relevant environment variable is not provided, // that is parsing error and can skip further processing of this field. @@ -144,7 +144,12 @@ func parseSlice(sliceType reflect.Type, env envMap, depth int, errorList *ErrorL assignableItem = item } - err := switchFunc(assignableItem, env.GetPrefix(slicePrefix), slicePrefix, depth, errorList) + valueString := "" + if env.Exists(slicePrefix) { + valueString = env.Get(slicePrefix) + } + + err := switchFunc(assignableItem, env.GetPrefix(slicePrefix), valueString, depth, errorList) if err != nil { errorList.Append(fmt.Errorf("error parsing slice: %w", err)) break diff --git a/envparse_test.go b/envparse_test.go index 12adcca..c6e1187 100644 --- a/envparse_test.go +++ b/envparse_test.go @@ -402,3 +402,37 @@ func TestParse_9(t *testing.T) { t.Errorf("expected APP_TEST == '', but got APP_TEST == '%s'", os.Getenv("APP_TEST")) } } + +func TestRequiredSliceOfStrings(t *testing.T) { + env := []string{ + "APP_STRINGS_0=1", + } + type conf struct { + Strings []string `env:"name=strings,required"` + } + c := &conf{} + err := Parse(c, env) + if err != nil { + t.Errorf("unexpected error: %s", err) + } + if len(c.Strings) != 1 || c.Strings[0] != "1" { + t.Errorf("expected c.Strings[0] == '1', but got: %v", c.Strings) + } +} + +func TestRequiredSliceOfInts(t *testing.T) { + env := []string{ + "APP_INTS_0=1", + } + type conf struct { + Ints []int `env:"required"` + } + c := &conf{} + err := Parse(c, env) + if err != nil { + t.Errorf("unexpected error: %s", err) + } + if len(c.Ints) != 1 || c.Ints[0] != 1 { + t.Errorf("expected c.Ints[0] == 1, but got: %v", c.Ints) + } +} diff --git a/tag.go b/tag.go index 369c180..d1ed11d 100644 --- a/tag.go +++ b/tag.go @@ -13,11 +13,10 @@ type tagType struct { } var ( - tagErrNameField = errors.New("field tag must provide a name for field") tagErrIncompatibleFields = errors.New("field tag cannot be required and have default value at the same time") ) -func parseTag(t string) (*tagType, error) { +func parseTag(t string, fieldName string) (*tagType, error) { name := "" required := false defaultValue := "" @@ -34,7 +33,7 @@ func parseTag(t string) (*tagType, error) { } if name == "" { - return nil, tagErrNameField + name = strings.ToUpper(regexp.MustCompile(`[^A-Za-z0-9]`).ReplaceAllString(fieldName, "")) } if required && defaultValue != "" { return nil, tagErrIncompatibleFields diff --git a/tag_test.go b/tag_test.go index 6971308..f11c517 100644 --- a/tag_test.go +++ b/tag_test.go @@ -8,30 +8,12 @@ import ( func TestParseTag(t *testing.T) { var in string var expectedOut *tagType - expectedErr := tagErrNameField - tag, err := parseTag(in) - if tag != expectedOut { - t.Errorf("expected tag to be %v, but got %v", expectedOut, tag) - } - if expectedErr != err { - t.Errorf("expected error to be '%s', but got '%s'", expectedErr, err) - } - - in = "name=" - expectedOut = nil - expectedErr = tagErrNameField - tag, err = parseTag(in) - if !reflect.DeepEqual(tag, expectedOut) { - t.Errorf("expected tag to be %v, but got %v", expectedOut, tag) - } - if expectedErr != err { - t.Errorf("expected error to be '%s', but got '%s'", expectedErr, err) - } + var expectedErr error in = "name=HELLO1234_*\n/" expectedOut = &tagType{name: "HELLO1234", required: false, defaultValue: ""} expectedErr = nil - tag, err = parseTag(in) + tag, err := parseTag(in, "") if !reflect.DeepEqual(tag, expectedOut) { t.Errorf("expected tag to be %v, but got %v", expectedOut, tag) } @@ -42,7 +24,7 @@ func TestParseTag(t *testing.T) { in = "name=HELLO1234_*\n/,required" expectedOut = &tagType{name: "HELLO1234", required: true, defaultValue: ""} expectedErr = nil - tag, err = parseTag(in) + tag, err = parseTag(in, "") if !reflect.DeepEqual(tag, expectedOut) { t.Errorf("expected tag to be %v, but got %v", expectedOut, tag) } @@ -53,7 +35,7 @@ func TestParseTag(t *testing.T) { in = "name=HELLO1234_*\n/,default" expectedOut = &tagType{name: "HELLO1234", required: false, defaultValue: ""} expectedErr = nil - tag, err = parseTag(in) + tag, err = parseTag(in, "") if !reflect.DeepEqual(tag, expectedOut) { t.Errorf("expected tag to be %v, but got %v", expectedOut, tag) } @@ -64,7 +46,7 @@ func TestParseTag(t *testing.T) { in = "name=HELLO1234_*\n/,default=12" expectedOut = &tagType{name: "HELLO1234", required: false, defaultValue: "12"} expectedErr = nil - tag, err = parseTag(in) + tag, err = parseTag(in, "") if !reflect.DeepEqual(tag, expectedOut) { t.Errorf("expected tag to be %v, but got %v", expectedOut, tag) } @@ -75,7 +57,7 @@ func TestParseTag(t *testing.T) { in = "name=HELLO1234_*\n/,default=,required" expectedOut = &tagType{name: "HELLO1234", required: true, defaultValue: ""} expectedErr = nil - tag, err = parseTag(in) + tag, err = parseTag(in, "") if !reflect.DeepEqual(tag, expectedOut) { t.Errorf("expected tag to be %v, but got %v", expectedOut, tag) } @@ -86,7 +68,7 @@ func TestParseTag(t *testing.T) { in = "name=HELLO1234_*\n/,default=12,required" expectedOut = nil expectedErr = tagErrIncompatibleFields - tag, err = parseTag(in) + tag, err = parseTag(in, "") if !reflect.DeepEqual(tag, expectedOut) { t.Errorf("expected tag to be %v, but got %v", expectedOut, tag) }