freeNode.go 4.36 KB
Newer Older
Eric Myhre's avatar
Eric Myhre committed
1 2 3
package ipldfree

import (
Eric Myhre's avatar
Eric Myhre committed
4 5 6 7
	"fmt"
	"strconv"

	"github.com/ipfs/go-cid"
Eric Myhre's avatar
Eric Myhre committed
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
	"github.com/ipld/go-ipld-prime"
)

var (
	_ ipld.Node = &Node{}
)

/*
	Node has some internal

	This implementation of `ipld.Node` is pretty comparable to `ipldbind.Node`,
	but is somewhat simpler in implementation because values of this type can
	only be produced by its own builder patterns (and thus it requires much
	less reflection and in particular does not depend on refmt for object mapping).

23
	The "zero" value of this struct has a kind of ReprKind_Invalid.
Eric Myhre's avatar
Eric Myhre committed
24 25 26 27 28 29

	This binding does not provide a serialization valid for hashing; to
	compute a CID, you'll have to convert to another kind of node.
	If you're not sure which kind serializable node to use, try `ipldcbor.Node`.
*/
type Node struct {
30
	kind ipld.ReprKind
Eric Myhre's avatar
Eric Myhre committed
31

32 33 34 35 36 37 38 39 40
	_map   map[string]ipld.Node // Value union.  Only one of these has meaning, depending on the value of 'Type'.
	_map2  map[int]ipld.Node    // Value union.  Only one of these has meaning, depending on the value of 'Type'.
	_arr   []ipld.Node          // Value union.  Only one of these has meaning, depending on the value of 'Type'.
	_bool  bool                 // Value union.  Only one of these has meaning, depending on the value of 'Type'.
	_str   string               // Value union.  Only one of these has meaning, depending on the value of 'Type'.
	_int   int                  // Value union.  Only one of these has meaning, depending on the value of 'Type'.
	_float float64              // Value union.  Only one of these has meaning, depending on the value of 'Type'.
	_bytes []byte               // Value union.  Only one of these has meaning, depending on the value of 'Type'.
	_link  cid.Cid              // Value union.  Only one of these has meaning, depending on the value of 'Type'.
Eric Myhre's avatar
Eric Myhre committed
41 42
}

43 44 45
func (n *Node) Kind() ipld.ReprKind {
	return n.kind
}
Eric Myhre's avatar
Eric Myhre committed
46

Eric Myhre's avatar
Eric Myhre committed
47 48 49
func (n *Node) IsNull() bool {
	return n.kind == ipld.ReprKind_Null
}
Eric Myhre's avatar
Eric Myhre committed
50
func (n *Node) AsBool() (v bool, _ error) {
51
	return n._bool, expectTyp(ipld.ReprKind_Bool, n.kind)
Eric Myhre's avatar
Eric Myhre committed
52 53
}
func (n *Node) AsString() (v string, _ error) {
54
	return n._str, expectTyp(ipld.ReprKind_String, n.kind)
Eric Myhre's avatar
Eric Myhre committed
55 56
}
func (n *Node) AsInt() (v int, _ error) {
57
	return n._int, expectTyp(ipld.ReprKind_Int, n.kind)
Eric Myhre's avatar
Eric Myhre committed
58 59
}
func (n *Node) AsLink() (v cid.Cid, _ error) {
60 61 62 63 64 65
	return n._link, expectTyp(ipld.ReprKind_Link, n.kind)
}

func (n *Node) Keys() ([]string, int) {
	return nil, 0 // FIXME
	// TODO need to maintain map order now, apparently, sigh
Eric Myhre's avatar
Eric Myhre committed
66 67 68
}

func (n *Node) TraverseField(pth string) (ipld.Node, error) {
69
	switch n.kind {
70 71
	case ipld.ReprKind_Invalid:
		return nil, fmt.Errorf("cannot traverse a node that is undefined")
72
	case ipld.ReprKind_Null:
Eric Myhre's avatar
Eric Myhre committed
73
		return nil, fmt.Errorf("cannot traverse terminals")
74
	case ipld.ReprKind_Map:
Eric Myhre's avatar
Eric Myhre committed
75 76 77 78 79 80 81 82 83 84 85 86 87 88
		switch {
		case n._map != nil:
			v, _ := n._map[pth]
			return v, nil
		case n._map2 != nil:
			i, err := strconv.Atoi(pth)
			if err != nil {
				return nil, fmt.Errorf("404")
			}
			v, _ := n._map2[i]
			return v, nil
		default:
			panic("unreachable")
		}
89
	case ipld.ReprKind_List:
Eric Myhre's avatar
Eric Myhre committed
90 91 92 93 94 95 96
		i, err := strconv.Atoi(pth)
		if err != nil {
			return nil, fmt.Errorf("404")
		}
		if i >= len(n._arr) {
			return nil, fmt.Errorf("404")
		}
97
		return n._arr[i], nil
98 99 100 101 102 103
	case ipld.ReprKind_Bool,
		ipld.ReprKind_String,
		ipld.ReprKind_Bytes,
		ipld.ReprKind_Int,
		ipld.ReprKind_Float,
		ipld.ReprKind_Link:
Eric Myhre's avatar
Eric Myhre committed
104 105 106 107 108 109 110
		return nil, fmt.Errorf("cannot traverse terminals")
	default:
		panic("unreachable")
	}
}

func (n *Node) TraverseIndex(idx int) (ipld.Node, error) {
111
	switch n.kind {
112 113
	case ipld.ReprKind_Invalid:
		return nil, fmt.Errorf("cannot traverse a node that is undefined")
114
	case ipld.ReprKind_Null:
115
		return nil, fmt.Errorf("cannot traverse terminals")
116
	case ipld.ReprKind_Map:
117 118
		return nil, fmt.Errorf("cannot traverse map by numeric index")
		// REVIEW: there's an argument that maybe we should support this; would be '_map2' code.
119
	case ipld.ReprKind_List:
120 121 122 123 124 125 126
		if idx >= len(n._arr) {
			return nil, fmt.Errorf("404")
		}
		if n._arr[idx] == nil {
			return nil, fmt.Errorf("404")
		}
		return n._arr[idx], nil
127 128 129 130 131 132
	case ipld.ReprKind_Bool,
		ipld.ReprKind_String,
		ipld.ReprKind_Bytes,
		ipld.ReprKind_Int,
		ipld.ReprKind_Float,
		ipld.ReprKind_Link:
133 134 135 136
		return nil, fmt.Errorf("cannot traverse terminals")
	default:
		panic("unreachable")
	}
Eric Myhre's avatar
Eric Myhre committed
137 138
}

139
func expectTyp(expect, actual ipld.ReprKind) error {
Eric Myhre's avatar
Eric Myhre committed
140 141 142
	if expect == actual {
		return nil
	}
143
	return fmt.Errorf("type assertion rejected: node is %q, assertion was for %q", actual, expect)
Eric Myhre's avatar
Eric Myhre committed
144
}