api.go 2.89 KB
Newer Older
tavit ohanian's avatar
tavit ohanian committed
1
// Package bindnode provides an ld.Node implementation via Go reflection.
2 3 4 5 6
package bindnode

import (
	"reflect"

tavit ohanian's avatar
tavit ohanian committed
7
	ld "gitlab.dms3.io/ld/go-ld-prime"
tavit ohanian's avatar
tavit ohanian committed
8
	"gitlab.dms3.io/ld/go-ld-prime/schema"
9 10
)

Daniel Martí's avatar
Daniel Martí committed
11
// Prototype implements a schema.TypedPrototype given a Go pointer type and an
12
// LD schema type. Note that the result is also an ld.NodePrototype.
13 14 15 16 17 18 19 20 21 22 23 24 25
//
// If both the Go type and schema type are supplied, it is assumed that they are
// compatible with one another.
//
// If either the Go type or schema type are nil, we infer the missing type from
// the other provided type. For example, we can infer an unnamed Go struct type
// for a schema struct tyep, and we can infer a schema Int type for a Go int64
// type. The inferring logic is still a work in progress and subject to change.
//
// When supplying a non-nil ptrType, Prototype only obtains the Go pointer type
// from it, so its underlying value will typically be nil. For example:
//
//     proto := bindnode.Prototype((*goType)(nil), schemaType)
Daniel Martí's avatar
Daniel Martí committed
26
func Prototype(ptrType interface{}, schemaType schema.Type) schema.TypedPrototype {
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
	if ptrType == nil && schemaType == nil {
		panic("either ptrType or schemaType must not be nil")
	}

	// TODO: if both are supplied, verify that they are compatible

	var goType reflect.Type
	if ptrType == nil {
		goType = inferGoType(schemaType)
	} else {
		goPtrType := reflect.TypeOf(ptrType)
		if goPtrType.Kind() != reflect.Ptr {
			panic("ptrType must be a pointer")
		}
		goType = goPtrType.Elem()
	}

	if schemaType == nil {
		schemaType = inferSchema(goType)
	}

	return &_prototype{schemaType: schemaType, goType: goType}
}

// Wrap implements a schema.TypedNode given a non-nil pointer to a Go value and an
52
// LD schema type. Note that the result is also an ld.Node.
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
//
// Wrap is meant to be used when one already has a Go value with data.
// As such, ptrVal must not be nil.
//
// Similar to Prototype, if schemaType is non-nil it is assumed to be compatible
// with the Go type, and otherwise it's inferred from the Go type.
func Wrap(ptrVal interface{}, schemaType schema.Type) schema.TypedNode {
	if ptrVal == nil {
		panic("ptrVal must not be nil")
	}
	goPtrVal := reflect.ValueOf(ptrVal)
	if goPtrVal.Kind() != reflect.Ptr {
		panic("ptrVal must be a pointer")
	}
	if goPtrVal.IsNil() {
		panic("ptrVal must not be nil")
	}
	goVal := goPtrVal.Elem()
	if schemaType == nil {
		schemaType = inferSchema(goVal.Type())
	}
	return &_node{val: goVal, schemaType: schemaType}
}

tavit ohanian's avatar
tavit ohanian committed
77
// Unwrap takes an ld.Node implemented by Prototype or Wrap,
78 79 80
// and returns a pointer to the inner Go value.
//
// Unwrap returns nil if the node isn't implemented by this package.
tavit ohanian's avatar
tavit ohanian committed
81
func Unwrap(node ld.Node) (ptr interface{}) {
82 83 84 85 86 87 88 89 90 91 92 93 94 95
	var val reflect.Value
	switch node := node.(type) {
	case *_node:
		val = node.val
	case *_nodeRepr:
		val = node.val
	default:
		return nil
	}
	if val.Kind() == reflect.Ptr {
		panic("didn't expect val to be a pointer")
	}
	return val.Addr().Interface()
}