@@ -111,21 +111,36 @@ func (l *Conf) unsanitizedGet(key string) any {
111111 return l .k .Get (key )
112112}
113113
114+ // sanitize recursively removes expandedValue references from the given data.
115+ // It uses the expandedValue.Value field to replace the expandedValue references.
114116func sanitize (a any ) any {
117+ return sanitizeExpanded (a , false )
118+ }
119+
120+ // sanitizeToStringMap recursively removes expandedValue references from the given data.
121+ // It uses the expandedValue.Original field to replace the expandedValue references.
122+ func sanitizeToStr (a any ) any {
123+ return sanitizeExpanded (a , true )
124+ }
125+
126+ func sanitizeExpanded (a any , useOriginal bool ) any {
115127 switch m := a .(type ) {
116128 case map [string ]any :
117129 c := maps .Copy (m )
118130 for k , v := range m {
119- c [k ] = sanitize ( v )
131+ c [k ] = sanitizeExpanded ( v , useOriginal )
120132 }
121133 return c
122134 case []any :
123135 var newSlice []any
124136 for _ , e := range m {
125- newSlice = append (newSlice , sanitize ( e ))
137+ newSlice = append (newSlice , sanitizeExpanded ( e , useOriginal ))
126138 }
127139 return newSlice
128140 case expandedValue :
141+ if useOriginal {
142+ return m .Original
143+ }
129144 return m .Value
130145 }
131146 return a
@@ -134,7 +149,7 @@ func sanitize(a any) any {
134149// Get can retrieve any value given the key to use.
135150func (l * Conf ) Get (key string ) any {
136151 val := l .unsanitizedGet (key )
137- return sanitize (val )
152+ return sanitizeExpanded (val , false )
138153}
139154
140155// IsSet checks to see if the key has been set in any of the data locations.
@@ -244,6 +259,21 @@ func castTo(exp expandedValue, useOriginal bool) (any, error) {
244259 return exp .Value , nil
245260}
246261
262+ // Check if a reflect.Type is of the form T:
263+ // T = string | map[string]T | []T | [n]T
264+ func isStringyStructure (t reflect.Type ) bool {
265+ if t .Kind () == reflect .String {
266+ return true
267+ }
268+ if t .Kind () == reflect .Map {
269+ return t .Key ().Kind () == reflect .String && isStringyStructure (t .Elem ())
270+ }
271+ if t .Kind () == reflect .Slice || t .Kind () == reflect .Array {
272+ return isStringyStructure (t .Elem ())
273+ }
274+ return false
275+ }
276+
247277// When a value has been loaded from an external source via a provider, we keep both the
248278// parsed value and the original string value. This allows us to expand the value to its
249279// original string representation when decoding into a string field, and use the original otherwise.
@@ -256,10 +286,13 @@ func useExpandValue() mapstructure.DecodeHookFuncType {
256286 return castTo (exp , to .Kind () == reflect .String )
257287 }
258288
259- // If the target field is a map or slice, sanitize input to remove expandedValue references.
260289 switch to .Kind () {
261290 case reflect .Array , reflect .Slice , reflect .Map :
262- // This does not handle map[string]string and []string explicitly.
291+ if isStringyStructure (to ) {
292+ // If the target field is a stringy structure, sanitize to use the original string value everywhere.
293+ return sanitizeToStr (data ), nil
294+ }
295+ // Otherwise, sanitize to use the parsed value everywhere.
263296 return sanitize (data ), nil
264297 }
265298 return data , nil
0 commit comments