mirror of
https://github.com/hanzoai/telemetry.git
synced 2026-10-30 20:01:49 +00:00
<!--Ex. Fixing a bug - Describe the bug and how this fixes the issue. Ex. Adding a feature - Explain what this achieves.--> #### Description Allow `configoptional.Optional` to wrap scalar values using new interfaces designed specifically to handle similar wrappers around scalar values. Follow up to https://github.com/open-telemetry/opentelemetry-collector/pull/13524. --------- Co-authored-by: Jade Guiton <[email protected]>
84 lines
2.6 KiB
Go
84 lines
2.6 KiB
Go
// Copyright The OpenTelemetry Authors
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package internal // import "go.opentelemetry.io/collector/confmap/internal"
|
|
|
|
import (
|
|
"reflect"
|
|
|
|
"github.com/go-viper/mapstructure/v2"
|
|
|
|
encoder "go.opentelemetry.io/collector/confmap/internal/mapstructure"
|
|
)
|
|
|
|
func Encode(rawVal any, set MarshalOptions) (any, error) {
|
|
enc := encoder.New(EncoderConfig(rawVal, set))
|
|
data, err := enc.Encode(rawVal)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return data, nil
|
|
}
|
|
|
|
// EncoderConfig returns a default encoder.EncoderConfig that includes
|
|
// an EncodeHook that handles both TextMarshaler and Marshaler
|
|
// interfaces.
|
|
func EncoderConfig(rawVal any, opts MarshalOptions) *encoder.EncoderConfig {
|
|
hooks := []mapstructure.DecodeHookFunc{
|
|
encoder.YamlMarshalerHookFunc(),
|
|
}
|
|
|
|
if opts.OpaqueUnredacted {
|
|
hooks = append(hooks, encoder.StringTextUnredactedHookFunc())
|
|
}
|
|
|
|
hooks = append(hooks,
|
|
encoder.TextMarshalerHookFunc(),
|
|
// This must come before unmarshalerHookFunc; the two may both want to trigger
|
|
// their corresponding interface for structs implementing both, and the scalar
|
|
// interfaces are the ones that will sometimes defer to the non-scalar interfaces.
|
|
scalarMarshalerHookFunc(),
|
|
marshalerHookFunc(rawVal),
|
|
)
|
|
|
|
return &encoder.EncoderConfig{
|
|
EncodeHook: mapstructure.ComposeDecodeHookFunc(hooks...),
|
|
}
|
|
}
|
|
|
|
// marshalerHookFunc returns a DecodeHookFuncValue that checks structs that aren't
|
|
// the original to see if they implement the Marshaler interface.
|
|
func marshalerHookFunc(orig any) mapstructure.DecodeHookFuncValue {
|
|
origType := reflect.TypeOf(orig)
|
|
return safeWrapDecodeHookFunc(func(from, _ reflect.Value) (any, error) {
|
|
if from.Kind() != reflect.Struct {
|
|
return from.Interface(), nil
|
|
}
|
|
|
|
// ignore original to avoid infinite loop.
|
|
if from.Type() == origType && reflect.DeepEqual(from.Interface(), orig) {
|
|
return from.Interface(), nil
|
|
}
|
|
marshaler, ok := from.Interface().(Marshaler)
|
|
if !ok {
|
|
return from.Interface(), nil
|
|
}
|
|
conf := NewFromStringMap(nil)
|
|
if err := marshaler.Marshal(conf); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
stringMap := conf.ToStringMap()
|
|
if stringMap == nil {
|
|
// If conf is still nil after marshaling, we want to encode it as an untyped nil
|
|
// instead of a map-typed nil. This ensures the value is a proper null value
|
|
// in the final marshaled output instead of an empty map. We hit this case
|
|
// when marshaling wrapper structs that have no direct representation
|
|
// in the marshaled output that aren't tagged with "squash" on the fields
|
|
// they're used on.
|
|
return nil, nil
|
|
}
|
|
return stringMap, nil
|
|
})
|
|
}
|