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

import (
4
	"fmt"
5 6 7 8 9 10 11
	"reflect"

	"github.com/ipld/go-ipld-prime"
	"github.com/polydawn/refmt/obj/atlas"
)

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

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

45
func determineReprKind(rv reflect.Value) ipld.ReprKind {
Eric Myhre's avatar
Eric Myhre committed
46
	// TODO also handle nils
47 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
	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")
}

Eric Myhre's avatar
Eric Myhre committed
74
func (n Node) ReprKind() ipld.ReprKind {
75 76 77
	return n.kind
}

Eric Myhre's avatar
Eric Myhre committed
78 79 80
func (n Node) IsNull() bool {
	return n.bound.IsNil()
}
Eric Myhre's avatar
Eric Myhre committed
81 82 83
func (n Node) AsBool() (v bool, _ error) {
	reflect.ValueOf(&v).Elem().Set(n.bound)
	return
84
}
85 86 87 88 89 90 91 92
func (n Node) AsInt() (v int, _ error) {
	reflect.ValueOf(&v).Elem().Set(n.bound)
	return
}
func (n Node) AsFloat() (v float64, _ error) {
	reflect.ValueOf(&v).Elem().Set(n.bound)
	return
}
Eric Myhre's avatar
Eric Myhre committed
93 94 95
func (n Node) AsString() (v string, _ error) {
	reflect.ValueOf(&v).Elem().Set(n.bound)
	return
96
}
97
func (n Node) AsBytes() (v []byte, _ error) {
Eric Myhre's avatar
Eric Myhre committed
98 99
	reflect.ValueOf(&v).Elem().Set(n.bound)
	return
100
}
101
func (n Node) AsLink() (v ipld.Link, _ error) {
Eric Myhre's avatar
Eric Myhre committed
102 103
	reflect.ValueOf(&v).Elem().Set(n.bound)
	return
104 105
}

106 107 108 109
func (n Node) NodeBuilder() ipld.NodeBuilder {
	panic("NYI")
}

110
func (n Node) MapIterator() ipld.MapIterator {
111 112 113
	panic("NYI")
}

114
func (n Node) ListIterator() ipld.ListIterator {
115 116 117 118 119
	panic("NYI")
}

func (n Node) Length() int {
	panic("NYI")
120 121
}

Eric Myhre's avatar
Eric Myhre committed
122 123
func (n Node) TraverseField(pth string) (ipld.Node, error) {
	v := n.bound
124

125 126 127
	// Traverse.
	//  Honor any atlent overrides if present;
	//  Use kind-based fallbacks if necessary.
Eric Myhre's avatar
Eric Myhre committed
128
	atlent, exists := n.atlas.Get(reflect.ValueOf(v.Type()).Pointer())
129 130 131 132 133 134 135 136 137 138 139
	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
140
			return Node{}, fmt.Errorf("traverse failed: type %q has no field named %q", v.Type().Name(), pth)
141 142 143 144 145 146 147 148
		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 {
149 150 151 152 153 154 155 156
		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:
157 158 159 160 161 162 163
			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:
164
			return Node{determineReprKind(v), v, n.atlas}, nil
165 166 167 168 169 170 171 172
		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")
173 174 175
		}
	}

176 177 178
	// Unwrap any pointers.
	for v.Kind() == reflect.Ptr {
		v = v.Elem()
179
	}
180 181 182 183 184 185

	// 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
186
	case // primitives: wrap in node
187 188 189 190 191 192
		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:
193
		return Node{determineReprKind(v), v, n.atlas}, nil
194 195 196 197 198 199
	case // recursives: wrap in node
		reflect.Array,
		reflect.Slice,
		reflect.Map,
		reflect.Struct,
		reflect.Interface:
200
		return Node{determineReprKind(v), v, n.atlas}, nil
201 202 203 204 205 206 207 208 209 210
	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")
211
	}
212 213
}

Eric Myhre's avatar
Eric Myhre committed
214
func (n Node) TraverseIndex(idx int) (ipld.Node, error) {
215
	panic("NYI")
216
}