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) + } } } 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