Skip to content

Commit ffe3fe7

Browse files
authored
feat: add UnmarshalYAML to protoutil (#61)
1 parent c0c719a commit ffe3fe7

File tree

2 files changed

+81
-2
lines changed

2 files changed

+81
-2
lines changed

protoutil/conversion.go

+22-1
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,14 @@ import (
1919
"encoding/json"
2020
"fmt"
2121

22+
"google.golang.org/protobuf/encoding/protojson"
23+
"google.golang.org/protobuf/proto"
2224
"google.golang.org/protobuf/types/known/structpb"
25+
"gopkg.in/yaml.v2"
2326
)
2427

25-
// ToProtoStruct converts v, which must marshal into a JSON object, into a proto struct.
28+
// ToProtoStruct converts v, which must marshal into a JSON object, into a proto
29+
// struct.
2630
func ToProtoStruct(v any) (*structpb.Struct, error) {
2731
jb, err := json.Marshal(v)
2832
if err != nil {
@@ -34,3 +38,20 @@ func ToProtoStruct(v any) (*structpb.Struct, error) {
3438
}
3539
return x, nil
3640
}
41+
42+
// UnmarshalYAML unmarshals the give YAML bytes to the given proto message.
43+
func UnmarshalYAML(b []byte, msg proto.Message) error {
44+
tmp := map[string]any{}
45+
if err := yaml.Unmarshal(b, tmp); err != nil {
46+
return fmt.Errorf("failed to unmarshal yaml: %w", err)
47+
}
48+
jb, err := json.Marshal(tmp)
49+
if err != nil {
50+
return fmt.Errorf("failed to marshal json: %w", err)
51+
}
52+
53+
if err := protojson.Unmarshal(jb, msg); err != nil {
54+
return fmt.Errorf("failed to unmarshal proto: %w", err)
55+
}
56+
return nil
57+
}

protoutil/conversion_test.go

+59-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import (
2424
"google.golang.org/protobuf/types/known/structpb"
2525
)
2626

27-
func TestParseProject(t *testing.T) {
27+
func TestToProtoStruct(t *testing.T) {
2828
t.Parallel()
2929

3030
cases := []struct {
@@ -82,3 +82,61 @@ func TestParseProject(t *testing.T) {
8282
})
8383
}
8484
}
85+
86+
func TestUnmarshalYAML(t *testing.T) {
87+
t.Parallel()
88+
89+
cases := []struct {
90+
name string
91+
b []byte
92+
want *structpb.Struct
93+
wantErrSubstr string
94+
}{
95+
{
96+
name: "success",
97+
b: []byte(`foo: bar
98+
slice:
99+
- abc
100+
- xyz
101+
num: 1
102+
bool: true
103+
`),
104+
want: &structpb.Struct{
105+
Fields: map[string]*structpb.Value{
106+
"foo": structpb.NewStringValue("bar"),
107+
"slice": structpb.NewListValue(&structpb.ListValue{
108+
Values: []*structpb.Value{
109+
structpb.NewStringValue("abc"),
110+
structpb.NewStringValue("xyz"),
111+
},
112+
}),
113+
"num": structpb.NewNumberValue(1),
114+
"bool": structpb.NewBoolValue(true),
115+
},
116+
},
117+
},
118+
{
119+
name: "invalid_yaml_error",
120+
b: []byte("foobar: {}{}"),
121+
want: &structpb.Struct{},
122+
wantErrSubstr: "failed to unmarshal yaml",
123+
},
124+
}
125+
126+
for _, tc := range cases {
127+
tc := tc
128+
129+
t.Run(tc.name, func(t *testing.T) {
130+
t.Parallel()
131+
132+
var msg structpb.Struct
133+
err := UnmarshalYAML(tc.b, &msg)
134+
if diff := testutil.DiffErrString(err, tc.wantErrSubstr); diff != "" {
135+
t.Errorf("unexpected error: %s", diff)
136+
}
137+
if diff := cmp.Diff(tc.want, &msg, protocmp.Transform()); diff != "" {
138+
t.Errorf("UnmarshalYAML (-want,+got):\n%s", diff)
139+
}
140+
})
141+
}
142+
}

0 commit comments

Comments
 (0)