freeNode.go 4.17 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 23 24 25 26 27 28 29
	"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).

	The "zero" value of this struct is interpreted as an empty map.

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

func (n *Node) TraverseIndex(idx int) (ipld.Node, error) {
109 110
	switch n.kind {
	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
}