@@ -19,6 +19,7 @@ import (
19
19
"github.com/knadh/koanf/v2"
20
20
21
21
encoder "go.opentelemetry.io/collector/confmap/internal/mapstructure"
22
+ "go.opentelemetry.io/collector/confmap/internal/third_party/composehook"
22
23
)
23
24
24
25
const (
@@ -234,7 +235,7 @@ func decodeConfig(m *Conf, result any, errorUnused bool, skipTopLevelUnmarshaler
234
235
TagName : MapstructureTag ,
235
236
WeaklyTypedInput : false ,
236
237
MatchName : caseSensitiveMatchName ,
237
- DecodeHook : mapstructure .ComposeDecodeHookFunc (
238
+ DecodeHook : composehook .ComposeDecodeHookFunc (
238
239
useExpandValue (),
239
240
expandNilStructPointersHookFunc (),
240
241
mapstructure .StringToSliceHookFunc ("," ),
@@ -306,6 +307,23 @@ func isStringyStructure(t reflect.Type) bool {
306
307
return false
307
308
}
308
309
310
+ // safeWrapDecodeHookFunc wraps a DecodeHookFuncValue to ensure fromVal is a valid `reflect.Value`
311
+ // object and therefore it is safe to call `reflect.Value` methods on fromVal.
312
+ //
313
+ // Use this only if the hook does not need to be called on untyped nil values.
314
+ // Typed nil values are safe to call and will be passed to the hook.
315
+ // See https://github.com/golang/go/issues/51649
316
+ func safeWrapDecodeHookFunc (
317
+ f mapstructure.DecodeHookFuncValue ,
318
+ ) mapstructure.DecodeHookFuncValue {
319
+ return func (fromVal reflect.Value , toVal reflect.Value ) (any , error ) {
320
+ if ! fromVal .IsValid () {
321
+ return nil , nil
322
+ }
323
+ return f (fromVal , toVal )
324
+ }
325
+ }
326
+
309
327
// When a value has been loaded from an external source via a provider, we keep both the
310
328
// parsed value and the original string value. This allows us to expand the value to its
311
329
// original string representation when decoding into a string field, and use the original otherwise.
@@ -355,7 +373,7 @@ func useExpandValue() mapstructure.DecodeHookFuncType {
355
373
// we want an unmarshaled Config to be equivalent to
356
374
// Config{Thing: &SomeStruct{}} instead of Config{Thing: nil}
357
375
func expandNilStructPointersHookFunc () mapstructure.DecodeHookFuncValue {
358
- return func (from reflect.Value , to reflect.Value ) (any , error ) {
376
+ return safeWrapDecodeHookFunc ( func (from reflect.Value , to reflect.Value ) (any , error ) {
359
377
// ensure we are dealing with map to map comparison
360
378
if from .Kind () == reflect .Map && to .Kind () == reflect .Map {
361
379
toElem := to .Type ().Elem ()
@@ -375,7 +393,7 @@ func expandNilStructPointersHookFunc() mapstructure.DecodeHookFuncValue {
375
393
}
376
394
}
377
395
return from .Interface (), nil
378
- }
396
+ })
379
397
}
380
398
381
399
// mapKeyStringToMapKeyTextUnmarshalerHookFunc returns a DecodeHookFuncType that checks that a conversion from
@@ -422,7 +440,7 @@ func mapKeyStringToMapKeyTextUnmarshalerHookFunc() mapstructure.DecodeHookFuncTy
422
440
// unmarshalerEmbeddedStructsHookFunc provides a mechanism for embedded structs to define their own unmarshal logic,
423
441
// by implementing the Unmarshaler interface.
424
442
func unmarshalerEmbeddedStructsHookFunc () mapstructure.DecodeHookFuncValue {
425
- return func (from reflect.Value , to reflect.Value ) (any , error ) {
443
+ return safeWrapDecodeHookFunc ( func (from reflect.Value , to reflect.Value ) (any , error ) {
426
444
if to .Type ().Kind () != reflect .Struct {
427
445
return from .Interface (), nil
428
446
}
@@ -455,14 +473,14 @@ func unmarshalerEmbeddedStructsHookFunc() mapstructure.DecodeHookFuncValue {
455
473
}
456
474
}
457
475
return fromAsMap , nil
458
- }
476
+ })
459
477
}
460
478
461
479
// Provides a mechanism for individual structs to define their own unmarshal logic,
462
480
// by implementing the Unmarshaler interface, unless skipTopLevelUnmarshaler is
463
481
// true and the struct matches the top level object being unmarshaled.
464
482
func unmarshalerHookFunc (result any , skipTopLevelUnmarshaler bool ) mapstructure.DecodeHookFuncValue {
465
- return func (from reflect.Value , to reflect.Value ) (any , error ) {
483
+ return safeWrapDecodeHookFunc ( func (from reflect.Value , to reflect.Value ) (any , error ) {
466
484
if ! to .CanAddr () {
467
485
return from .Interface (), nil
468
486
}
@@ -495,14 +513,14 @@ func unmarshalerHookFunc(result any, skipTopLevelUnmarshaler bool) mapstructure.
495
513
}
496
514
497
515
return unmarshaler , nil
498
- }
516
+ })
499
517
}
500
518
501
519
// marshalerHookFunc returns a DecodeHookFuncValue that checks structs that aren't
502
520
// the original to see if they implement the Marshaler interface.
503
521
func marshalerHookFunc (orig any ) mapstructure.DecodeHookFuncValue {
504
522
origType := reflect .TypeOf (orig )
505
- return func (from reflect.Value , _ reflect.Value ) (any , error ) {
523
+ return safeWrapDecodeHookFunc ( func (from reflect.Value , _ reflect.Value ) (any , error ) {
506
524
if from .Kind () != reflect .Struct {
507
525
return from .Interface (), nil
508
526
}
@@ -520,7 +538,7 @@ func marshalerHookFunc(orig any) mapstructure.DecodeHookFuncValue {
520
538
return nil , err
521
539
}
522
540
return conf .ToStringMap (), nil
523
- }
541
+ })
524
542
}
525
543
526
544
// Unmarshaler interface may be implemented by types to customize their behavior when being unmarshaled from a Conf.
@@ -562,7 +580,7 @@ type Marshaler interface {
562
580
// 4. configuration have no `keys` field specified, the output should be default config
563
581
// - for example, input is {}, then output is Config{ Keys: ["a", "b"]}
564
582
func zeroSliceHookFunc () mapstructure.DecodeHookFuncValue {
565
- return func (from reflect.Value , to reflect.Value ) (any , error ) {
583
+ return safeWrapDecodeHookFunc ( func (from reflect.Value , to reflect.Value ) (any , error ) {
566
584
if to .CanSet () && to .Kind () == reflect .Slice && from .Kind () == reflect .Slice {
567
585
if from .IsNil () {
568
586
// input slice is nil, set output slice to nil.
@@ -574,7 +592,7 @@ func zeroSliceHookFunc() mapstructure.DecodeHookFuncValue {
574
592
}
575
593
576
594
return from .Interface (), nil
577
- }
595
+ })
578
596
}
579
597
580
598
type moduleFactory [T any , S any ] interface {
0 commit comments