boundNode.go 5.86 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 {
25
	kind  ipld.ReprKind // compute during bind
Eric Myhre's avatar
Eric Myhre committed
26
	bound reflect.Value // should already be ptr-unwrapped
27 28
	atlas atlas.Atlas
}
29 30 31 32 33 34

/*
	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
35 36 37 38 39
	rv := reflect.ValueOf(bindme)
	for rv.Kind() == reflect.Ptr {
		rv = rv.Elem()
	}
	return Node{
40
		kind:  determineReprKind(rv),
Eric Myhre's avatar
Eric Myhre committed
41
		bound: rv,
42 43 44 45
		atlas: atl,
	}
}

46
func determineReprKind(rv reflect.Value) ipld.ReprKind {
Eric Myhre's avatar
Eric Myhre committed
47
	// TODO also handle nils
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
	switch rv.Type().Kind() {
	case reflect.Bool:
		return ipld.ReprKind_Bool
	case reflect.String:
		return ipld.ReprKind_String
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
		reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		return ipld.ReprKind_Int
	case reflect.Float32, reflect.Float64:
		return ipld.ReprKind_Float
	case reflect.Complex64, reflect.Complex128:
		panic(fmt.Errorf("invalid ipldbind.Node: ipld has no concept for complex numbers"))
	case reflect.Array, reflect.Slice:
		return ipld.ReprKind_List
	case reflect.Map, reflect.Struct:
		return ipld.ReprKind_Map
	case reflect.Interface:
		determineReprKind(rv.Elem())
	case reflect.Chan, reflect.Func, reflect.UnsafePointer:
		panic(fmt.Errorf("invalid ipldbind.Node: cannot atlas over type %q; it's a %v", rv.Type().Name(), rv.Kind()))
	case reflect.Ptr:
		// might've already been traversed during bind, but interface path can find more.
		determineReprKind(rv.Elem())
	}
	panic("unreachable")
}

func (n Node) Kind() ipld.ReprKind {
	return n.kind
}

Eric Myhre's avatar
Eric Myhre committed
79 80 81
func (n Node) IsNull() bool {
	return n.bound.IsNil()
}
Eric Myhre's avatar
Eric Myhre committed
82 83 84
func (n Node) AsBool() (v bool, _ error) {
	reflect.ValueOf(&v).Elem().Set(n.bound)
	return
85
}
Eric Myhre's avatar
Eric Myhre committed
86 87 88
func (n Node) AsString() (v string, _ error) {
	reflect.ValueOf(&v).Elem().Set(n.bound)
	return
89
}
Eric Myhre's avatar
Eric Myhre committed
90 91 92
func (n Node) AsInt() (v int, _ error) {
	reflect.ValueOf(&v).Elem().Set(n.bound)
	return
93
}
Eric Myhre's avatar
Eric Myhre committed
94 95 96
func (n Node) AsLink() (v cid.Cid, _ error) {
	reflect.ValueOf(&v).Elem().Set(n.bound)
	return
97 98
}

99 100 101 102 103
func (n Node) Keys() ([]string, int) {
	return nil, 0 // FIXME
	// TODO: REVIEW: structs have clear key order; maps do not.  what do?
}

Eric Myhre's avatar
Eric Myhre committed
104 105
func (n Node) TraverseField(pth string) (ipld.Node, error) {
	v := n.bound
106

107 108 109
	// Traverse.
	//  Honor any atlent overrides if present;
	//  Use kind-based fallbacks if necessary.
Eric Myhre's avatar
Eric Myhre committed
110
	atlent, exists := n.atlas.Get(reflect.ValueOf(v.Type()).Pointer())
111 112 113 114 115 116 117 118 119 120 121
	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
122
			return Node{}, fmt.Errorf("traverse failed: type %q has no field named %q", v.Type().Name(), pth)
123 124 125 126 127 128 129 130
		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 {
131 132 133 134 135 136 137 138
		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:
139 140 141 142 143 144 145
			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:
146
			return Node{determineReprKind(v), v, n.atlas}, nil
147 148 149 150 151 152 153 154
		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")
155 156 157
		}
	}

158 159 160
	// Unwrap any pointers.
	for v.Kind() == reflect.Ptr {
		v = v.Elem()
161
	}
162 163 164 165 166 167

	// 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
168
	case // primitives: wrap in node
169 170 171 172 173 174
		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:
175
		return Node{determineReprKind(v), v, n.atlas}, nil
176 177 178 179 180 181
	case // recursives: wrap in node
		reflect.Array,
		reflect.Slice,
		reflect.Map,
		reflect.Struct,
		reflect.Interface:
182
		return Node{determineReprKind(v), v, n.atlas}, nil
183 184 185 186 187 188 189 190 191 192
	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")
193
	}
194 195
}

Eric Myhre's avatar
Eric Myhre committed
196
func (n Node) TraverseIndex(idx int) (ipld.Node, error) {
197
	panic("NYI")
198
}