Skip to content

Commit 308b5ca

Browse files
committed
Use confmap facilities for marshaling and unmarshaling
1 parent 27da57d commit 308b5ca

File tree

5 files changed

+59
-61
lines changed

5 files changed

+59
-61
lines changed

confmap/xconfmap/scalarmarshaler.go

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
package xconfmap
55

66
import (
7-
"encoding"
87
"reflect"
98

109
"github.com/go-viper/mapstructure/v2"
@@ -39,21 +38,10 @@ func scalarmarshalerHookFunc() mapstructure.DecodeHookFuncValue {
3938
}
4039

4140
v, err := marshaler.GetScalarValue()
42-
4341
if err != nil {
4442
return nil, err
4543
}
4644

47-
if tm, ok := v.(encoding.TextMarshaler); ok {
48-
b, err := tm.MarshalText()
49-
50-
if err != nil {
51-
return nil, err
52-
}
53-
54-
return string(b), nil
55-
}
56-
5745
return v, nil
5846
})
5947
}

confmap/xconfmap/scalarmarshaler_test.go

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,30 @@ type NonImplWrapperType[T any] struct {
5050
inner T `mapstructure:"-"`
5151
}
5252

53+
var _ confmap.Unmarshaler = (*wrapperType[any])(nil)
5354
var _ ScalarMarshaler = wrapperType[any]{}
5455
var _ ScalarUnmarshaler = (*wrapperType[any])(nil)
5556

5657
type wrapperType[T any] struct {
5758
inner T `mapstructure:"-"`
5859
}
5960

61+
func (wt *wrapperType[T]) Unmarshal(conf *confmap.Conf) error {
62+
if err := conf.Unmarshal(&wt.inner, WithScalarUnmarshaler()); err != nil {
63+
return err
64+
}
65+
66+
return nil
67+
}
68+
69+
func (wt wrapperType[T]) Marshal(conf *confmap.Conf) error {
70+
if err := conf.Marshal(wt.inner, WithScalarMarshaler()); err != nil {
71+
return fmt.Errorf("failed to marshal wrapperType value: %w", err)
72+
}
73+
74+
return nil
75+
}
76+
6077
func (wt wrapperType[T]) MarshalScalar(in *string) (any, error) {
6178
if in != nil {
6279
return *in, nil
@@ -65,15 +82,15 @@ func (wt wrapperType[T]) MarshalScalar(in *string) (any, error) {
6582
return wt.inner, nil
6683
}
6784

68-
func (wt wrapperType[T]) GetScalarValue() any {
69-
return wt.inner
85+
func (wt wrapperType[T]) GetScalarValue() (any, error) {
86+
return wt.inner, nil
7087
}
7188

7289
func (wt *wrapperType[T]) UnmarshalScalar(val any) error {
7390
v, ok := val.(T)
7491

7592
if !ok {
76-
return fmt.Errorf("val is %T, not %T", val, v)
93+
return fmt.Errorf("could not unmarshal scalar: val is %T, not %T", val, v)
7794
}
7895

7996
wt.inner = v
@@ -86,16 +103,17 @@ func (wt *wrapperType[T]) ScalarType() any {
86103

87104
type testConfig struct {
88105
// Handled by confmap, treated as string
89-
Tma textMarshalerAlias `mapstructure:"tma"`
90-
Ntma nonTextMarshalerAlias `mapstructure:"ntma"`
91-
Nonimplint NonImplWrapperType[int] `mapstructure:"non_impl_int"`
92-
Nonimplstr NonImplWrapperType[string] `mapstructure:"non_impl_str"`
93-
Nonimpltms NonImplWrapperType[textMarshalerStruct] `mapstructure:"non_impl_tms"`
94-
Nonimplntms NonImplWrapperType[nonTextMarshalerStruct] `mapstructure:"non_impl_ntms"`
95-
Implint wrapperType[int] `mapstructure:"impl_int"`
96-
Implstr wrapperType[string] `mapstructure:"impl_str"`
97-
Impltms wrapperType[textMarshalerStruct] `mapstructure:"impl_tms"`
98-
Implntms wrapperType[nonTextMarshalerStruct] `mapstructure:"impl_ntms"`
106+
Tma textMarshalerAlias `mapstructure:"tma"`
107+
Ntma nonTextMarshalerAlias `mapstructure:"ntma"`
108+
Nonimplint NonImplWrapperType[int] `mapstructure:"non_impl_int"`
109+
Nonimplstr NonImplWrapperType[string] `mapstructure:"non_impl_str"`
110+
Nonimpltms NonImplWrapperType[textMarshalerStruct] `mapstructure:"non_impl_tms"`
111+
Nonimplntms NonImplWrapperType[nonTextMarshalerStruct] `mapstructure:"non_impl_ntms"`
112+
Implint wrapperType[int] `mapstructure:"impl_int"`
113+
Implstr wrapperType[string] `mapstructure:"impl_str"`
114+
Impltms wrapperType[textMarshalerStruct] `mapstructure:"impl_tms"`
115+
Implntms wrapperType[nonTextMarshalerStruct] `mapstructure:"impl_ntms"`
116+
Recursive wrapperType[wrapperType[textMarshalerStruct]] `mapstructure:"recursive"`
99117
}
100118

101119
func (cfg *testConfig) Unmarshal(conf *confmap.Conf) error {
@@ -125,6 +143,7 @@ func TestMarshalConfig(t *testing.T) {
125143
Implstr: wrapperType[string]{inner: "test"},
126144
Impltms: wrapperType[textMarshalerStruct]{inner: textMarshalerStruct{id: 0, data: []byte{80}}},
127145
Implntms: wrapperType[nonTextMarshalerStruct]{inner: nonTextMarshalerStruct{id: 2, data: []byte{80}}},
146+
Recursive: wrapperType[wrapperType[textMarshalerStruct]]{inner: wrapperType[textMarshalerStruct]{inner: textMarshalerStruct{id: 2, data: []byte{80}}}},
128147
}
129148

130149
require.NoError(t, conf.Marshal(cfg, WithScalarMarshaler()))

confmap/xconfmap/scalarunmarshaler.go

Lines changed: 14 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
package xconfmap
55

66
import (
7-
"encoding"
87
"reflect"
98

109
"github.com/go-viper/mapstructure/v2"
@@ -15,7 +14,7 @@ import (
1514

1615
func WithScalarUnmarshaler() confmap.UnmarshalOption {
1716
return internal.UnmarshalOptionFunc(func(uo *internal.UnmarshalOptions) {
18-
uo.AdditionalDecodeHookFuncs = append(uo.AdditionalDecodeHookFuncs, scalarunmarshalerHookFunc())
17+
uo.AdditionalDecodeHookFuncs = append(uo.AdditionalDecodeHookFuncs, scalarunmarshalerHookFunc(*uo))
1918
})
2019
}
2120

@@ -33,39 +32,33 @@ type ScalarUnmarshaler interface {
3332
// Provides a mechanism for individual structs to define their own unmarshal logic,
3433
// by implementing the Unmarshaler interface, unless skipTopLevelUnmarshaler is
3534
// true and the struct matches the top level object being unmarshaled.
36-
func scalarunmarshalerHookFunc() mapstructure.DecodeHookFuncValue {
35+
func scalarunmarshalerHookFunc(opts internal.UnmarshalOptions) mapstructure.DecodeHookFuncValue {
3736
return safeWrapDecodeHookFunc(func(from reflect.Value, to reflect.Value) (any, error) {
37+
opts.AdditionalDecodeHookFuncs = append(opts.AdditionalDecodeHookFuncs, scalarunmarshalerHookFunc(opts))
38+
3839
if !to.CanAddr() {
3940
return from.Interface(), nil
4041
}
4142

43+
if from.Kind() == reflect.Struct ||
44+
from.Kind() == reflect.Pointer && from.Elem().Kind() == reflect.Struct {
45+
return from.Interface(), nil
46+
}
47+
4248
toPtr := to.Addr().Interface()
4349

4450
unmarshaler, ok := toPtr.(ScalarUnmarshaler)
4551
if !ok {
4652
return from.Interface(), nil
4753
}
4854

49-
var v any
50-
tp := reflect.New(reflect.TypeOf(unmarshaler.ScalarType()))
51-
if tu, ok := tp.Interface().(encoding.TextUnmarshaler); ok {
52-
// Should we error out here?
53-
if str, ok := from.Interface().(string); ok {
54-
if err := tu.UnmarshalText([]byte(str)); err != nil {
55-
return nil, err
56-
}
57-
v = tp.Elem().Interface()
58-
}
59-
} else if from.CanConvert(tp.Elem().Type()) {
60-
from.Convert(tp.Elem().Type())
61-
v = from.Interface()
62-
} else if _, ok := from.Interface().(map[string]any); ok {
63-
v = tp.Elem().Interface()
64-
} else {
65-
v = from.Interface()
55+
resultVal := reflect.New(reflect.TypeOf(unmarshaler.ScalarType()))
56+
57+
if err := internal.Decode(from.Interface(), resultVal.Interface(), opts, false); err != nil {
58+
return nil, err
6659
}
6760

68-
if err := unmarshaler.UnmarshalScalar(v); err != nil {
61+
if err := unmarshaler.UnmarshalScalar(resultVal.Elem().Interface()); err != nil {
6962
return nil, err
7063
}
7164

confmap/xconfmap/scalarunmarshaler_test.go

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,26 +8,23 @@ import (
88
"testing"
99

1010
"github.com/stretchr/testify/require"
11-
"go.opentelemetry.io/collector/confmap"
1211
"go.opentelemetry.io/collector/confmap/confmaptest"
1312
)
1413

1514
func TestUnmarshalConfig(t *testing.T) {
15+
wantCfg := &testConfig{
16+
Tma: textMarshalerAlias("test"),
17+
Ntma: nonTextMarshalerAlias("test"),
18+
Implint: wrapperType[int]{inner: 1},
19+
Implstr: wrapperType[string]{inner: "test"},
20+
Impltms: wrapperType[textMarshalerStruct]{inner: textMarshalerStruct{id: 0, data: []byte{80}}},
21+
Recursive: wrapperType[wrapperType[textMarshalerStruct]]{inner: wrapperType[textMarshalerStruct]{inner: textMarshalerStruct{id: 0, data: []byte{80}}}},
22+
}
23+
1624
cm, err := confmaptest.LoadConf(filepath.Join("testdata", "config.yaml"))
1725
require.NoError(t, err)
18-
wantCfg := &testConfig{}
19-
require.NoError(t, cm.Unmarshal(wantCfg, WithScalarUnmarshaler()))
20-
require.NoError(t, cm.Marshal(wantCfg, WithScalarMarshaler()))
21-
22-
conf := confmap.New()
23-
cfg := &testConfig{
24-
Tma: textMarshalerAlias("test"),
25-
Ntma: nonTextMarshalerAlias("test"),
26-
Implint: wrapperType[int]{inner: 1},
27-
Implstr: wrapperType[string]{inner: "test"},
28-
Impltms: wrapperType[textMarshalerStruct]{inner: textMarshalerStruct{id: 0, data: []byte{80}}},
29-
}
26+
cfg := &testConfig{}
27+
require.NoError(t, cm.Unmarshal(cfg, WithScalarUnmarshaler()))
3028

31-
require.NoError(t, conf.Unmarshal(cfg))
3229
require.Equal(t, wantCfg, cfg)
3330
}

confmap/xconfmap/testdata/config.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ non_impl_ntms: {}
77
non_impl_str: {}
88
non_impl_tms: {}
99
ntma: test
10-
tma: test
10+
tma: test
11+
recursive: P

0 commit comments

Comments
 (0)