freeNode.go 3.79 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 30 31
	"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 {
	typ typ

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
}

type typ struct{ t byte }

var (
Eric Myhre's avatar
Eric Myhre committed
46
	tNil   = typ{}
Eric Myhre's avatar
Eric Myhre committed
47 48 49
	tMap   = typ{'{'}
	tArr   = typ{'['}
	tBool  = typ{'b'}
50
	tStr   = typ{'s'}
Eric Myhre's avatar
Eric Myhre committed
51 52
	tInt   = typ{'i'}
	tFloat = typ{'f'}
53
	tBytes = typ{'x'}
Eric Myhre's avatar
Eric Myhre committed
54
	tLink  = typ{'/'}
Eric Myhre's avatar
Eric Myhre committed
55 56
)

Eric Myhre's avatar
Eric Myhre committed
57 58 59 60 61 62 63 64 65 66
func (n *Node) AsBool() (v bool, _ error) {
	return n._bool, expectTyp(tBool, n.typ)
}
func (n *Node) AsString() (v string, _ error) {
	return n._str, expectTyp(tStr, n.typ)
}
func (n *Node) AsInt() (v int, _ error) {
	return n._int, expectTyp(tInt, n.typ)
}
func (n *Node) AsLink() (v cid.Cid, _ error) {
67
	return n._link, expectTyp(tLink, n.typ)
Eric Myhre's avatar
Eric Myhre committed
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
}

func (n *Node) TraverseField(pth string) (ipld.Node, error) {
	switch n.typ {
	case tNil:
		return nil, fmt.Errorf("cannot traverse terminals")
	case tMap:
		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")
		}
	case tArr:
		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
	case tStr, tBytes, tBool, tInt, tFloat, tLink:
Eric Myhre's avatar
Eric Myhre committed
99 100 101 102 103 104 105
		return nil, fmt.Errorf("cannot traverse terminals")
	default:
		panic("unreachable")
	}
}

func (n *Node) TraverseIndex(idx int) (ipld.Node, error) {
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
	switch n.typ {
	case tNil:
		return nil, fmt.Errorf("cannot traverse terminals")
	case tMap:
		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.
	case tArr:
		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
	case tStr, tBytes, tBool, tInt, tFloat, tLink:
		return nil, fmt.Errorf("cannot traverse terminals")
	default:
		panic("unreachable")
	}
Eric Myhre's avatar
Eric Myhre committed
125 126 127 128 129 130
}

func expectTyp(expect, actual typ) error {
	if expect == actual {
		return nil
	}
131
	return fmt.Errorf("type assertion rejected: node is %q, assertion was for %q", actual, expect)
Eric Myhre's avatar
Eric Myhre committed
132
}