freeNode.go 4.3 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
	_map   map[string]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'.
37
	_str   string               // Value union.  Only one of these has meaning, depending on the value of 'Type'.
38 39
	_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
40 41
}

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

Eric Myhre's avatar
Eric Myhre committed
46 47 48
func (n *Node) IsNull() bool {
	return n.kind == ipld.ReprKind_Null
}
Eric Myhre's avatar
Eric Myhre committed
49
func (n *Node) AsBool() (v bool, _ error) {
50
	return n._bool, expectTyp(ipld.ReprKind_Bool, n.kind)
Eric Myhre's avatar
Eric Myhre committed
51
}
52 53 54 55 56 57
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
58
func (n *Node) AsString() (v string, _ error) {
59
	return n._str, expectTyp(ipld.ReprKind_String, n.kind)
Eric Myhre's avatar
Eric Myhre committed
60
}
61 62
func (n *Node) AsBytes() (v []byte, _ error) {
	return n._bytes, expectTyp(ipld.ReprKind_Bytes, n.kind)
Eric Myhre's avatar
Eric Myhre committed
63 64
}
func (n *Node) AsLink() (v cid.Cid, _ error) {
65 66 67 68 69 70
	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
71 72 73
}

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

func (n *Node) TraverseIndex(idx int) (ipld.Node, error) {
107
	switch n.kind {
108 109
	case ipld.ReprKind_Invalid:
		return nil, fmt.Errorf("cannot traverse a node that is undefined")
110
	case ipld.ReprKind_Null:
111
		return nil, fmt.Errorf("cannot traverse terminals")
112
	case ipld.ReprKind_Map:
113 114
		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.
115
	case ipld.ReprKind_List:
116 117 118 119 120 121 122
		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
123 124 125 126 127 128
	case ipld.ReprKind_Bool,
		ipld.ReprKind_String,
		ipld.ReprKind_Bytes,
		ipld.ReprKind_Int,
		ipld.ReprKind_Float,
		ipld.ReprKind_Link:
129 130 131 132
		return nil, fmt.Errorf("cannot traverse terminals")
	default:
		panic("unreachable")
	}
Eric Myhre's avatar
Eric Myhre committed
133 134
}

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