Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -610,6 +610,10 @@ func (u *unmarshaler) unmarshalField(node *yaml.Node, field protoreflect.FieldDe
}
}

if node.Kind == yaml.AliasNode {
node = node.Alias
}

switch {
case field.IsList():
u.unmarshalList(node, field, message.ProtoReflect().Mutable(field).List())
Expand All @@ -626,6 +630,10 @@ func (u *unmarshaler) unmarshalField(node *yaml.Node, field protoreflect.FieldDe

// Unmarshal the list, with explicit handling for lists of messages.
func (u *unmarshaler) unmarshalList(node *yaml.Node, field protoreflect.FieldDescriptor, list protoreflect.List) {
// Could be an empty list
if isNull(node) {
return
}
if u.checkKind(node, yaml.SequenceNode) {
switch field.Kind() {
case protoreflect.MessageKind, protoreflect.GroupKind:
Expand All @@ -636,6 +644,9 @@ func (u *unmarshaler) unmarshalList(node *yaml.Node, field protoreflect.FieldDes
}
default:
for _, itemNode := range node.Content {
if itemNode.Kind == yaml.AliasNode {
itemNode = itemNode.Alias
}
val, ok := u.unmarshalScalar(itemNode, field, false)
if !ok {
continue
Expand All @@ -656,6 +667,14 @@ func (u *unmarshaler) unmarshalMap(node *yaml.Node, field protoreflect.FieldDesc
for i := 1; i < len(node.Content); i += 2 {
keyNode := node.Content[i-1]
valueNode := node.Content[i]
// Unfold any merge
if isMerge(keyNode) {
if valueNode.Kind == yaml.AliasNode {
valueNode = valueNode.Alias
}
u.unmarshalMap(valueNode, field, mapVal)
continue
}
mapKey, ok := u.unmarshalScalar(keyNode, mapKeyField, true)
if !ok {
continue
Expand All @@ -679,6 +698,10 @@ func isNull(node *yaml.Node) bool {
return node.Tag == "!!null"
}

func isMerge(node *yaml.Node) bool {
return node.Tag == "!!merge"
}

// Resolve the node to be used with the custom unmarshaler. Returns nil if the
// there was an error.
func (u *unmarshaler) findNodeForCustom(node *yaml.Node, forAny bool) *yaml.Node {
Expand Down Expand Up @@ -729,6 +752,9 @@ func (u *unmarshaler) unmarshalMessage(node *yaml.Node, message proto.Message, f
if isNull(node) {
return // Null is always allowed for messages
}
if node.Kind == yaml.AliasNode {
node = node.Alias
}
if node.Kind != yaml.MappingNode {
u.addErrorf(node, "expected fields for %v, got %v",
message.ProtoReflect().Descriptor().FullName(), getNodeKind(node.Kind))
Expand All @@ -745,6 +771,15 @@ func (u *unmarshaler) unmarshalMessageFields(node *yaml.Node, message proto.Mess
var key string
switch keyNode.Kind {
case yaml.ScalarNode:
// Unfold if it's a merge node
if isMerge(keyNode) {
valueNode := node.Content[i+1]
if valueNode.Kind == yaml.AliasNode {
valueNode = valueNode.Alias
}
u.unmarshalMessageFields(valueNode, message, forAny)
continue
}
key = keyNode.Value
case yaml.SequenceNode:
// Interpret single element sequences as extension field.
Expand Down Expand Up @@ -1006,6 +1041,10 @@ func (u *unmarshaler) unmarshalValue(
node *yaml.Node,
value *structpb.Value,
) {
// If the node is an alias, use the real node
if node.Kind == yaml.AliasNode {
node = node.Alias
}
// Unmarshal the value.
switch node.Kind {
case yaml.SequenceNode: // A list.
Expand Down
107 changes: 107 additions & 0 deletions decode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -214,3 +214,110 @@ values:
require.NoError(t, err)
require.Equal(t, "hi", actual.GetValues()[0].GetOneofStringValue())
}

func TestEmptySequence(t *testing.T) {
data := []byte(`
sequence: `)
actual := &testv1.EdgeTestSequence{}
err := Unmarshal(data, actual)
require.NoError(t, err)
require.Empty(t, actual.Sequence)
}

func TestAliasScalar(t *testing.T) {
data := []byte(`
foo: &alias value
bar: *alias`)
actual := &testv1.EdgeTestTwoScalar{}
err := Unmarshal(data, actual)
require.NoError(t, err)
require.Equal(t, "value", actual.Foo)
require.Equal(t, "value", actual.Bar)
}

func TestAliasSequenceItem(t *testing.T) {
data := []byte(`
foo:
- &alias item
bar:
- *alias`)
actual := &testv1.EdgeTestTwoSequence{}
err := Unmarshal(data, actual)
require.NoError(t, err)
require.Len(t, actual.Foo, 1)
require.Equal(t, "item", actual.Foo[0])
require.Len(t, actual.Bar, 1)
require.Equal(t, "item", actual.Bar[0])
}

func TestAliasSequenceMessageItem(t *testing.T) {
data := []byte(`
nested:
- &alias
foo: value
bar: value
second_nested:
- *alias`)
actual := &testv1.EdgeTestNestedMessageSequence{}
err := Unmarshal(data, actual)
require.NoError(t, err)
require.Len(t, actual.Nested, 1)
require.Equal(t, "value", actual.Nested[0].Foo)
require.Equal(t, "value", actual.Nested[0].Bar)
require.Len(t, actual.SecondNested, 1)
require.Equal(t, "value", actual.SecondNested[0].Foo)
require.Equal(t, "value", actual.SecondNested[0].Bar)
}

func TestAliasMap(t *testing.T) {
data := []byte(`
foo: &alias
key1: value
key2: value
bar: *alias
`)
actual := &testv1.EdgeTestTwoMap{}
err := Unmarshal(data, actual)
require.NoError(t, err)
require.Len(t, actual.Foo, 2)
require.Equal(t, "value", actual.Foo["key1"])
require.Equal(t, "value", actual.Foo["key2"])
require.Len(t, actual.Bar, 2)
require.Equal(t, "value", actual.Bar["key1"])
require.Equal(t, "value", actual.Bar["key2"])
}

func TestMergeMap(t *testing.T) {
data := []byte(`
foo: &alias
key1: value
key2: value
bar:
<< : *alias
key3: value
key4: value
`)
actual := &testv1.EdgeTestTwoMap{}
err := Unmarshal(data, actual)
require.NoError(t, err)
require.Len(t, actual.Foo, 2)
require.Len(t, actual.Bar, 4)
}

func TestMergeMessage(t *testing.T) {
data := []byte(`
nested: &alias
foo: value
bar: value
second_nested:
<< : *alias
bar: another value
`)
actual := &testv1.EdgeTestNestedMessage{}
err := Unmarshal(data, actual)
require.NoError(t, err)
require.Equal(t, "value", actual.Nested.Foo)
require.Equal(t, "value", actual.Nested.Bar)
require.Equal(t, "value", actual.SecondNested.Foo)
require.Equal(t, "another value", actual.SecondNested.Bar)
}
Loading