From 49a85fa966f82025092615dc3900e5592fd78d9f Mon Sep 17 00:00:00 2001 From: Dmitriy Matrenichev Date: Wed, 10 Aug 2022 17:13:11 +0300 Subject: [PATCH] chore: add support for map[string]interface{} For https://github.com/siderolabs/talos/issues/6057 Signed-off-by: Dmitriy Matrenichev --- marshal.go | 17 +++++++++++++++++ marshal_test.go | 14 ++++++++++++++ predefined_types.go | 25 +++++++++++++------------ slice_test.go | 6 ++++++ unmarshal.go | 14 +++++++++++++- 5 files changed, 63 insertions(+), 13 deletions(-) diff --git a/marshal.go b/marshal.go index 924fe4a..5bdbdd9 100644 --- a/marshal.go +++ b/marshal.go @@ -16,6 +16,7 @@ import ( "google.golang.org/protobuf/encoding/protowire" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/durationpb" + "google.golang.org/protobuf/types/known/structpb" "google.golang.org/protobuf/types/known/timestamppb" ) @@ -253,6 +254,22 @@ func (m *marshaller) encodeValue(num protowire.Number, val reflect.Value) { func (m *marshaller) tryEncodePredefined(num protowire.Number, val reflect.Value) bool { switch val.Type() { + case typeMapInterface: + v := val.Interface().(map[string]interface{}) //nolint:errcheck,forcetypeassert + + val, err := structpb.NewStruct(v) + if err != nil { + panic(fmt.Errorf("failed to create structpb.Struct: %w", err)) + } + + encoded, err := proto.Marshal(val) + if err != nil { + panic(err) + } + + putTag(m, num, protowire.BytesType) + putBytes(m, encoded) + case typeDuration: d := val.Interface().(time.Duration) //nolint:errcheck,forcetypeassert duration := durationpb.New(d) diff --git a/marshal_test.go b/marshal_test.go index 9161480..0ebde24 100644 --- a/marshal_test.go +++ b/marshal_test.go @@ -592,3 +592,17 @@ func TestIncorrectCustomEncoders(t *testing.T) { ) }) } + +func TestMarshalMapInterface(t *testing.T) { + // json decodes numeric values as float64s + // json decoder do not support slices + testEncodeDecode(OneFieldStruct[map[string]interface{}]{map[string]interface{}{ + "a": 1.0, + "b": "2", + "c": true, + "e": map[string]interface{}{ + "a": 1.0, + "g": 10.10, + }, + }})(t) +} diff --git a/predefined_types.go b/predefined_types.go index c9e6c2e..3eedfd1 100644 --- a/predefined_types.go +++ b/predefined_types.go @@ -24,18 +24,19 @@ type ( ) var ( - typeFixedU32 = typeOf[FixedU32]() - typeFixedU64 = typeOf[FixedU64]() - typeFixedS32 = typeOf[FixedS32]() - typeFixedS64 = typeOf[FixedS64]() - typeTime = typeOf[time.Time]() - typeDuration = typeOf[time.Duration]() - typeFixedU32s = reflect.SliceOf(typeFixedU32) - typeFixedU64s = reflect.SliceOf(typeFixedU64) - typeFixedS32s = reflect.SliceOf(typeFixedS32) - typeFixedS64s = reflect.SliceOf(typeFixedS64) - typeDurations = reflect.SliceOf(typeDuration) - typeByte = typeOf[byte]() + typeFixedU32 = typeOf[FixedU32]() + typeFixedU64 = typeOf[FixedU64]() + typeFixedS32 = typeOf[FixedS32]() + typeFixedS64 = typeOf[FixedS64]() + typeTime = typeOf[time.Time]() + typeDuration = typeOf[time.Duration]() + typeMapInterface = typeOf[map[string]interface{}]() + typeFixedU32s = reflect.SliceOf(typeFixedU32) + typeFixedU64s = reflect.SliceOf(typeFixedU64) + typeFixedS32s = reflect.SliceOf(typeFixedS32) + typeFixedS64s = reflect.SliceOf(typeFixedS64) + typeDurations = reflect.SliceOf(typeDuration) + typeByte = typeOf[byte]() ) func typeOf[T any]() reflect.Type { diff --git a/slice_test.go b/slice_test.go index db9fa79..4357d7e 100644 --- a/slice_test.go +++ b/slice_test.go @@ -205,6 +205,8 @@ func TestSmallIntegers(t *testing.T) { type customByte byte + type customSlice []byte + type customType struct { Int16 int16 `protobuf:"1"` Uint16 uint16 `protobuf:"3"` @@ -243,6 +245,10 @@ func TestSmallIntegers(t *testing.T) { "slice of uint16 type should be encoded in 'fixed32' form", testEncodeDecodeWrapped([]uint16{1, 0xFFFF, 3}, encodedUint16s), }, + { + "customSlice should be encoded in 'bytes' form", + testEncodeDecodeWrapped(customSlice{1, 0xFF, 3}, encodedBytes), + }, { "customType should be encoded in 'fixed32' form", testEncodeDecodeWrapped(customType{ diff --git a/unmarshal.go b/unmarshal.go index 521cf4c..a305f29 100644 --- a/unmarshal.go +++ b/unmarshal.go @@ -13,6 +13,7 @@ import ( "google.golang.org/protobuf/encoding/protowire" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/durationpb" + "google.golang.org/protobuf/types/known/structpb" "google.golang.org/protobuf/types/known/timestamppb" ) @@ -348,7 +349,7 @@ func instantiate(dst reflect.Value) error { return nil } -//nolint:cyclop +//nolint:cyclop,gocyclo func unmarshalBytes(dst reflect.Value, value complexValue) (err error) { defer func() { if err != nil { @@ -389,6 +390,17 @@ func unmarshalBytes(dst reflect.Value, value complexValue) (err error) { dst.Set(reflect.ValueOf(result.AsDuration())) + return nil + case typeMapInterface: + var result structpb.Struct + + err = proto.Unmarshal(bytes, &result) + if err != nil { + return err + } + + dst.Set(reflect.ValueOf(result.AsMap())) + return nil }