Commit adc089c2 authored by Eric Myhre's avatar Eric Myhre

Merge branch 'drop-mutable-node'

parents fdbbd90f 025fcf8a
......@@ -23,8 +23,7 @@ func TestRecover(t *testing.T) {
t.Run("correct traversal should return nil", func(t *testing.T) {
Wish(t,
Recover(func() {
n := &ipldfree.Node{}
n.SetString("foo")
n, _ := ipldfree.NodeBuilder().CreateString("foo")
WrapNode(n).AsString()
}),
ShouldEqual,
......
......@@ -103,6 +103,10 @@ func (n Node) AsLink() (v ipld.Link, _ error) {
return
}
func (n Node) NodeBuilder() ipld.NodeBuilder {
panic("NYI")
}
func (n Node) Keys() ipld.KeyIterator {
panic("NYI")
}
......
......@@ -65,6 +65,10 @@ func (n *Node) AsLink() (v ipld.Link, _ error) {
return n._link, expectTyp(ipld.ReprKind_Link, n.kind)
}
func (n *Node) NodeBuilder() ipld.NodeBuilder {
return nodeBuilder{n}
}
func (n *Node) Keys() ipld.KeyIterator {
return &keyIterator{n, 0}
}
......
package ipldfree
import (
"github.com/ipld/go-ipld-prime"
)
var (
_ ipld.MutableNode = &Node{}
)
func (n *Node) SetNull() {
n.coerceType(ipld.ReprKind_Null)
}
func (n *Node) SetField(k string, v ipld.Node) {
n.coerceType(ipld.ReprKind_Map)
if _, exists := n._map[k]; !exists {
n._mapOrd = append(n._mapOrd, k)
}
n._map[k] = v
}
func (n *Node) SetIndex(k int, v ipld.Node) {
n.coerceType(ipld.ReprKind_List)
// REVIEW: there are implications to serial arrays as we spec'd them.
// Namely, they can't be sparse. It's just not defined.
// And that means we simply have to have a way to define the length.
// We can do implicit grows via this setter; I'm fine with that.
// But we'll also need, evidently, a Truncate method.
// (Or, a magical sentinel value for node that says EOL.)
oldLen := len(n._arr)
minLen := k + 1
if minLen > oldLen {
// Grow.
oldCap := cap(n._arr)
if minLen > oldCap {
// Out of cap; do whole new backing array allocation.
// Growth maths are per stdlib's reflect.grow.
// First figure out how much growth to do.
newCap := oldCap
if newCap == 0 {
newCap = minLen
} else {
for minLen > newCap {
if minLen < 1024 {
newCap += newCap
} else {
newCap += newCap / 4
}
}
}
// Now alloc and copy over old.
newArr := make([]ipld.Node, minLen, newCap)
copy(newArr, n._arr)
n._arr = newArr
} else {
// Still have cap, just extend the slice.
n._arr = n._arr[0:minLen]
}
}
n._arr[k] = v
//fmt.Printf("len,cap is now %d,%d\n", len(n._arr), cap(n._arr))
}
func (n *Node) SetBool(v bool) {
n.coerceType(ipld.ReprKind_Bool)
n._bool = v
}
func (n *Node) SetInt(v int) {
n.coerceType(ipld.ReprKind_Int)
n._int = v
}
func (n *Node) SetFloat(v float64) {
n.coerceType(ipld.ReprKind_Float)
n._float = v
}
func (n *Node) SetString(v string) {
n.coerceType(ipld.ReprKind_String)
n._str = v
}
func (n *Node) SetBytes(v []byte) {
n.coerceType(ipld.ReprKind_Bytes)
n._bytes = v
}
func (n *Node) SetLink(v ipld.Link) {
n.coerceType(ipld.ReprKind_Link)
n._link = v
}
func (n *Node) coerceType(newKind ipld.ReprKind) {
// If this node pointer has actually just been nil, initialize.
// (Our arrays sometimes initialize full of nils, so this comes up.)
// TODO
// REVIEW actually it's pretty dubious that we should return those.
// Nobody ever said our concept of array should be non-sparse and get nils in it.
// Clear previous data, if relevant.
// Don't bother with zeroing finite-size scalars.
switch n.kind {
case ipld.ReprKind_Map:
switch newKind {
case ipld.ReprKind_Map:
return
default:
n._map = nil
n._mapOrd = nil
}
case ipld.ReprKind_List:
switch newKind {
case ipld.ReprKind_List:
return
default:
n._arr = nil
}
case ipld.ReprKind_String:
switch newKind {
case ipld.ReprKind_String:
return
default:
n._str = ""
}
case ipld.ReprKind_Bytes:
switch newKind {
case ipld.ReprKind_Bytes:
return
default:
n._bytes = nil
}
case ipld.ReprKind_Link:
switch newKind {
case ipld.ReprKind_Link:
return
default:
n._link = nil
}
}
// Set new type union marker.
// Initialize empty value if necessary (maps).
n.kind = newKind
switch newKind {
case ipld.ReprKind_Map:
n._map = make(map[string]ipld.Node)
}
// You'll still want to set the value itself after this.
}
......@@ -3,7 +3,6 @@ package ipldfree
import (
"testing"
"github.com/ipld/go-ipld-prime"
"github.com/ipld/go-ipld-prime/tests"
)
......@@ -12,13 +11,6 @@ func TestNodeBuilder(t *testing.T) {
tests.TestBuildingRecursives(t, NodeBuilder())
}
func TestMutableNode(t *testing.T) {
// this should likely become legacy stuff and go away
mutNodeFac := func() ipld.MutableNode { return &Node{} }
tests.TestScalars(t, mutNodeFac)
tests.TestRecursives(t, mutNodeFac)
}
func TestTokening(t *testing.T) {
tests.TestScalarMarshal(t, NodeBuilder())
tests.TestRecursiveMarshal(t, NodeBuilder())
......
......@@ -63,6 +63,20 @@ type Node interface {
AsString() (string, error)
AsBytes() ([]byte, error)
AsLink() (Link, error)
// NodeBuilder returns a NodeBuilder which can be used to build
// new nodes of the same implementation type as this one.
//
// For map and list nodes, the NodeBuilder's append-oriented methods
// will work using this node's values as a base.
// If this is a typed node, the NodeBuilder will carry the same
// typesystem constraints as this Node.
//
// (This feature is used by the traversal package, especially in
// e.g. traversal.Transform, for doing tree updates while keeping the
// existing implementation preferences and doing as many operations
// in copy-on-write fashions as possible.)
NodeBuilder() NodeBuilder
}
// KeyIterator is an interface for traversing nodes of kind map.
......@@ -82,27 +96,6 @@ type KeyIterator interface {
HasNext() bool
}
// MutableNode is an interface which allows setting its value.
// MutableNode can be all the same kinds as Node can, and can also
// be freely coerced between kinds.
//
// Using a method which coerces a Mutable node to a new kind can
// discard data; the SetField and SetIndex methods are concatenative,
// but using any of the Set{Scalar} methods will result in any map or
// array content being discarded(!).
type MutableNode interface {
Node
SetField(k string, v Node) // SetField coerces the node to a map kind and sets a key:val pair.
SetIndex(k int, v Node) // SetIndex coerces the node to an array kind and sets an index:val pair. (It will implicitly increase the size to include the index.)
SetNull()
SetBool(v bool)
SetInt(v int)
SetFloat(v float64)
SetString(v string)
SetBytes(v []byte)
SetLink(v Link)
}
// REVIEW: immediate-mode AsBytes() method (as opposed to e.g. returning
// an io.Reader instance) might be problematic, esp. if we introduce
// AdvancedLayouts which support large bytes natively.
......
package tests
import (
"testing"
"github.com/ipld/go-ipld-prime"
"github.com/ipld/go-ipld-prime/fluent"
. "github.com/warpfork/go-wish"
)
type MutableNodeFactory func() ipld.MutableNode
func TestScalars(t *testing.T, newNode MutableNodeFactory) {
t.Run("null node bounce", func(t *testing.T) {
n0 := newNode()
n0.SetNull()
Wish(t, n0.Kind(), ShouldEqual, ipld.ReprKind_Null)
Wish(t, n0.IsNull(), ShouldEqual, true)
})
t.Run("bool node bounce", func(t *testing.T) {
n0 := newNode()
n0.SetBool(true)
Wish(t, n0.Kind(), ShouldEqual, ipld.ReprKind_Bool)
Wish(t, n0.IsNull(), ShouldEqual, false)
Wish(t, fluent.WrapNode(n0).AsBool(), ShouldEqual, true)
})
t.Run("int node bounce", func(t *testing.T) {
n0 := newNode()
n0.SetInt(17)
Wish(t, n0.Kind(), ShouldEqual, ipld.ReprKind_Int)
Wish(t, n0.IsNull(), ShouldEqual, false)
Wish(t, fluent.WrapNode(n0).AsInt(), ShouldEqual, 17)
})
t.Run("float node bounce", func(t *testing.T) {
n0 := newNode()
n0.SetFloat(0.122)
Wish(t, n0.Kind(), ShouldEqual, ipld.ReprKind_Float)
Wish(t, n0.IsNull(), ShouldEqual, false)
Wish(t, fluent.WrapNode(n0).AsFloat(), ShouldEqual, 0.122)
})
t.Run("string node bounce", func(t *testing.T) {
n0 := newNode()
n0.SetString("asdf")
Wish(t, n0.Kind(), ShouldEqual, ipld.ReprKind_String)
Wish(t, n0.IsNull(), ShouldEqual, false)
Wish(t, fluent.WrapNode(n0).AsString(), ShouldEqual, "asdf")
})
t.Run("bytes node bounce", func(t *testing.T) {
n0 := newNode()
n0.SetBytes([]byte{65, 66})
Wish(t, n0.Kind(), ShouldEqual, ipld.ReprKind_Bytes)
Wish(t, n0.IsNull(), ShouldEqual, false)
Wish(t, fluent.WrapNode(n0).AsBytes(), ShouldEqual, []byte{65, 66})
})
t.Run("link node bounce", func(t *testing.T) {
n0 := newNode()
n0.SetBool(true)
Wish(t, n0.Kind(), ShouldEqual, ipld.ReprKind_Bool)
Wish(t, n0.IsNull(), ShouldEqual, false)
Wish(t, fluent.WrapNode(n0).AsBool(), ShouldEqual, true)
})
}
func TestRecursives(t *testing.T, newNode MutableNodeFactory) {
t.Run("short list node bounce", func(t *testing.T) {
n0 := newNode()
n00 := newNode()
n00.SetString("asdf")
n0.SetIndex(0, n00)
Wish(t, fluent.WrapNode(n0).TraverseIndex(0).AsString(), ShouldEqual, "asdf")
})
t.Run("nested list node bounce", func(t *testing.T) {
n0 := newNode()
n00 := newNode()
n0.SetIndex(0, n00)
n000 := newNode()
n000.SetString("asdf")
n00.SetIndex(0, n000)
n01 := newNode()
n01.SetString("quux")
n0.SetIndex(1, n01)
nf := fluent.WrapNode(n0)
Wish(t, nf.Kind(), ShouldEqual, ipld.ReprKind_List)
Wish(t, nf.TraverseIndex(0).TraverseIndex(0).AsString(), ShouldEqual, "asdf")
Wish(t, nf.TraverseIndex(1).AsString(), ShouldEqual, "quux")
})
}
......@@ -10,8 +10,7 @@ import (
func TestSimpleTypes(t *testing.T) {
t.Run("string alone", func(t *testing.T) {
n1 := &ipldfree.Node{}
n1.SetString("asdf")
n1, _ := ipldfree.NodeBuilder().CreateString("asdf")
t1 := TypeString{
anyType{name: "Foo"},
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment