Skip to content

Commit

Permalink
node/bindnode: make AssignNode work at the repr level
Browse files Browse the repository at this point in the history
The repr-level AssignNode just called the type-level AssignNode.
This worked for cases where the repr level was identical,
such as a struct with map repr, so we hadn't caught the mistake yet.

The problem is that forwarding the call from the repr level to the type
level would then call the Assembler methods on the type level.
In the case of our test, that would fail, as the renames would not be
taken into account properly.

The fix is to make the two methods call the assembler methods on their
own types, be it type level or repr level.
Luckily, we don't need to copy-paste the code or move it around,
because datamodel.Copy has luckily existed since v0.14.0.

Thanks to Rod Vagg for writing the reproducer in the form of a test.

Fixes #365.
  • Loading branch information
mvdan authored and rvagg committed Feb 23, 2022
1 parent adcfa4c commit e39d20b
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 80 deletions.
30 changes: 30 additions & 0 deletions node/bindnode/infer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"github.com/ipld/go-ipld-prime/codec/dagjson"
"github.com/ipld/go-ipld-prime/datamodel"
cidlink "github.com/ipld/go-ipld-prime/linking/cid"
"github.com/ipld/go-ipld-prime/node/basicnode"
"github.com/ipld/go-ipld-prime/node/bindnode"
"github.com/ipld/go-ipld-prime/schema"
)
Expand Down Expand Up @@ -978,3 +979,32 @@ func TestProduceGoTypes(t *testing.T) {
})
}
}

func TestRenameAssignNode(t *testing.T) {
type Foo struct{ I int }

ts, _ := ipld.LoadSchemaBytes([]byte(`
type Foo struct {
I Int (rename "J")
}
`))
FooProto := bindnode.Prototype((*Foo)(nil), ts.TypeByName("Foo"))

// Decode straight into bindnode typed builder
nb := FooProto.Representation().NewBuilder()
err := dagjson.Decode(nb, bytes.NewReader([]byte(`{"J":100}`)))
qt.Assert(t, err, qt.IsNil)
node := nb.Build()

// decode into basicnode builder
nb = basicnode.Prototype.Any.NewBuilder()
err = dagjson.Decode(nb, bytes.NewReader([]byte(`{"J":100}`)))
qt.Assert(t, err, qt.IsNil)
node = nb.Build()

// AssignNode from the basicnode form
nb = FooProto.Representation().NewBuilder()
err = nb.AssignNode(node)
qt.Assert(t, err, qt.IsNil)
node = nb.Build()
}
80 changes: 1 addition & 79 deletions node/bindnode/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -752,85 +752,7 @@ func (w *_assembler) AssignNode(node datamodel.Node) error {
// w.val.Set(newVal)
// return nil
// }
switch node.Kind() {
case datamodel.Kind_Map:
itr := node.MapIterator()
// TODO: consider reusing this code from elsewhere,
// via something like datamodel.BlindCopyMap.
am, err := w.BeginMap(-1) // TODO: length?
if err != nil {
return err
}
for !itr.Done() {
k, v, err := itr.Next()
if err != nil {
return err
}
if err := am.AssembleKey().AssignNode(k); err != nil {
return err
}
if err := am.AssembleValue().AssignNode(v); err != nil {
return err
}
}
return am.Finish()
case datamodel.Kind_List:
itr := node.ListIterator()
am, err := w.BeginList(-1) // TODO: length?
if err != nil {
return err
}
for !itr.Done() {
_, v, err := itr.Next()
if err != nil {
return err
}
if err := am.AssembleValue().AssignNode(v); err != nil {
return err
}
}
return am.Finish()

case datamodel.Kind_Bool:
b, err := node.AsBool()
if err != nil {
return err
}
return w.AssignBool(b)
case datamodel.Kind_Int:
i, err := node.AsInt()
if err != nil {
return err
}
return w.AssignInt(i)
case datamodel.Kind_Float:
f, err := node.AsFloat()
if err != nil {
return err
}
return w.AssignFloat(f)
case datamodel.Kind_String:
s, err := node.AsString()
if err != nil {
return err
}
return w.AssignString(s)
case datamodel.Kind_Bytes:
p, err := node.AsBytes()
if err != nil {
return err
}
return w.AssignBytes(p)
case datamodel.Kind_Link:
l, err := node.AsLink()
if err != nil {
return err
}
return w.AssignLink(l)
case datamodel.Kind_Null:
return w.AssignNull()
}
return fmt.Errorf("AssignNode TODO: %v %v", w.val.Type(), node.Kind())
return datamodel.Copy(node, w)
}

func (w *_assembler) Prototype() datamodel.NodePrototype {
Expand Down
3 changes: 2 additions & 1 deletion node/bindnode/repr.go
Original file line number Diff line number Diff line change
Expand Up @@ -804,7 +804,8 @@ func (w *_assemblerRepr) AssignLink(link datamodel.Link) error {
}

func (w *_assemblerRepr) AssignNode(node datamodel.Node) error {
return (*_assembler)(w).AssignNode(node)
// TODO: attempt to take a shortcut, like assembler.AssignNode
return datamodel.Copy(node, w)
}

func (w *_assemblerRepr) Prototype() datamodel.NodePrototype {
Expand Down

0 comments on commit e39d20b

Please sign in to comment.