common/helpers: make some mapstructure hooks work with embedded structs

When using `mapstructure:",squash"`, most structure-specific hook don't
dive into the structure as they are provided with the parent structure.
Add an helper to make them work on the embedded structure as well and
use it for the generic "deprecated fields" hook, but also for the hook
for the common Kafka configuration.

This is a bit brittle. There are other use cases, but they may not need
this change.
This commit is contained in:
Vincent Bernat
2025-07-20 12:35:04 +02:00
parent 756e4a8fbd
commit 76151bea66
6 changed files with 85 additions and 5 deletions

View File

@@ -24,11 +24,11 @@ func RegisterMapstructureUnmarshallerHook(hook mapstructure.DecodeHookFunc) {
// RegisterMapstructureDeprecatedFields registers a decoder hook removing
// deprecated fields. This should only be done during init.
func RegisterMapstructureDeprecatedFields[V any](fieldNames ...string) {
RegisterMapstructureUnmarshallerHook(func(from, to reflect.Value) (interface{}, error) {
RegisterMapstructureUnmarshallerHook(func(from, to reflect.Value) (any, error) {
var zeroV V
from = ElemOrIdentity(from)
to = ElemOrIdentity(to)
if to.Type() != reflect.TypeOf(zeroV) {
if !SameTypeOrSuperset(to.Type(), reflect.TypeOf(zeroV)) {
return from.Interface(), nil
}
if from.Kind() != reflect.Map {
@@ -54,6 +54,24 @@ func RegisterMapstructureDeprecatedFields[V any](fieldNames ...string) {
})
}
// SameTypeOrSuperset returns true if "input" and "ref" type are the same or
// when "input" has "ref" as a squashed field.
func SameTypeOrSuperset(input, ref reflect.Type) bool {
if input == ref {
return true
}
if input.Kind() != reflect.Struct {
return false
}
for i := 0; i < input.NumField(); i++ {
field := input.Field(i)
if tag := field.Tag.Get("mapstructure"); tag == ",squash" && field.Type == ref {
return true
}
}
return false
}
// GetMapStructureDecoderConfig returns a decoder config for
// mapstructure with all registered hooks as well as appropriate
// default configuration.