freeNode.go 4.57 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
	_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'.
	_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'.
38
	_str   string               // Value union.  Only one of these has meaning, depending on the value of 'Type'.
39 40
	_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 54 55 56 57 58
func (n *Node) AsInt() (v int, _ error) {
	return n._int, expectTyp(ipld.ReprKind_Int, n.kind)
}
func (n *Node) AsFloat() (v float64, _ error) {
	return n._float, expectTyp(ipld.ReprKind_Float, n.kind)
}
Eric Myhre's avatar
Eric Myhre committed
59
func (n *Node) AsString() (v string, _ error) {
60
	return n._str, expectTyp(ipld.ReprKind_String, n.kind)
Eric Myhre's avatar
Eric Myhre committed
61
}
62 63
func (n *Node) AsBytes() (v []byte, _ error) {
	return n._bytes, expectTyp(ipld.ReprKind_Bytes, n.kind)
Eric Myhre's avatar
Eric Myhre committed
64 65
}
func (n *Node) AsLink() (v cid.Cid, _ error) {
66 67 68 69 70 71
	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
72 73 74
}

func (n *Node) TraverseField(pth string) (ipld.Node, error) {
75
	switch n.kind {
76 77
	case ipld.ReprKind_Invalid:
		return nil, fmt.Errorf("cannot traverse a node that is undefined")
78
	case ipld.ReprKind_Null:
Eric Myhre's avatar
Eric Myhre committed
79
		return nil, fmt.Errorf("cannot traverse terminals")
80
	case ipld.ReprKind_Map:
Eric Myhre's avatar
Eric Myhre committed
81 82 83 84 85 86 87 88 89 90 91 92 93 94
		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")
		}
95
	case ipld.ReprKind_List:
Eric Myhre's avatar
Eric Myhre committed
96 97 98 99 100 101 102
		i, err := strconv.Atoi(pth)
		if err != nil {
			return nil, fmt.Errorf("404")
		}
		if i >= len(n._arr) {
			return nil, fmt.Errorf("404")
		}
103
		return n._arr[i], nil
104 105 106 107 108 109
	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
110 111 112 113 114 115 116
		return nil, fmt.Errorf("cannot traverse terminals")
	default:
		panic("unreachable")
	}
}

func (n *Node) TraverseIndex(idx int) (ipld.Node, error) {
117
	switch n.kind {
118 119
	case ipld.ReprKind_Invalid:
		return nil, fmt.Errorf("cannot traverse a node that is undefined")
120
	case ipld.ReprKind_Null:
121
		return nil, fmt.Errorf("cannot traverse terminals")
122
	case ipld.ReprKind_Map:
123 124
		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.
125
	case ipld.ReprKind_List:
126 127 128 129 130 131 132
		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
133 134 135 136 137 138
	case ipld.ReprKind_Bool,
		ipld.ReprKind_String,
		ipld.ReprKind_Bytes,
		ipld.ReprKind_Int,
		ipld.ReprKind_Float,
		ipld.ReprKind_Link:
139 140 141 142
		return nil, fmt.Errorf("cannot traverse terminals")
	default:
		panic("unreachable")
	}
Eric Myhre's avatar
Eric Myhre committed
143 144
}

145
func expectTyp(expect, actual ipld.ReprKind) error {
Eric Myhre's avatar
Eric Myhre committed
146 147 148
	if expect == actual {
		return nil
	}
149
	return fmt.Errorf("type assertion rejected: node is %q, assertion was for %q", actual, expect)
Eric Myhre's avatar
Eric Myhre committed
150
}