Skip to content

Commit

Permalink
fluent: add toInterface (#304)
Browse files Browse the repository at this point in the history
Fixes #178.
  • Loading branch information
MarcoPolo authored Dec 2, 2021
1 parent 2bfc036 commit dc30274
Show file tree
Hide file tree
Showing 2 changed files with 118 additions and 0 deletions.
71 changes: 71 additions & 0 deletions fluent/toInterfaceValue.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package fluent

import (
"errors"

"github.com/ipld/go-ipld-prime/datamodel"
)

var errInvalidKind = errors.New("invalid kind")
var errUnknownKind = errors.New("unknown kind")

// ToInterface converts an IPLD node to its simplest equivalent Go value.
//
// Booleans, integers, floats, strings, bytes, and links are returned as themselves,
// as per the node's AsT method. Note that nulls are returned as untyped nils.
//
// Lists and maps are returned as []interface{} and map[string]interface{}, respectively.
func ToInterface(node datamodel.Node) (interface{}, error) {
switch k := node.Kind(); k {
case datamodel.Kind_Invalid:
return nil, errInvalidKind
case datamodel.Kind_Null:
return nil, nil
case datamodel.Kind_Bool:
return node.AsBool()
case datamodel.Kind_Int:
return node.AsInt()
case datamodel.Kind_Float:
return node.AsFloat()
case datamodel.Kind_String:
return node.AsString()
case datamodel.Kind_Bytes:
return node.AsBytes()
case datamodel.Kind_Link:
return node.AsLink()
case datamodel.Kind_Map:
outMap := make(map[string]interface{}, node.Length())
for mi := node.MapIterator(); !mi.Done(); {
k, v, err := mi.Next()
if err != nil {
return nil, err
}
kVal, err := k.AsString()
if err != nil {
return nil, err
}
vVal, err := ToInterface(v)
if err != nil {
return nil, err
}
outMap[kVal] = vVal
}
return outMap, nil
case datamodel.Kind_List:
outList := make([]interface{}, 0, node.Length())
for li := node.ListIterator(); !li.Done(); {
_, v, err := li.Next()
if err != nil {
return nil, err
}
vVal, err := ToInterface(v)
if err != nil {
return nil, err
}
outList = append(outList, vVal)
}
return outList, nil
default:
return nil, errUnknownKind
}
}
47 changes: 47 additions & 0 deletions fluent/toInterfaceValue_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package fluent_test

import (
"testing"

qt "github.com/frankban/quicktest"
"github.com/ipfs/go-cid"
"github.com/ipld/go-ipld-prime/fluent"
cidlink "github.com/ipld/go-ipld-prime/linking/cid"
"github.com/ipld/go-ipld-prime/node/basicnode"
)

var roundTripTestCases = []struct {
name string
value interface{}
}{
{name: "Number", value: int64(100)},
{name: "String", value: "hi"},
{name: "Bool", value: true},
{name: "Bytes", value: []byte("hi")},
{name: "Map", value: map[string]interface{}{"a": "1", "b": int64(2), "c": 3.14, "d": true}},
{name: "Array", value: []interface{}{"a", "b", "c"}},
{name: "Nil", value: nil},
}

func TestRoundTrip(t *testing.T) {
for _, testCase := range roundTripTestCases {
t.Run(testCase.name, func(t *testing.T) {
c := qt.New(t)
n, err := fluent.Reflect(basicnode.Prototype.Any, testCase.value)
c.Assert(err, qt.IsNil)
out, err := fluent.ToInterface(n)
c.Assert(err, qt.IsNil)
c.Check(out, qt.DeepEquals, testCase.value)
})
}
}

func TestLink(t *testing.T) {
c := qt.New(t)
someCid, err := cid.Parse("bafybeihrqe2hmfauph5yfbd6ucv7njqpiy4tvbewlvhzjl4bhnyiu6h7pm")
c.Assert(err, qt.IsNil)
link := cidlink.Link{Cid: someCid}
v, err := fluent.ToInterface(basicnode.NewLink(link))
c.Assert(err, qt.IsNil)
c.Assert(v.(cidlink.Link), qt.Equals, link)
}

0 comments on commit dc30274

Please sign in to comment.