Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(bindnode): infer links and Any from Go types #432

Merged
merged 1 commit into from
Jun 1, 2022
Merged
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
19 changes: 17 additions & 2 deletions node/bindnode/infer.go
Original file line number Diff line number Diff line change
Expand Up @@ -380,7 +380,6 @@ func inferSchema(typ reflect.Type, level int) schema.Type {
if level > maxRecursionLevel {
panic(fmt.Sprintf("inferSchema: refusing to recurse past %d levels", maxRecursionLevel))
}
// TODO: support Link and Any
switch typ.Kind() {
case reflect.Bool:
return schemaTypeBool
Expand All @@ -391,6 +390,12 @@ func inferSchema(typ reflect.Type, level int) schema.Type {
case reflect.String:
return schemaTypeString
case reflect.Struct:
// these types must match exactly since we need symmetry of being able to
// get the values an also assign values to them
if typ == goTypeCid || typ == goTypeCidLink {
return schemaTypeLink
}

fieldsSchema := make([]schema.StructField, typ.NumField())
for i := range fieldsSchema {
field := typ.Field(i)
Expand Down Expand Up @@ -430,8 +435,18 @@ func inferSchema(typ reflect.Type, level int) schema.Type {
typSchema := schema.SpawnList(name, etypSchema.Name(), nullable)
defaultTypeSystem.Accumulate(typSchema)
return typSchema
case reflect.Interface:
// these types must match exactly since we need symmetry of being able to
// get the values an also assign values to them
if typ == goTypeLink {
return schemaTypeLink
}
if typ == goTypeNode {
rvagg marked this conversation as resolved.
Show resolved Hide resolved
return schemaTypeAny
}
panic("bindnode: unable to infer from interface")
}
panic(typ.Kind().String())
panic(fmt.Sprintf("bindnode: unable to infer from type %s", typ.Kind().String()))
}

// There are currently 27 reflect.Kind iota values,
Expand Down
38 changes: 38 additions & 0 deletions node/bindnode/infer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"testing"

qt "github.com/frankban/quicktest"
"github.com/google/go-cmp/cmp"
"github.com/ipfs/go-cid"

"github.com/ipld/go-ipld-prime"
Expand Down Expand Up @@ -1211,3 +1212,40 @@ func TestEmptyTypedAssignNode(t *testing.T) {
})
}
}

func TestInferLinksAndAny(t *testing.T) {
link, err := cid.Decode("bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi")
qt.Assert(t, err, qt.IsNil)

type Nd struct {
A cid.Cid
B cidlink.Link
C datamodel.Link
D datamodel.Node
}

proto := bindnode.Prototype(&Nd{}, nil)

expected := &Nd{
A: link,
B: cidlink.Link{Cid: link},
C: cidlink.Link{Cid: link},
D: basicnode.NewString("Any Here"),
}

node := bindnode.Wrap(expected, proto.Type())

byts, err := ipld.Encode(node, dagjson.Encode)
qt.Assert(t, err, qt.IsNil)

qt.Assert(t, string(byts), qt.Equals, `{"A":{"/":"bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi"},"B":{"/":"bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi"},"C":{"/":"bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi"},"D":"Any Here"}`)

nodeRt, err := ipld.DecodeUsingPrototype(byts, dagjson.Decode, proto)
qt.Assert(t, err, qt.IsNil)

if actual, ok := bindnode.Unwrap(nodeRt).(*Nd); ok {
qt.Assert(t, actual, qt.CmpEquals(cmp.Comparer(func(x, y cid.Cid) bool { return x.Equals(y) })), expected)
} else {
t.Error("expected *Nd from Unwrap")
}
}