boundNode.go 5.78 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 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 74 75 76 77
func determineReprKind(rv reflect.Value) ipld.ReprKind {
	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
78 79 80
func (n Node) AsBool() (v bool, _ error) {
	reflect.ValueOf(&v).Elem().Set(n.bound)
	return
81
}
Eric Myhre's avatar
Eric Myhre committed
82 83 84
func (n Node) AsString() (v string, _ error) {
	reflect.ValueOf(&v).Elem().Set(n.bound)
	return
85
}
Eric Myhre's avatar
Eric Myhre committed
86 87 88
func (n Node) AsInt() (v int, _ error) {
	reflect.ValueOf(&v).Elem().Set(n.bound)
	return
89
}
Eric Myhre's avatar
Eric Myhre committed
90 91 92
func (n Node) AsLink() (v cid.Cid, _ error) {
	reflect.ValueOf(&v).Elem().Set(n.bound)
	return
93 94
}

95 96 97 98 99
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
100 101
func (n Node) TraverseField(pth string) (ipld.Node, error) {
	v := n.bound
102

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

154 155 156
	// Unwrap any pointers.
	for v.Kind() == reflect.Ptr {
		v = v.Elem()
157
	}
158 159 160 161 162 163

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

Eric Myhre's avatar
Eric Myhre committed
192
func (n Node) TraverseIndex(idx int) (ipld.Node, error) {
193
	panic("NYI")
194
}