From ae1e635be2a8730972abb9d2facb5c70b5e5ce79 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 12 Aug 2024 13:15:06 +0200 Subject: [PATCH 1/2] Require map[T]encoding.TextUnmarshaler support --- env_test.go | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/env_test.go b/env_test.go index 1a567b44..60019165 100644 --- a/env_test.go +++ b/env_test.go @@ -456,6 +456,34 @@ func TestParsesEnvInvalidMap(t *testing.T) { isTrue(t, errors.Is(err, ParseError{})) } +func TestParseMapOfUnmarshalerValues(t *testing.T) { + type mapOfUnmarshaler map[string]unmarshaler + + type config struct { + Durations mapOfUnmarshaler `env:"DURATIONS"` + } + + t.Setenv("DURATIONS", "year:8760h") + + var cfg config + isNoErr(t, Parse(&cfg)) + isEqual(t, mapOfUnmarshaler{"year": {Duration: 365 * 24 * time.Hour}}, cfg.Durations) +} + +func TestParseMapOfUnmarshalerPtrs(t *testing.T) { + type mapOfUnmarshaler map[string]*unmarshaler + + type config struct { + Durations mapOfUnmarshaler `env:"DURATIONS"` + } + + t.Setenv("DURATIONS", "year:8760h") + + var cfg config + isNoErr(t, Parse(&cfg)) + isEqual(t, mapOfUnmarshaler{"year": {Duration: 365 * 24 * time.Hour}}, cfg.Durations) +} + func TestParseCustomMapType(t *testing.T) { type custommap map[string]bool From c392b72f1af161ffe8034276e90756801f30f986 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 12 Aug 2024 13:17:03 +0200 Subject: [PATCH 2/2] Support map[T]encoding.TextUnmarshaler --- env.go | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/env.go b/env.go index 27776b89..ec888176 100644 --- a/env.go +++ b/env.go @@ -699,9 +699,35 @@ func handleMap(field reflect.Value, value string, sf reflect.StructField, funcMa elemType := sf.Type.Elem() elemParserFunc, ok := funcMap[elemType] if !ok { - elemParserFunc, ok = defaultBuiltInParsers[elemType.Kind()] - if !ok { - return newNoParserError(sf) + rawType := elemType + isPtr := reflect.Ptr == rawType.Kind() + + if isPtr { + rawType = rawType.Elem() + } + + if _, ok := reflect.New(rawType).Interface().(encoding.TextUnmarshaler); ok { + if isPtr { + elemParserFunc = func(v string) (any, error) { + ptr := reflect.New(rawType) + unmarshaler := ptr.Interface().(encoding.TextUnmarshaler) + err := unmarshaler.UnmarshalText([]byte(v)) + + return unmarshaler, err + } + } else { + elemParserFunc = func(v string) (any, error) { + ptr := reflect.New(rawType) + err := ptr.Interface().(encoding.TextUnmarshaler).UnmarshalText([]byte(v)) + + return ptr.Elem().Interface(), err + } + } + } else { + elemParserFunc, ok = defaultBuiltInParsers[elemType.Kind()] + if !ok { + return newNoParserError(sf) + } } }