diff --git a/envconfig.go b/envconfig.go index 9ece5a0..c9dfbfe 100644 --- a/envconfig.go +++ b/envconfig.go @@ -192,6 +192,19 @@ func (p *prefixLookuper) Key(key string) string { return p.prefix + key } +func (p *prefixLookuper) Unwrap() Lookuper { + l := p.l + for v, ok := l.(UnwrappableLookuper); ok; { + l = v.Unwrap() + } + return l +} + +// UnwrappableLookuper is a lookuper that can return the underlying lookuper. +type UnwrappableLookuper interface { + Unwrap() Lookuper +} + // MultiLookuper wraps a collection of lookupers. It does not combine them, and // lookups appear in the order in which they are provided to the initializer. func MultiLookuper(lookupers ...Lookuper) Lookuper { @@ -602,9 +615,14 @@ func lookup(key string, required bool, defaultValue string, l Lookuper) (string, if defaultValue != "" { // Expand the default value. This allows for a default value that maps to - // a different variable. + // a different environment variable. val = os.Expand(defaultValue, func(i string) string { - s, ok := l.Lookup(i) + lookuper := l + if v, ok := lookuper.(UnwrappableLookuper); ok { + lookuper = v.Unwrap() + } + + s, ok := lookuper.Lookup(i) if ok { return s } diff --git a/envconfig_test.go b/envconfig_test.go index 9f6532a..22c92fe 100644 --- a/envconfig_test.go +++ b/envconfig_test.go @@ -1234,6 +1234,44 @@ func TestProcessWith(t *testing.T) { }, lookuper: MapLookuper(nil), }, + { + name: "default/expand_prefix", + target: &struct { + Field string `env:"FIELD,default=$DEFAULT"` + }{}, + exp: &struct { + Field string `env:"FIELD,default=$DEFAULT"` + }{ + Field: "value", + }, + lookuper: PrefixLookuper("PREFIX_", MapLookuper(map[string]string{ + // Ensure that we use the underlying MapLookuper instead of the prefixed + // value when resolving a default: + // + // https://github.com/sethvargo/go-envconfig/issues/85 + // + "DEFAULT": "value", + })), + }, + { + name: "default/expand_prefix_prefix", + target: &struct { + Field string `env:"FIELD,default=$DEFAULT"` + }{}, + exp: &struct { + Field string `env:"FIELD,default=$DEFAULT"` + }{ + Field: "value", + }, + lookuper: PrefixLookuper("OUTER_", PrefixLookuper("INNER_", MapLookuper(map[string]string{ + // Ensure that we use the underlying MapLookuper instead of the prefixed + // value when resolving a default: + // + // https://github.com/sethvargo/go-envconfig/issues/85 + // + "DEFAULT": "value", + }))), + }, { name: "default/slice", target: &struct {