boundNode.go 4.38 KB
Newer Older
1 2 3
package ipldbind

import (
4
	"fmt"
5 6
	"reflect"

7
	"github.com/ipfs/go-cid"
8 9 10 11 12
	"github.com/ipld/go-ipld-prime"
	"github.com/polydawn/refmt/obj/atlas"
)

var (
Eric Myhre's avatar
Eric Myhre committed
13
	_ ipld.Node = Node{}
14 15 16 17 18 19 20 21 22 23 24
)

/*
	Node binds to some Go object in memory, using the definitions provided
	by refmt's object atlasing tools.

	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 {
Eric Myhre's avatar
Eric Myhre committed
25
	bound reflect.Value // should already be ptr-unwrapped
26 27
	atlas atlas.Atlas
}
28 29 30 31 32 33

/*
	Bind binds any go value into being interfacable as a Node, using the provided
	atlas to understand how to traverse it.
*/
func Bind(bindme interface{}, atl atlas.Atlas) ipld.Node {
Eric Myhre's avatar
Eric Myhre committed
34 35 36 37 38 39
	rv := reflect.ValueOf(bindme)
	for rv.Kind() == reflect.Ptr {
		rv = rv.Elem()
	}
	return Node{
		bound: rv,
40 41 42 43
		atlas: atl,
	}
}

Eric Myhre's avatar
Eric Myhre committed
44 45 46
func (n Node) AsBool() (v bool, _ error) {
	reflect.ValueOf(&v).Elem().Set(n.bound)
	return
47
}
Eric Myhre's avatar
Eric Myhre committed
48 49 50
func (n Node) AsString() (v string, _ error) {
	reflect.ValueOf(&v).Elem().Set(n.bound)
	return
51
}
Eric Myhre's avatar
Eric Myhre committed
52 53 54
func (n Node) AsInt() (v int, _ error) {
	reflect.ValueOf(&v).Elem().Set(n.bound)
	return
55
}
Eric Myhre's avatar
Eric Myhre committed
56 57 58
func (n Node) AsLink() (v cid.Cid, _ error) {
	reflect.ValueOf(&v).Elem().Set(n.bound)
	return
59 60
}

Eric Myhre's avatar
Eric Myhre committed
61 62
func (n Node) TraverseField(pth string) (ipld.Node, error) {
	v := n.bound
63

64 65 66
	// Traverse.
	//  Honor any atlent overrides if present;
	//  Use kind-based fallbacks if necessary.
Eric Myhre's avatar
Eric Myhre committed
67
	atlent, exists := n.atlas.Get(reflect.ValueOf(v.Type()).Pointer())
68 69 70 71 72 73 74 75 76 77 78
	if exists {
		switch {
		case atlent.MarshalTransformFunc != nil:
			panic(fmt.Errorf("invalid ipldbind.Node: type %q atlas specifies transform, but ipld doesn't support this power level", v.Type().Name()))
		case atlent.StructMap != nil:
			for _, fe := range atlent.StructMap.Fields {
				if fe.SerialName == pth {
					v = fe.ReflectRoute.TraverseToValue(v)
					break
				}
			}
Eric Myhre's avatar
Eric Myhre committed
79
			return Node{}, fmt.Errorf("traverse failed: type %q has no field named %q", v.Type().Name(), pth)
80 81 82 83 84 85 86 87
		case atlent.UnionKeyedMorphism != nil:
			panic(fmt.Errorf("invalid ipldbind.Node: type %q atlas specifies union, but ipld doesn't know how to make sense of this", v.Type().Name()))
		case atlent.MapMorphism != nil:
			v = v.MapIndex(reflect.ValueOf(pth))
		default:
			panic("unreachable")
		}
	} else {
88 89 90 91 92 93 94 95
		switch v.Type().Kind() {
		case // primitives: set them
			reflect.Bool,
			reflect.String,
			reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
			reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr,
			reflect.Float32, reflect.Float64,
			reflect.Complex64, reflect.Complex128:
96 97 98 99 100 101 102
			panic(fmt.Errorf("invalid ipldbind.Node: atlas for type %q is union; ipld doesn't know how to make sense of this", v.Type().Name()))
		case // recursives: wrap in node
			reflect.Array,
			reflect.Slice,
			reflect.Map,
			reflect.Struct,
			reflect.Interface:
Eric Myhre's avatar
Eric Myhre committed
103
			return Node{v, n.atlas}, nil
104 105 106 107 108 109 110 111
		case // esotera: reject with panic
			reflect.Chan,
			reflect.Func,
			reflect.UnsafePointer:
			panic(fmt.Errorf("invalid ipldbind.Node: cannot atlas over type %q; it's a %v", v.Type().Name(), v.Kind()))
		case // pointers: should've already been unwrapped
			reflect.Ptr:
			panic("unreachable")
112 113 114
		}
	}

115 116 117
	// Unwrap any pointers.
	for v.Kind() == reflect.Ptr {
		v = v.Elem()
118
	}
119 120 121 122 123 124

	// Assign into the result.
	//  Either assign the result directly (for primitives)
	//  Or wrap with a Node and assign that (for recursives).
	// TODO decide what to do with typedef'd primitives.
	switch v.Type().Kind() {
Eric Myhre's avatar
Eric Myhre committed
125
	case // primitives: wrap in node
126 127 128 129 130 131
		reflect.Bool,
		reflect.String,
		reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
		reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr,
		reflect.Float32, reflect.Float64,
		reflect.Complex64, reflect.Complex128:
Eric Myhre's avatar
Eric Myhre committed
132
		return Node{v, n.atlas}, nil
133 134 135 136 137 138
	case // recursives: wrap in node
		reflect.Array,
		reflect.Slice,
		reflect.Map,
		reflect.Struct,
		reflect.Interface:
Eric Myhre's avatar
Eric Myhre committed
139
		return Node{v, n.atlas}, nil
140 141 142 143 144 145 146 147 148 149
	case // esotera: reject with panic
		reflect.Chan,
		reflect.Func,
		reflect.UnsafePointer:
		panic(fmt.Errorf("invalid ipldbind.Node: cannot atlas over type %q; it's a %v", v.Type().Name(), v.Kind()))
	case // pointers: should've already been unwrapped
		reflect.Ptr:
		panic("unreachable")
	default:
		panic("unreachable")
150
	}
151 152
}

Eric Myhre's avatar
Eric Myhre committed
153
func (n Node) TraverseIndex(idx int) (ipld.Node, error) {
154
	panic("NYI")
155
}