diff --git a/env.go b/env.go index 93d7a76c..7a4997ce 100644 --- a/env.go +++ b/env.go @@ -380,7 +380,12 @@ func doParseField( return nil } if refField.Kind() == reflect.Ptr && refField.Elem().Kind() == reflect.Struct && !refField.IsNil() { - return parseInternal(refField.Interface(), processField, optionsWithEnvPrefix(refTypeField, opts)) + // If the field has its own env tag, don't recurse into the struct. + // Let it fall through to setField, which handles custom parsers. + ownKey, _ := parseKeyForOption(refTypeField.Tag.Get(opts.TagName)) + if ownKey == "" && !opts.UseFieldNameByDefault { + return parseInternal(refField.Interface(), processField, optionsWithEnvPrefix(refTypeField, opts)) + } } if refField.Kind() == reflect.Struct && refField.CanAddr() && refField.Type().Name() == "" { return parseInternal(refField.Addr().Interface(), processField, optionsWithEnvPrefix(refTypeField, opts)) diff --git a/env_test.go b/env_test.go index 2fe79c31..917d135b 100644 --- a/env_test.go +++ b/env_test.go @@ -2417,3 +2417,34 @@ func TestEnvBleed(t *testing.T) { isEqual(t, "", cfg.Foo) }) } + +func TestCustomParserPointerToCustomType(t *testing.T) { + // Regression test for #385 + type foo struct { + name string + } + + t.Setenv("VAR_CUSTOM", "test") + t.Setenv("BLAH_CUSTOM", "test3") + + type config struct { + Var foo `env:"VAR_CUSTOM"` + Foo *foo `env:"BLAH_CUSTOM"` + } + + cfg := &config{ + Var: foo{"var"}, + Foo: &foo{"foo"}, + } + + err := ParseWithOptions(cfg, Options{FuncMap: map[reflect.Type]ParserFunc{ + reflect.TypeOf(foo{}): func(v string) (interface{}, error) { + return foo{name: v}, nil + }, + }}) + + isNoErr(t, err) + isEqual(t, "test", cfg.Var.name) + isTrue(t, cfg.Foo != nil) + isEqual(t, "test3", cfg.Foo.name) +}