@@ -16,6 +16,7 @@ package config // import "go.opentelemetry.io/collector/config"
16
16
17
17
import (
18
18
"context"
19
+ "encoding"
19
20
"fmt"
20
21
"reflect"
21
22
@@ -139,23 +140,15 @@ func decoderConfig(result interface{}) *mapstructure.DecoderConfig {
139
140
TagName : "mapstructure" ,
140
141
WeaklyTypedInput : true ,
141
142
DecodeHook : mapstructure .ComposeDecodeHookFunc (
142
- expandNilStructPointersFunc ,
143
- stringToSliceHookFunc ,
144
- mapStringToMapComponentIDHookFunc ,
145
- stringToTimeDurationHookFunc ,
146
- textUnmarshallerHookFunc ,
143
+ expandNilStructPointersHookFunc () ,
144
+ mapstructure . StringToSliceHookFunc ( "," ) ,
145
+ mapKeyStringToMapKeyTextUnmarshalerHookFunc () ,
146
+ mapstructure . StringToTimeDurationHookFunc () ,
147
+ mapstructure . TextUnmarshallerHookFunc () ,
147
148
),
148
149
}
149
150
}
150
151
151
- var (
152
- stringToSliceHookFunc = mapstructure .StringToSliceHookFunc ("," )
153
- stringToTimeDurationHookFunc = mapstructure .StringToTimeDurationHookFunc ()
154
- textUnmarshallerHookFunc = mapstructure .TextUnmarshallerHookFunc ()
155
-
156
- componentIDType = reflect .TypeOf (NewComponentID ("foo" ))
157
- )
158
-
159
152
// In cases where a config has a mapping of something to a struct pointers
160
153
// we want nil values to resolve to a pointer to the zero value of the
161
154
// underlying struct just as we want nil values of a mapping of something
@@ -170,51 +163,62 @@ var (
170
163
//
171
164
// we want an unmarshaled Config to be equivalent to
172
165
// Config{Thing: &SomeStruct{}} instead of Config{Thing: nil}
173
- var expandNilStructPointersFunc = func (from reflect.Value , to reflect.Value ) (interface {}, error ) {
174
- // ensure we are dealing with map to map comparison
175
- if from .Kind () == reflect .Map && to .Kind () == reflect .Map {
176
- toElem := to .Type ().Elem ()
177
- // ensure that map values are pointers to a struct
178
- // (that may be nil and require manual setting w/ zero value)
179
- if toElem .Kind () == reflect .Ptr && toElem .Elem ().Kind () == reflect .Struct {
180
- fromRange := from .MapRange ()
181
- for fromRange .Next () {
182
- fromKey := fromRange .Key ()
183
- fromValue := fromRange .Value ()
184
- // ensure that we've run into a nil pointer instance
185
- if fromValue .IsNil () {
186
- newFromValue := reflect .New (toElem .Elem ())
187
- from .SetMapIndex (fromKey , newFromValue )
166
+ func expandNilStructPointersHookFunc () mapstructure.DecodeHookFuncValue {
167
+ return func (from reflect.Value , to reflect.Value ) (interface {}, error ) {
168
+ // ensure we are dealing with map to map comparison
169
+ if from .Kind () == reflect .Map && to .Kind () == reflect .Map {
170
+ toElem := to .Type ().Elem ()
171
+ // ensure that map values are pointers to a struct
172
+ // (that may be nil and require manual setting w/ zero value)
173
+ if toElem .Kind () == reflect .Ptr && toElem .Elem ().Kind () == reflect .Struct {
174
+ fromRange := from .MapRange ()
175
+ for fromRange .Next () {
176
+ fromKey := fromRange .Key ()
177
+ fromValue := fromRange .Value ()
178
+ // ensure that we've run into a nil pointer instance
179
+ if fromValue .IsNil () {
180
+ newFromValue := reflect .New (toElem .Elem ())
181
+ from .SetMapIndex (fromKey , newFromValue )
182
+ }
188
183
}
189
184
}
190
185
}
186
+ return from .Interface (), nil
191
187
}
192
- return from .Interface (), nil
193
188
}
194
189
195
- // mapStringToMapComponentIDHookFunc returns a DecodeHookFunc that converts a map[string]interface{} to
196
- // map[ComponentID]interface{}.
197
- // This is needed in combination since the ComponentID.UnmarshalText may produce equal IDs for different strings,
190
+ // mapKeyStringToMapKeyTextUnmarshalerHookFunc returns a DecodeHookFuncType that checks that a conversion from
191
+ // map[string]interface{} to map[encoding.TextUnmarshaler]interface{} does not overwrite keys,
192
+ // when UnmarshalText produces equal elements from different strings (e.g. trims whitespaces).
193
+ //
194
+ // This is needed in combination with ComponentID, which may produce equal IDs for different strings,
198
195
// and an error needs to be returned in that case, otherwise the last equivalent ID overwrites the previous one.
199
- var mapStringToMapComponentIDHookFunc = func (f reflect.Type , t reflect.Type , data interface {}) (interface {}, error ) {
200
- if f .Kind () != reflect .Map || f .Key ().Kind () != reflect .String {
201
- return data , nil
202
- }
196
+ func mapKeyStringToMapKeyTextUnmarshalerHookFunc () mapstructure.DecodeHookFuncType {
197
+ return func (f reflect.Type , t reflect.Type , data interface {}) (interface {}, error ) {
198
+ if f .Kind () != reflect .Map || f .Key ().Kind () != reflect .String {
199
+ return data , nil
200
+ }
203
201
204
- if t .Kind () != reflect .Map || t . Key () != componentIDType {
205
- return data , nil
206
- }
202
+ if t .Kind () != reflect .Map {
203
+ return data , nil
204
+ }
207
205
208
- m := make (map [ComponentID ]interface {})
209
- for k , v := range data .(map [string ]interface {}) {
210
- id , err := NewComponentIDFromString (k )
211
- if err != nil {
212
- return nil , err
206
+ if _ , ok := reflect .New (t .Key ()).Interface ().(encoding.TextUnmarshaler ); ! ok {
207
+ return data , nil
213
208
}
214
- if _ , ok := m [id ]; ok {
215
- return nil , fmt .Errorf ("duplicate name %q after trimming spaces %v" , k , id )
209
+
210
+ m := reflect .MakeMap (reflect .MapOf (t .Key (), reflect .TypeOf (true )))
211
+ for k := range data .(map [string ]interface {}) {
212
+ tKey := reflect .New (t .Key ())
213
+ if err := tKey .Interface ().(encoding.TextUnmarshaler ).UnmarshalText ([]byte (k )); err != nil {
214
+ return nil , err
215
+ }
216
+
217
+ if m .MapIndex (reflect .Indirect (tKey )).IsValid () {
218
+ return nil , fmt .Errorf ("duplicate name %q after trimming spaces %v" , k , tKey )
219
+ }
220
+ m .SetMapIndex (reflect .Indirect (tKey ), reflect .ValueOf (true ))
216
221
}
217
- m [ id ] = v
222
+ return data , nil
218
223
}
219
- return m , nil
220
224
}
0 commit comments