 9bdcbe0447
			
		
	
	9bdcbe0447
	
	
	
		
			
			Major integrations and fixes: - Added BACKBEAT SDK integration for P2P operation timing - Implemented beat-aware status tracking for distributed operations - Added Docker secrets support for secure license management - Resolved KACHING license validation via HTTPS/TLS - Updated docker-compose configuration for clean stack deployment - Disabled rollback policies to prevent deployment failures - Added license credential storage (CHORUS-DEV-MULTI-001) Technical improvements: - BACKBEAT P2P operation tracking with phase management - Enhanced configuration system with file-based secrets - Improved error handling for license validation - Clean separation of KACHING and CHORUS deployment stacks 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
		
			
				
	
	
		
			1755 lines
		
	
	
		
			51 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			1755 lines
		
	
	
		
			51 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package bindnode
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"math"
 | |
| 	"reflect"
 | |
| 	"runtime"
 | |
| 	"strings"
 | |
| 
 | |
| 	"github.com/ipfs/go-cid"
 | |
| 	"github.com/ipld/go-ipld-prime/datamodel"
 | |
| 	cidlink "github.com/ipld/go-ipld-prime/linking/cid"
 | |
| 	"github.com/ipld/go-ipld-prime/node/basicnode"
 | |
| 	"github.com/ipld/go-ipld-prime/node/mixins"
 | |
| 	"github.com/ipld/go-ipld-prime/schema"
 | |
| )
 | |
| 
 | |
| // Assert that we implement all the interfaces as expected.
 | |
| // Grouped by the interfaces to implement, roughly.
 | |
| var (
 | |
| 	_ datamodel.NodePrototype = (*_prototype)(nil)
 | |
| 	_ schema.TypedPrototype   = (*_prototype)(nil)
 | |
| 	_ datamodel.NodePrototype = (*_prototypeRepr)(nil)
 | |
| 
 | |
| 	_ datamodel.Node   = (*_node)(nil)
 | |
| 	_ schema.TypedNode = (*_node)(nil)
 | |
| 	_ datamodel.Node   = (*_nodeRepr)(nil)
 | |
| 
 | |
| 	_ datamodel.Node     = (*_uintNode)(nil)
 | |
| 	_ schema.TypedNode   = (*_uintNode)(nil)
 | |
| 	_ datamodel.UintNode = (*_uintNode)(nil)
 | |
| 	_ datamodel.Node     = (*_uintNodeRepr)(nil)
 | |
| 	_ datamodel.UintNode = (*_uintNodeRepr)(nil)
 | |
| 
 | |
| 	_ datamodel.NodeBuilder   = (*_builder)(nil)
 | |
| 	_ datamodel.NodeBuilder   = (*_builderRepr)(nil)
 | |
| 	_ datamodel.NodeAssembler = (*_assembler)(nil)
 | |
| 	_ datamodel.NodeAssembler = (*_assemblerRepr)(nil)
 | |
| 
 | |
| 	_ datamodel.MapAssembler = (*_structAssembler)(nil)
 | |
| 	_ datamodel.MapAssembler = (*_structAssemblerRepr)(nil)
 | |
| 	_ datamodel.MapIterator  = (*_structIterator)(nil)
 | |
| 	_ datamodel.MapIterator  = (*_structIteratorRepr)(nil)
 | |
| 
 | |
| 	_ datamodel.ListAssembler = (*_listAssembler)(nil)
 | |
| 	_ datamodel.ListAssembler = (*_listAssemblerRepr)(nil)
 | |
| 	_ datamodel.ListIterator  = (*_listIterator)(nil)
 | |
| 	_ datamodel.ListIterator  = (*_tupleIteratorRepr)(nil)
 | |
| 
 | |
| 	_ datamodel.MapAssembler = (*_unionAssembler)(nil)
 | |
| 	_ datamodel.MapAssembler = (*_unionAssemblerRepr)(nil)
 | |
| 	_ datamodel.MapIterator  = (*_unionIterator)(nil)
 | |
| 	_ datamodel.MapIterator  = (*_unionIteratorRepr)(nil)
 | |
| )
 | |
| 
 | |
| type _prototype struct {
 | |
| 	cfg        config
 | |
| 	schemaType schema.Type
 | |
| 	goType     reflect.Type // non-pointer
 | |
| }
 | |
| 
 | |
| func (w *_prototype) NewBuilder() datamodel.NodeBuilder {
 | |
| 	return &_builder{_assembler{
 | |
| 		cfg:        w.cfg,
 | |
| 		schemaType: w.schemaType,
 | |
| 		val:        reflect.New(w.goType).Elem(),
 | |
| 	}}
 | |
| }
 | |
| 
 | |
| func (w *_prototype) Type() schema.Type {
 | |
| 	return w.schemaType
 | |
| }
 | |
| 
 | |
| func (w *_prototype) Representation() datamodel.NodePrototype {
 | |
| 	return (*_prototypeRepr)(w)
 | |
| }
 | |
| 
 | |
| type _node struct {
 | |
| 	cfg        config
 | |
| 	schemaType schema.Type
 | |
| 
 | |
| 	val reflect.Value // non-pointer
 | |
| }
 | |
| 
 | |
| // TODO: only expose TypedNode methods if the schema was explicit.
 | |
| // type _typedNode struct {
 | |
| // 	_node
 | |
| // }
 | |
| 
 | |
| func newNode(cfg config, schemaType schema.Type, val reflect.Value) schema.TypedNode {
 | |
| 	if schemaType.TypeKind() == schema.TypeKind_Int && nonPtrVal(val).Kind() == reflect.Uint64 {
 | |
| 		// special case for uint64 values so we can handle the >int64 range
 | |
| 		// we give this treatment to all uint64s, regardless of current value
 | |
| 		// because we have no guarantees the value won't change underneath us
 | |
| 		return &_uintNode{
 | |
| 			cfg:        cfg,
 | |
| 			schemaType: schemaType,
 | |
| 			val:        val,
 | |
| 		}
 | |
| 	}
 | |
| 	return &_node{cfg, schemaType, val}
 | |
| }
 | |
| 
 | |
| func (w *_node) Type() schema.Type {
 | |
| 	return w.schemaType
 | |
| }
 | |
| 
 | |
| func (w *_node) Representation() datamodel.Node {
 | |
| 	return (*_nodeRepr)(w)
 | |
| }
 | |
| 
 | |
| func (w *_node) Kind() datamodel.Kind {
 | |
| 	return actualKind(w.schemaType)
 | |
| }
 | |
| 
 | |
| // matching schema level types to data model kinds, since our Node and Builder
 | |
| // interfaces operate on kinds
 | |
| func compatibleKind(schemaType schema.Type, kind datamodel.Kind) error {
 | |
| 	switch sch := schemaType.(type) {
 | |
| 	case *schema.TypeAny:
 | |
| 		return nil
 | |
| 	default:
 | |
| 		actual := actualKind(sch) // ActsLike data model
 | |
| 		if actual == kind {
 | |
| 			return nil
 | |
| 		}
 | |
| 
 | |
| 		// Error
 | |
| 		methodName := ""
 | |
| 		if pc, _, _, ok := runtime.Caller(1); ok {
 | |
| 			if fn := runtime.FuncForPC(pc); fn != nil {
 | |
| 				methodName = fn.Name()
 | |
| 				// Go from "pkg/path.Type.Method" to just "Method".
 | |
| 				methodName = methodName[strings.LastIndexByte(methodName, '.')+1:]
 | |
| 			}
 | |
| 		}
 | |
| 		return datamodel.ErrWrongKind{
 | |
| 			TypeName:        schemaType.Name(),
 | |
| 			MethodName:      methodName,
 | |
| 			AppropriateKind: datamodel.KindSet{kind},
 | |
| 			ActualKind:      actual,
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func actualKind(schemaType schema.Type) datamodel.Kind {
 | |
| 	return schemaType.TypeKind().ActsLike()
 | |
| }
 | |
| 
 | |
| func nonPtrVal(val reflect.Value) reflect.Value {
 | |
| 	// TODO: support **T as well as *T?
 | |
| 	if val.Kind() == reflect.Ptr {
 | |
| 		if val.IsNil() {
 | |
| 			// TODO: error in this case?
 | |
| 			return reflect.Value{}
 | |
| 		}
 | |
| 		val = val.Elem()
 | |
| 	}
 | |
| 	return val
 | |
| }
 | |
| 
 | |
| func ptrVal(val reflect.Value) reflect.Value {
 | |
| 	if val.Kind() == reflect.Ptr {
 | |
| 		return val
 | |
| 	}
 | |
| 	return val.Addr()
 | |
| }
 | |
| 
 | |
| func nonPtrType(val reflect.Value) reflect.Type {
 | |
| 	typ := val.Type()
 | |
| 	if typ.Kind() == reflect.Ptr {
 | |
| 		return typ.Elem()
 | |
| 	}
 | |
| 	return typ
 | |
| }
 | |
| 
 | |
| // where we need to cal Set(), ensure the Value we're setting is a pointer or
 | |
| // not, depending on the field we're setting into.
 | |
| func matchSettable(val interface{}, to reflect.Value) reflect.Value {
 | |
| 	setVal := nonPtrVal(reflect.ValueOf(val))
 | |
| 	if !setVal.Type().AssignableTo(to.Type()) && setVal.Type().ConvertibleTo(to.Type()) {
 | |
| 		setVal = setVal.Convert(to.Type())
 | |
| 	}
 | |
| 	return setVal
 | |
| }
 | |
| 
 | |
| func (w *_node) LookupByString(key string) (datamodel.Node, error) {
 | |
| 	switch typ := w.schemaType.(type) {
 | |
| 	case *schema.TypeStruct:
 | |
| 		field := typ.Field(key)
 | |
| 		if field == nil {
 | |
| 			return nil, schema.ErrInvalidKey{
 | |
| 				TypeName: typ.Name(),
 | |
| 				Key:      basicnode.NewString(key),
 | |
| 			}
 | |
| 		}
 | |
| 		fval := nonPtrVal(w.val).FieldByName(fieldNameFromSchema(key))
 | |
| 		if !fval.IsValid() {
 | |
| 			return nil, fmt.Errorf("bindnode TODO: go-schema mismatch")
 | |
| 		}
 | |
| 		if field.IsOptional() {
 | |
| 			if fval.IsNil() {
 | |
| 				return datamodel.Absent, nil
 | |
| 			}
 | |
| 			if fval.Kind() == reflect.Ptr {
 | |
| 				fval = fval.Elem()
 | |
| 			}
 | |
| 		}
 | |
| 		if field.IsNullable() {
 | |
| 			if fval.IsNil() {
 | |
| 				return datamodel.Null, nil
 | |
| 			}
 | |
| 			if fval.Kind() == reflect.Ptr {
 | |
| 				fval = fval.Elem()
 | |
| 			}
 | |
| 		}
 | |
| 		if _, ok := field.Type().(*schema.TypeAny); ok {
 | |
| 			if customConverter := w.cfg.converterFor(fval); customConverter != nil {
 | |
| 				// field is an Any and we have a custom type converter for the type
 | |
| 				return customConverter.customToAny(ptrVal(fval).Interface())
 | |
| 			}
 | |
| 			// field is an Any, safely assume a Node in fval
 | |
| 			return nonPtrVal(fval).Interface().(datamodel.Node), nil
 | |
| 		}
 | |
| 		return newNode(w.cfg, field.Type(), fval), nil
 | |
| 	case *schema.TypeMap:
 | |
| 		// maps can only be structs with a Values map
 | |
| 		var kval reflect.Value
 | |
| 		valuesVal := nonPtrVal(w.val).FieldByName("Values")
 | |
| 		switch ktyp := typ.KeyType().(type) {
 | |
| 		case *schema.TypeString:
 | |
| 			// plain String keys, so safely use the map key as is
 | |
| 			kval = reflect.ValueOf(key)
 | |
| 		default:
 | |
| 			// key is something other than a string that we need to assemble via
 | |
| 			// the string representation form, use _assemblerRepr to reverse from
 | |
| 			// string to the type that indexes the map
 | |
| 			asm := &_assembler{
 | |
| 				cfg:        w.cfg,
 | |
| 				schemaType: ktyp,
 | |
| 				val:        reflect.New(valuesVal.Type().Key()).Elem(),
 | |
| 			}
 | |
| 			if err := (*_assemblerRepr)(asm).AssignString(key); err != nil {
 | |
| 				return nil, err
 | |
| 			}
 | |
| 			kval = asm.val
 | |
| 		}
 | |
| 		fval := valuesVal.MapIndex(kval)
 | |
| 		if !fval.IsValid() { // not found
 | |
| 			return nil, datamodel.ErrNotExists{Segment: datamodel.PathSegmentOfString(key)}
 | |
| 		}
 | |
| 		// TODO: Error/panic if fval.IsNil() && !typ.ValueIsNullable()?
 | |
| 		// Otherwise we could have two non-equal Go values (nil map,
 | |
| 		// non-nil-but-empty map) which represent the exact same IPLD
 | |
| 		// node when the field is not nullable.
 | |
| 		if typ.ValueIsNullable() {
 | |
| 			if fval.IsNil() {
 | |
| 				return datamodel.Null, nil
 | |
| 			}
 | |
| 			fval = fval.Elem()
 | |
| 		}
 | |
| 		if _, ok := typ.ValueType().(*schema.TypeAny); ok {
 | |
| 			if customConverter := w.cfg.converterFor(fval); customConverter != nil {
 | |
| 				// value is an Any and we have a custom type converter for the type
 | |
| 				return customConverter.customToAny(ptrVal(fval).Interface())
 | |
| 			}
 | |
| 			// value is an Any, safely assume a Node in fval
 | |
| 			return nonPtrVal(fval).Interface().(datamodel.Node), nil
 | |
| 		}
 | |
| 		return newNode(w.cfg, typ.ValueType(), fval), nil
 | |
| 	case *schema.TypeUnion:
 | |
| 		// treat a union similar to a struct, but we have the member names more
 | |
| 		// easily accessible to match to 'key'
 | |
| 		var idx int
 | |
| 		var mtyp schema.Type
 | |
| 		for i, member := range typ.Members() {
 | |
| 			if member.Name() == key {
 | |
| 				idx = i
 | |
| 				mtyp = member
 | |
| 				break
 | |
| 			}
 | |
| 		}
 | |
| 		if mtyp == nil { // not found
 | |
| 			return nil, datamodel.ErrNotExists{Segment: datamodel.PathSegmentOfString(key)}
 | |
| 		}
 | |
| 		// TODO: we could look up the right Go field straight away via idx.
 | |
| 		haveIdx, mval := unionMember(nonPtrVal(w.val))
 | |
| 		if haveIdx != idx { // mismatching type
 | |
| 			return nil, datamodel.ErrNotExists{Segment: datamodel.PathSegmentOfString(key)}
 | |
| 		}
 | |
| 		return newNode(w.cfg, mtyp, mval), nil
 | |
| 	}
 | |
| 	return nil, datamodel.ErrWrongKind{
 | |
| 		TypeName:        w.schemaType.Name(),
 | |
| 		MethodName:      "LookupByString",
 | |
| 		AppropriateKind: datamodel.KindSet_JustMap,
 | |
| 		ActualKind:      w.Kind(),
 | |
| 	}
 | |
| }
 | |
| 
 | |
| var invalidValue reflect.Value
 | |
| 
 | |
| // unionMember finds which union member is set in the corresponding Go struct.
 | |
| func unionMember(val reflect.Value) (int, reflect.Value) {
 | |
| 	// The first non-nil field is a match.
 | |
| 	for i := 0; i < val.NumField(); i++ {
 | |
| 		elemVal := val.Field(i)
 | |
| 		if elemVal.Kind() != reflect.Ptr {
 | |
| 			panic("bindnode bug: found unexpected non-pointer in a union field")
 | |
| 		}
 | |
| 		if elemVal.IsNil() {
 | |
| 			continue
 | |
| 		}
 | |
| 		return i, elemVal.Elem()
 | |
| 	}
 | |
| 	return -1, invalidValue
 | |
| }
 | |
| 
 | |
| func unionSetMember(val reflect.Value, memberIdx int, memberPtr reflect.Value) {
 | |
| 	// Reset the entire union struct to zero, to clear any non-nil pointers.
 | |
| 	val.Set(reflect.Zero(val.Type()))
 | |
| 
 | |
| 	// Set the index pointer to the given value.
 | |
| 	val.Field(memberIdx).Set(memberPtr)
 | |
| }
 | |
| 
 | |
| func (w *_node) LookupByIndex(idx int64) (datamodel.Node, error) {
 | |
| 	switch typ := w.schemaType.(type) {
 | |
| 	case *schema.TypeList:
 | |
| 		val := nonPtrVal(w.val)
 | |
| 		// we should be able assume that val is something we can Len() and Index()
 | |
| 		if idx < 0 || int(idx) >= val.Len() {
 | |
| 			return nil, datamodel.ErrNotExists{Segment: datamodel.PathSegmentOfInt(idx)}
 | |
| 		}
 | |
| 		val = val.Index(int(idx))
 | |
| 		_, isAny := typ.ValueType().(*schema.TypeAny)
 | |
| 		if isAny {
 | |
| 			if customConverter := w.cfg.converterFor(val); customConverter != nil {
 | |
| 				// values are Any and we have a converter for this type that will give us
 | |
| 				// a datamodel.Node
 | |
| 				return customConverter.customToAny(ptrVal(val).Interface())
 | |
| 			}
 | |
| 		}
 | |
| 		if typ.ValueIsNullable() {
 | |
| 			if val.IsNil() {
 | |
| 				return datamodel.Null, nil
 | |
| 			}
 | |
| 			// nullable elements are assumed to be pointers
 | |
| 			val = val.Elem()
 | |
| 		}
 | |
| 		if isAny {
 | |
| 			// Any always yields a plain datamodel.Node
 | |
| 			return nonPtrVal(val).Interface().(datamodel.Node), nil
 | |
| 		}
 | |
| 		return newNode(w.cfg, typ.ValueType(), val), nil
 | |
| 	}
 | |
| 	return nil, datamodel.ErrWrongKind{
 | |
| 		TypeName:        w.schemaType.Name(),
 | |
| 		MethodName:      "LookupByIndex",
 | |
| 		AppropriateKind: datamodel.KindSet_JustList,
 | |
| 		ActualKind:      w.Kind(),
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (w *_node) LookupBySegment(seg datamodel.PathSegment) (datamodel.Node, error) {
 | |
| 	switch w.Kind() {
 | |
| 	case datamodel.Kind_Map:
 | |
| 		return w.LookupByString(seg.String())
 | |
| 	case datamodel.Kind_List:
 | |
| 		idx, err := seg.Index()
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 		return w.LookupByIndex(idx)
 | |
| 	}
 | |
| 	return nil, datamodel.ErrWrongKind{
 | |
| 		TypeName:        w.schemaType.Name(),
 | |
| 		MethodName:      "LookupBySegment",
 | |
| 		AppropriateKind: datamodel.KindSet_Recursive,
 | |
| 		ActualKind:      w.Kind(),
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (w *_node) LookupByNode(key datamodel.Node) (datamodel.Node, error) {
 | |
| 	switch w.Kind() {
 | |
| 	case datamodel.Kind_Map:
 | |
| 		s, err := key.AsString()
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 		return w.LookupByString(s)
 | |
| 	case datamodel.Kind_List:
 | |
| 		i, err := key.AsInt()
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 		return w.LookupByIndex(i)
 | |
| 	}
 | |
| 	return nil, datamodel.ErrWrongKind{
 | |
| 		TypeName:        w.schemaType.Name(),
 | |
| 		MethodName:      "LookupByNode",
 | |
| 		AppropriateKind: datamodel.KindSet_Recursive,
 | |
| 		ActualKind:      w.Kind(),
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (w *_node) MapIterator() datamodel.MapIterator {
 | |
| 	val := nonPtrVal(w.val)
 | |
| 	// structs, unions and maps can all iterate but they each have different
 | |
| 	// access semantics for the underlying type, so we need a different iterator
 | |
| 	// for each
 | |
| 	switch typ := w.schemaType.(type) {
 | |
| 	case *schema.TypeStruct:
 | |
| 		return &_structIterator{
 | |
| 			cfg:        w.cfg,
 | |
| 			schemaType: typ,
 | |
| 			fields:     typ.Fields(),
 | |
| 			val:        val,
 | |
| 		}
 | |
| 	case *schema.TypeUnion:
 | |
| 		return &_unionIterator{
 | |
| 			cfg:        w.cfg,
 | |
| 			schemaType: typ,
 | |
| 			members:    typ.Members(),
 | |
| 			val:        val,
 | |
| 		}
 | |
| 	case *schema.TypeMap:
 | |
| 		// we can assume a: struct{Keys []string, Values map[x]y}
 | |
| 		return &_mapIterator{
 | |
| 			cfg:        w.cfg,
 | |
| 			schemaType: typ,
 | |
| 			keysVal:    val.FieldByName("Keys"),
 | |
| 			valuesVal:  val.FieldByName("Values"),
 | |
| 		}
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (w *_node) ListIterator() datamodel.ListIterator {
 | |
| 	val := nonPtrVal(w.val)
 | |
| 	switch typ := w.schemaType.(type) {
 | |
| 	case *schema.TypeList:
 | |
| 		return &_listIterator{cfg: w.cfg, schemaType: typ, val: val}
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (w *_node) Length() int64 {
 | |
| 	val := nonPtrVal(w.val)
 | |
| 	switch w.Kind() {
 | |
| 	case datamodel.Kind_Map:
 | |
| 		switch typ := w.schemaType.(type) {
 | |
| 		case *schema.TypeStruct:
 | |
| 			return int64(len(typ.Fields()))
 | |
| 		case *schema.TypeUnion:
 | |
| 			return 1
 | |
| 		}
 | |
| 		return int64(val.FieldByName("Keys").Len())
 | |
| 	case datamodel.Kind_List:
 | |
| 		return int64(val.Len())
 | |
| 	}
 | |
| 	return -1
 | |
| }
 | |
| 
 | |
| // TODO: better story around pointers and absent/null
 | |
| 
 | |
| func (w *_node) IsAbsent() bool {
 | |
| 	return false
 | |
| }
 | |
| 
 | |
| func (w *_node) IsNull() bool {
 | |
| 	return false
 | |
| }
 | |
| 
 | |
| // The AsX methods are matter of fetching the non-pointer form of the underlying
 | |
| // value and returning the appropriate Go type. The user may have registered
 | |
| // custom converters for the kind being converted, in which case the underlying
 | |
| // type may not be the type we need, but the converter will supply it for us.
 | |
| 
 | |
| func (w *_node) AsBool() (bool, error) {
 | |
| 	if err := compatibleKind(w.schemaType, datamodel.Kind_Bool); err != nil {
 | |
| 		return false, err
 | |
| 	}
 | |
| 	if customConverter := w.cfg.converterFor(w.val); customConverter != nil {
 | |
| 		// user has registered a converter that takes the underlying type and returns a bool
 | |
| 		return customConverter.customToBool(ptrVal(w.val).Interface())
 | |
| 	}
 | |
| 	return nonPtrVal(w.val).Bool(), nil
 | |
| }
 | |
| 
 | |
| func (w *_node) AsInt() (int64, error) {
 | |
| 	if err := compatibleKind(w.schemaType, datamodel.Kind_Int); err != nil {
 | |
| 		return 0, err
 | |
| 	}
 | |
| 	if customConverter := w.cfg.converterFor(w.val); customConverter != nil {
 | |
| 		// user has registered a converter that takes the underlying type and returns an int
 | |
| 		return customConverter.customToInt(ptrVal(w.val).Interface())
 | |
| 	}
 | |
| 	val := nonPtrVal(w.val)
 | |
| 	if kindUint[val.Kind()] {
 | |
| 		u := val.Uint()
 | |
| 		if u > math.MaxInt64 {
 | |
| 			return 0, fmt.Errorf("bindnode: integer overflow, %d is too large for an int64", u)
 | |
| 		}
 | |
| 		return int64(u), nil
 | |
| 	}
 | |
| 	return val.Int(), nil
 | |
| }
 | |
| 
 | |
| func (w *_node) AsFloat() (float64, error) {
 | |
| 	if err := compatibleKind(w.schemaType, datamodel.Kind_Float); err != nil {
 | |
| 		return 0, err
 | |
| 	}
 | |
| 	if customConverter := w.cfg.converterFor(w.val); customConverter != nil {
 | |
| 		// user has registered a converter that takes the underlying type and returns a float
 | |
| 		return customConverter.customToFloat(ptrVal(w.val).Interface())
 | |
| 	}
 | |
| 	return nonPtrVal(w.val).Float(), nil
 | |
| }
 | |
| 
 | |
| func (w *_node) AsString() (string, error) {
 | |
| 	if err := compatibleKind(w.schemaType, datamodel.Kind_String); err != nil {
 | |
| 		return "", err
 | |
| 	}
 | |
| 	if customConverter := w.cfg.converterFor(w.val); customConverter != nil {
 | |
| 		// user has registered a converter that takes the underlying type and returns a string
 | |
| 		return customConverter.customToString(ptrVal(w.val).Interface())
 | |
| 	}
 | |
| 	return nonPtrVal(w.val).String(), nil
 | |
| }
 | |
| 
 | |
| func (w *_node) AsBytes() ([]byte, error) {
 | |
| 	if err := compatibleKind(w.schemaType, datamodel.Kind_Bytes); err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	if customConverter := w.cfg.converterFor(w.val); customConverter != nil {
 | |
| 		// user has registered a converter that takes the underlying type and returns a []byte
 | |
| 		return customConverter.customToBytes(ptrVal(w.val).Interface())
 | |
| 	}
 | |
| 	return nonPtrVal(w.val).Bytes(), nil
 | |
| }
 | |
| 
 | |
| func (w *_node) AsLink() (datamodel.Link, error) {
 | |
| 	if err := compatibleKind(w.schemaType, datamodel.Kind_Link); err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	if customConverter := w.cfg.converterFor(w.val); customConverter != nil {
 | |
| 		// user has registered a converter that takes the underlying type and returns a cid.Cid
 | |
| 		cid, err := customConverter.customToLink(ptrVal(w.val).Interface())
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 		return cidlink.Link{Cid: cid}, nil
 | |
| 	}
 | |
| 	switch val := nonPtrVal(w.val).Interface().(type) {
 | |
| 	case datamodel.Link:
 | |
| 		return val, nil
 | |
| 	case cid.Cid:
 | |
| 		return cidlink.Link{Cid: val}, nil
 | |
| 	default:
 | |
| 		return nil, fmt.Errorf("bindnode: unexpected link type %T", val)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (w *_node) Prototype() datamodel.NodePrototype {
 | |
| 	return &_prototype{cfg: w.cfg, schemaType: w.schemaType, goType: w.val.Type()}
 | |
| }
 | |
| 
 | |
| type _builder struct {
 | |
| 	_assembler
 | |
| }
 | |
| 
 | |
| func (w *_builder) Build() datamodel.Node {
 | |
| 	// TODO: should we panic if no Assign call was made, just like codegen?
 | |
| 	return newNode(w.cfg, w.schemaType, w.val)
 | |
| }
 | |
| 
 | |
| func (w *_builder) Reset() {
 | |
| 	panic("bindnode TODO: Reset")
 | |
| }
 | |
| 
 | |
| type _assembler struct {
 | |
| 	cfg        config
 | |
| 	schemaType schema.Type
 | |
| 	val        reflect.Value // non-pointer
 | |
| 
 | |
| 	// finish is used as an optional post-assemble step.
 | |
| 	// For example, assigning to a kinded union uses a finish func
 | |
| 	// to set the right union member in the Go union struct,
 | |
| 	// which isn't known before the assemble has finished.
 | |
| 	finish func() error
 | |
| 
 | |
| 	nullable bool // true if field or map value is nullable
 | |
| }
 | |
| 
 | |
| // createNonPtrVal is used for Set() operations on the underlying value
 | |
| func (w *_assembler) createNonPtrVal() reflect.Value {
 | |
| 	val := w.val
 | |
| 	// TODO: if val is not a pointer, we reuse its value.
 | |
| 	// If it is a pointer, we allocate a new one and replace it.
 | |
| 	// We should probably never reuse the existing value.
 | |
| 
 | |
| 	// TODO: support **T as well as *T?
 | |
| 	if val.Kind() == reflect.Ptr {
 | |
| 		// TODO: Sometimes we call createNonPtrVal before an assignment actually
 | |
| 		// happens. Does that matter?
 | |
| 		// If it matters and we only want to modify the destination value on
 | |
| 		// success, then we should make use of the "finish" func.
 | |
| 		val.Set(reflect.New(val.Type().Elem()))
 | |
| 		val = val.Elem()
 | |
| 	}
 | |
| 	return val
 | |
| }
 | |
| 
 | |
| func (w *_assembler) Representation() datamodel.NodeAssembler {
 | |
| 	return (*_assemblerRepr)(w)
 | |
| }
 | |
| 
 | |
| // basicMapAssembler is for assembling basicnode values, it's only use is for
 | |
| // Any fields that end up needing a BeginMap()
 | |
| type basicMapAssembler struct {
 | |
| 	datamodel.MapAssembler
 | |
| 
 | |
| 	builder   datamodel.NodeBuilder
 | |
| 	parent    *_assembler
 | |
| 	converter *converter
 | |
| }
 | |
| 
 | |
| func (w *basicMapAssembler) Finish() error {
 | |
| 	if err := w.MapAssembler.Finish(); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	basicNode := w.builder.Build()
 | |
| 	if w.converter != nil {
 | |
| 		// we can assume an Any converter because basicMapAssembler is only for Any
 | |
| 		// the user has registered the ability to convert a datamodel.Node to the
 | |
| 		// underlying Go type which may not be a datamodel.Node
 | |
| 		typ, err := w.converter.customFromAny(basicNode)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		w.parent.createNonPtrVal().Set(matchSettable(typ, reflect.ValueOf(basicNode)))
 | |
| 	} else {
 | |
| 		w.parent.createNonPtrVal().Set(reflect.ValueOf(basicNode))
 | |
| 	}
 | |
| 	if w.parent.finish != nil {
 | |
| 		if err := w.parent.finish(); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (w *_assembler) BeginMap(sizeHint int64) (datamodel.MapAssembler, error) {
 | |
| 	switch typ := w.schemaType.(type) {
 | |
| 	case *schema.TypeAny:
 | |
| 		basicBuilder := basicnode.Prototype.Any.NewBuilder()
 | |
| 		mapAsm, err := basicBuilder.BeginMap(sizeHint)
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 		converter := w.cfg.converterFor(w.val)
 | |
| 		return &basicMapAssembler{MapAssembler: mapAsm, builder: basicBuilder, parent: w, converter: converter}, nil
 | |
| 	case *schema.TypeStruct:
 | |
| 		val := w.createNonPtrVal()
 | |
| 		// _structAssembler walks through the fields in order as the entries are
 | |
| 		// assembled, verifyCompatibility() should mean it's safe to assume that
 | |
| 		// they match the schema, but we need to keep track of the fields that are
 | |
| 		// set in case of premature Finish()
 | |
| 		doneFields := make([]bool, val.NumField())
 | |
| 		return &_structAssembler{
 | |
| 			cfg:        w.cfg,
 | |
| 			schemaType: typ,
 | |
| 			val:        val,
 | |
| 			doneFields: doneFields,
 | |
| 			finish:     w.finish,
 | |
| 		}, nil
 | |
| 	case *schema.TypeMap:
 | |
| 		// assume a struct{Keys []string, Values map[x]y} that we can fill with
 | |
| 		// _mapAssembler
 | |
| 		val := w.createNonPtrVal()
 | |
| 		keysVal := val.FieldByName("Keys")
 | |
| 		valuesVal := val.FieldByName("Values")
 | |
| 		if valuesVal.IsNil() {
 | |
| 			valuesVal.Set(reflect.MakeMap(valuesVal.Type()))
 | |
| 		}
 | |
| 		return &_mapAssembler{
 | |
| 			cfg:        w.cfg,
 | |
| 			schemaType: typ,
 | |
| 			keysVal:    keysVal,
 | |
| 			valuesVal:  valuesVal,
 | |
| 			finish:     w.finish,
 | |
| 		}, nil
 | |
| 	case *schema.TypeUnion:
 | |
| 		// we can use _unionAssembler to assemble a union as if it were a map with
 | |
| 		// a single entry
 | |
| 		val := w.createNonPtrVal()
 | |
| 		return &_unionAssembler{
 | |
| 			cfg:        w.cfg,
 | |
| 			schemaType: typ,
 | |
| 			val:        val,
 | |
| 			finish:     w.finish,
 | |
| 		}, nil
 | |
| 	}
 | |
| 	return nil, datamodel.ErrWrongKind{
 | |
| 		TypeName:        w.schemaType.Name(),
 | |
| 		MethodName:      "BeginMap",
 | |
| 		AppropriateKind: datamodel.KindSet_JustMap,
 | |
| 		ActualKind:      actualKind(w.schemaType),
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // basicListAssembler is for assembling basicnode values, it's only use is for
 | |
| // Any fields that end up needing a BeginList()
 | |
| type basicListAssembler struct {
 | |
| 	datamodel.ListAssembler
 | |
| 
 | |
| 	builder   datamodel.NodeBuilder
 | |
| 	parent    *_assembler
 | |
| 	converter *converter
 | |
| }
 | |
| 
 | |
| func (w *basicListAssembler) Finish() error {
 | |
| 	if err := w.ListAssembler.Finish(); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	basicNode := w.builder.Build()
 | |
| 	if w.converter != nil {
 | |
| 		// we can assume an Any converter because basicListAssembler is only for Any
 | |
| 		// the user has registered the ability to convert a datamodel.Node to the
 | |
| 		// underlying Go type which may not be a datamodel.Node
 | |
| 		typ, err := w.converter.customFromAny(basicNode)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		w.parent.createNonPtrVal().Set(matchSettable(typ, reflect.ValueOf(basicNode)))
 | |
| 	} else {
 | |
| 		w.parent.createNonPtrVal().Set(reflect.ValueOf(basicNode))
 | |
| 	}
 | |
| 	if w.parent.finish != nil {
 | |
| 		if err := w.parent.finish(); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (w *_assembler) BeginList(sizeHint int64) (datamodel.ListAssembler, error) {
 | |
| 	switch typ := w.schemaType.(type) {
 | |
| 	case *schema.TypeAny:
 | |
| 		basicBuilder := basicnode.Prototype.Any.NewBuilder()
 | |
| 		listAsm, err := basicBuilder.BeginList(sizeHint)
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 		converter := w.cfg.converterFor(w.val)
 | |
| 		return &basicListAssembler{ListAssembler: listAsm, builder: basicBuilder, parent: w, converter: converter}, nil
 | |
| 	case *schema.TypeList:
 | |
| 		// we should be able to safely assume we're dealing with a Go slice here,
 | |
| 		// so _listAssembler can append to that
 | |
| 		val := w.createNonPtrVal()
 | |
| 		return &_listAssembler{
 | |
| 			cfg:        w.cfg,
 | |
| 			schemaType: typ,
 | |
| 			val:        val,
 | |
| 			finish:     w.finish,
 | |
| 		}, nil
 | |
| 	}
 | |
| 	return nil, datamodel.ErrWrongKind{
 | |
| 		TypeName:        w.schemaType.Name(),
 | |
| 		MethodName:      "BeginList",
 | |
| 		AppropriateKind: datamodel.KindSet_JustList,
 | |
| 		ActualKind:      actualKind(w.schemaType),
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (w *_assembler) AssignNull() error {
 | |
| 	_, isAny := w.schemaType.(*schema.TypeAny)
 | |
| 	if customConverter := w.cfg.converterFor(w.val); customConverter != nil && isAny {
 | |
| 		// an Any field that is being assigned a Null, we pass the Null directly to
 | |
| 		// the converter, regardless of whether this field is nullable or not
 | |
| 		typ, err := customConverter.customFromAny(datamodel.Null)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		w.createNonPtrVal().Set(matchSettable(typ, w.val))
 | |
| 	} else {
 | |
| 		if !w.nullable {
 | |
| 			return datamodel.ErrWrongKind{
 | |
| 				TypeName:   w.schemaType.Name(),
 | |
| 				MethodName: "AssignNull",
 | |
| 				// TODO
 | |
| 			}
 | |
| 		}
 | |
| 		// set the zero value for the underlying type as a stand-in for Null
 | |
| 		w.val.Set(reflect.Zero(w.val.Type()))
 | |
| 	}
 | |
| 	if w.finish != nil {
 | |
| 		if err := w.finish(); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (w *_assembler) AssignBool(b bool) error {
 | |
| 	if err := compatibleKind(w.schemaType, datamodel.Kind_Bool); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	customConverter := w.cfg.converterFor(w.val)
 | |
| 	_, isAny := w.schemaType.(*schema.TypeAny)
 | |
| 	if customConverter != nil {
 | |
| 		var typ interface{}
 | |
| 		var err error
 | |
| 		if isAny {
 | |
| 			// field is an Any, so the converter will be an Any converter that wants
 | |
| 			// a datamodel.Node to convert to whatever the underlying Go type is
 | |
| 			if typ, err = customConverter.customFromAny(basicnode.NewBool(b)); err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 		} else {
 | |
| 			// field is a Bool, but the user has registered a converter from a bool to
 | |
| 			// whatever the underlying Go type is
 | |
| 			if typ, err = customConverter.customFromBool(b); err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 		}
 | |
| 		w.createNonPtrVal().Set(matchSettable(typ, w.val))
 | |
| 	} else {
 | |
| 		if isAny {
 | |
| 			// Any means the Go type must receive a datamodel.Node
 | |
| 			w.createNonPtrVal().Set(reflect.ValueOf(basicnode.NewBool(b)))
 | |
| 		} else {
 | |
| 			w.createNonPtrVal().SetBool(b)
 | |
| 		}
 | |
| 	}
 | |
| 	if w.finish != nil {
 | |
| 		if err := w.finish(); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (w *_assembler) assignUInt(uin datamodel.UintNode) error {
 | |
| 	if err := compatibleKind(w.schemaType, datamodel.Kind_Int); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	_, isAny := w.schemaType.(*schema.TypeAny)
 | |
| 	// TODO: customConverter for uint??
 | |
| 	if isAny {
 | |
| 		// Any means the Go type must receive a datamodel.Node
 | |
| 		w.createNonPtrVal().Set(reflect.ValueOf(uin))
 | |
| 	} else {
 | |
| 		i, err := uin.AsUint()
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		if kindUint[w.val.Kind()] {
 | |
| 			w.createNonPtrVal().SetUint(i)
 | |
| 		} else {
 | |
| 			// TODO: check for overflow
 | |
| 			w.createNonPtrVal().SetInt(int64(i))
 | |
| 		}
 | |
| 	}
 | |
| 	if w.finish != nil {
 | |
| 		if err := w.finish(); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (w *_assembler) AssignInt(i int64) error {
 | |
| 	if err := compatibleKind(w.schemaType, datamodel.Kind_Int); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	// TODO: check for overflow
 | |
| 	customConverter := w.cfg.converterFor(w.val)
 | |
| 	_, isAny := w.schemaType.(*schema.TypeAny)
 | |
| 	if customConverter != nil {
 | |
| 		var typ interface{}
 | |
| 		var err error
 | |
| 		if isAny {
 | |
| 			// field is an Any, so the converter will be an Any converter that wants
 | |
| 			// a datamodel.Node to convert to whatever the underlying Go type is
 | |
| 			if typ, err = customConverter.customFromAny(basicnode.NewInt(i)); err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 		} else {
 | |
| 			// field is an Int, but the user has registered a converter from an int to
 | |
| 			// whatever the underlying Go type is
 | |
| 			if typ, err = customConverter.customFromInt(i); err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 		}
 | |
| 		w.createNonPtrVal().Set(matchSettable(typ, w.val))
 | |
| 	} else {
 | |
| 		if isAny {
 | |
| 			// Any means the Go type must receive a datamodel.Node
 | |
| 			w.createNonPtrVal().Set(reflect.ValueOf(basicnode.NewInt(i)))
 | |
| 		} else if kindUint[w.val.Kind()] {
 | |
| 			if i < 0 {
 | |
| 				// TODO: write a test
 | |
| 				return fmt.Errorf("bindnode: cannot assign negative integer to %s", w.val.Type())
 | |
| 			}
 | |
| 			w.createNonPtrVal().SetUint(uint64(i))
 | |
| 		} else {
 | |
| 			w.createNonPtrVal().SetInt(i)
 | |
| 		}
 | |
| 	}
 | |
| 	if w.finish != nil {
 | |
| 		if err := w.finish(); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (w *_assembler) AssignFloat(f float64) error {
 | |
| 	if err := compatibleKind(w.schemaType, datamodel.Kind_Float); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	customConverter := w.cfg.converterFor(w.val)
 | |
| 	_, isAny := w.schemaType.(*schema.TypeAny)
 | |
| 	if customConverter != nil {
 | |
| 		var typ interface{}
 | |
| 		var err error
 | |
| 		if isAny {
 | |
| 			// field is an Any, so the converter will be an Any converter that wants
 | |
| 			// a datamodel.Node to convert to whatever the underlying Go type is
 | |
| 			if typ, err = customConverter.customFromAny(basicnode.NewFloat(f)); err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 		} else {
 | |
| 			// field is a Float, but the user has registered a converter from a float
 | |
| 			// to whatever the underlying Go type is
 | |
| 			if typ, err = customConverter.customFromFloat(f); err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 		}
 | |
| 		w.createNonPtrVal().Set(matchSettable(typ, w.val))
 | |
| 	} else {
 | |
| 		if isAny {
 | |
| 			// Any means the Go type must receive a datamodel.Node
 | |
| 			w.createNonPtrVal().Set(reflect.ValueOf(basicnode.NewFloat(f)))
 | |
| 		} else {
 | |
| 			w.createNonPtrVal().SetFloat(f)
 | |
| 		}
 | |
| 	}
 | |
| 	if w.finish != nil {
 | |
| 		if err := w.finish(); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (w *_assembler) AssignString(s string) error {
 | |
| 	if err := compatibleKind(w.schemaType, datamodel.Kind_String); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	customConverter := w.cfg.converterFor(w.val)
 | |
| 	_, isAny := w.schemaType.(*schema.TypeAny)
 | |
| 	if customConverter != nil {
 | |
| 		var typ interface{}
 | |
| 		var err error
 | |
| 		if isAny {
 | |
| 			// field is an Any, so the converter will be an Any converter that wants
 | |
| 			// a datamodel.Node to convert to whatever the underlying Go type is
 | |
| 			if typ, err = customConverter.customFromAny(basicnode.NewString(s)); err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 		} else {
 | |
| 			// field is a String, but the user has registered a converter from a
 | |
| 			// string to whatever the underlying Go type is
 | |
| 			if typ, err = customConverter.customFromString(s); err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 		}
 | |
| 		w.createNonPtrVal().Set(matchSettable(typ, w.val))
 | |
| 	} else {
 | |
| 		if isAny {
 | |
| 			// Any means the Go type must receive a datamodel.Node
 | |
| 			w.createNonPtrVal().Set(reflect.ValueOf(basicnode.NewString(s)))
 | |
| 		} else {
 | |
| 			w.createNonPtrVal().SetString(s)
 | |
| 		}
 | |
| 	}
 | |
| 	if w.finish != nil {
 | |
| 		if err := w.finish(); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (w *_assembler) AssignBytes(p []byte) error {
 | |
| 	if err := compatibleKind(w.schemaType, datamodel.Kind_Bytes); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	customConverter := w.cfg.converterFor(w.val)
 | |
| 	_, isAny := w.schemaType.(*schema.TypeAny)
 | |
| 	if customConverter != nil {
 | |
| 		var typ interface{}
 | |
| 		var err error
 | |
| 		if isAny {
 | |
| 			// field is an Any, so the converter will be an Any converter that wants
 | |
| 			// a datamodel.Node to convert to whatever the underlying Go type is
 | |
| 			if typ, err = customConverter.customFromAny(basicnode.NewBytes(p)); err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 		} else {
 | |
| 			// field is a Bytes, but the user has registered a converter from a []byte
 | |
| 			// to whatever the underlying Go type is
 | |
| 			if typ, err = customConverter.customFromBytes(p); err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 		}
 | |
| 		w.createNonPtrVal().Set(matchSettable(typ, w.val))
 | |
| 	} else {
 | |
| 		if isAny {
 | |
| 			// Any means the Go type must receive a datamodel.Node
 | |
| 			w.createNonPtrVal().Set(reflect.ValueOf(basicnode.NewBytes(p)))
 | |
| 		} else {
 | |
| 			w.createNonPtrVal().SetBytes(p)
 | |
| 		}
 | |
| 	}
 | |
| 	if w.finish != nil {
 | |
| 		if err := w.finish(); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (w *_assembler) AssignLink(link datamodel.Link) error {
 | |
| 	val := w.createNonPtrVal()
 | |
| 	// TODO: newVal.Type() panics if link==nil; add a test and fix.
 | |
| 	customConverter := w.cfg.converterFor(w.val)
 | |
| 	if _, ok := w.schemaType.(*schema.TypeAny); ok {
 | |
| 		if customConverter != nil {
 | |
| 			// field is an Any, so the converter will be an Any converter that wants
 | |
| 			// a datamodel.Node to convert to whatever the underlying Go type is
 | |
| 			typ, err := customConverter.customFromAny(basicnode.NewLink(link))
 | |
| 			if err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 			w.createNonPtrVal().Set(matchSettable(typ, w.val))
 | |
| 		} else {
 | |
| 			// Any means the Go type must receive a datamodel.Node
 | |
| 			val.Set(reflect.ValueOf(basicnode.NewLink(link)))
 | |
| 		}
 | |
| 	} else if customConverter != nil {
 | |
| 		if cl, ok := link.(cidlink.Link); ok {
 | |
| 			// field is a Link, but the user has registered a converter from a cid.Cid
 | |
| 			// to whatever the underlying Go type is
 | |
| 			typ, err := customConverter.customFromLink(cl.Cid)
 | |
| 			if err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 			w.createNonPtrVal().Set(matchSettable(typ, w.val))
 | |
| 		} else {
 | |
| 			return fmt.Errorf("bindnode: custom converter can only receive a cidlink.Link through AssignLink")
 | |
| 		}
 | |
| 	} else if newVal := reflect.ValueOf(link); newVal.Type().AssignableTo(val.Type()) {
 | |
| 		// Directly assignable.
 | |
| 		val.Set(newVal)
 | |
| 	} else if newVal.Type() == goTypeCidLink && goTypeCid.AssignableTo(val.Type()) {
 | |
| 		// Unbox a cidlink.Link to assign to a go-cid.Cid value.
 | |
| 		newVal = newVal.FieldByName("Cid")
 | |
| 		val.Set(newVal)
 | |
| 	} else if actual := actualKind(w.schemaType); actual != datamodel.Kind_Link {
 | |
| 		// We're assigning a Link to a schema type that isn't a Link.
 | |
| 		return datamodel.ErrWrongKind{
 | |
| 			TypeName:        w.schemaType.Name(),
 | |
| 			MethodName:      "AssignLink",
 | |
| 			AppropriateKind: datamodel.KindSet_JustLink,
 | |
| 			ActualKind:      actualKind(w.schemaType),
 | |
| 		}
 | |
| 	} else {
 | |
| 		// The schema type is a Link, but we somehow can't assign to the Go value.
 | |
| 		// Almost certainly a bug; we should have verified for compatibility upfront.
 | |
| 		return fmt.Errorf("bindnode bug: AssignLink with %s argument can't be used on Go type %s",
 | |
| 			newVal.Type(), val.Type())
 | |
| 	}
 | |
| 	if w.finish != nil {
 | |
| 		if err := w.finish(); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (w *_assembler) AssignNode(node datamodel.Node) error {
 | |
| 	// TODO: does this ever trigger?
 | |
| 	// newVal := reflect.ValueOf(node)
 | |
| 	// if newVal.Type().AssignableTo(w.val.Type()) {
 | |
| 	// 	w.val.Set(newVal)
 | |
| 	// 	return nil
 | |
| 	// }
 | |
| 	if uintNode, ok := node.(datamodel.UintNode); ok {
 | |
| 		return w.assignUInt(uintNode)
 | |
| 	}
 | |
| 	return datamodel.Copy(node, w)
 | |
| }
 | |
| 
 | |
| func (w *_assembler) Prototype() datamodel.NodePrototype {
 | |
| 	return &_prototype{cfg: w.cfg, schemaType: w.schemaType, goType: w.val.Type()}
 | |
| }
 | |
| 
 | |
| // _structAssembler is used for Struct assembling via BeginMap()
 | |
| type _structAssembler struct {
 | |
| 	// TODO: embed _assembler?
 | |
| 
 | |
| 	cfg config
 | |
| 
 | |
| 	schemaType *schema.TypeStruct
 | |
| 	val        reflect.Value // non-pointer
 | |
| 	finish     func() error
 | |
| 
 | |
| 	// TODO: more state checks
 | |
| 
 | |
| 	// TODO: Consider if we could do this in a cheaper way,
 | |
| 	// such as looking at the reflect.Value directly.
 | |
| 	// If not, at least avoid an extra alloc.
 | |
| 	doneFields []bool
 | |
| 
 | |
| 	// TODO: optimize for structs
 | |
| 
 | |
| 	curKey _assembler
 | |
| 
 | |
| 	nextIndex int // only used by repr.go
 | |
| }
 | |
| 
 | |
| func (w *_structAssembler) AssembleKey() datamodel.NodeAssembler {
 | |
| 	w.curKey = _assembler{
 | |
| 		cfg:        w.cfg,
 | |
| 		schemaType: schemaTypeString,
 | |
| 		val:        reflect.New(goTypeString).Elem(),
 | |
| 	}
 | |
| 	return &w.curKey
 | |
| }
 | |
| 
 | |
| func (w *_structAssembler) AssembleValue() datamodel.NodeAssembler {
 | |
| 	// TODO: optimize this to do one lookup by name
 | |
| 	name := w.curKey.val.String()
 | |
| 	field := w.schemaType.Field(name)
 | |
| 	if field == nil {
 | |
| 		// TODO: should've been raised when the key was submitted instead.
 | |
| 		// TODO: should make well-typed errors for this.
 | |
| 		return _errorAssembler{fmt.Errorf("bindnode TODO: invalid key: %q is not a field in type %s", name, w.schemaType.Name())}
 | |
| 		// panic(schema.ErrInvalidKey{
 | |
| 		// 	TypeName: w.schemaType.Name(),
 | |
| 		// 	Key:      basicnode.NewString(name),
 | |
| 		// })
 | |
| 	}
 | |
| 	ftyp, ok := w.val.Type().FieldByName(fieldNameFromSchema(name))
 | |
| 	if !ok {
 | |
| 		// It is unfortunate this is not detected proactively earlier during bind.
 | |
| 		return _errorAssembler{fmt.Errorf("schema type %q has field %q, we expect go struct to have field %q", w.schemaType.Name(), field.Name(), fieldNameFromSchema(name))}
 | |
| 	}
 | |
| 	if len(ftyp.Index) > 1 {
 | |
| 		return _errorAssembler{fmt.Errorf("bindnode TODO: embedded fields")}
 | |
| 	}
 | |
| 	w.doneFields[ftyp.Index[0]] = true
 | |
| 	fval := w.val.FieldByIndex(ftyp.Index)
 | |
| 	if field.IsOptional() {
 | |
| 		if fval.Kind() == reflect.Ptr {
 | |
| 			// ptrVal = new(T); val = *ptrVal
 | |
| 			fval.Set(reflect.New(fval.Type().Elem()))
 | |
| 			fval = fval.Elem()
 | |
| 		} else {
 | |
| 			// val = *new(T)
 | |
| 			fval.Set(reflect.New(fval.Type()).Elem())
 | |
| 		}
 | |
| 	}
 | |
| 	// TODO: reuse same assembler for perf?
 | |
| 	return &_assembler{
 | |
| 		cfg:        w.cfg,
 | |
| 		schemaType: field.Type(),
 | |
| 		val:        fval,
 | |
| 		nullable:   field.IsNullable(),
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (w *_structAssembler) AssembleEntry(k string) (datamodel.NodeAssembler, error) {
 | |
| 	if err := w.AssembleKey().AssignString(k); err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	am := w.AssembleValue()
 | |
| 	return am, nil
 | |
| }
 | |
| 
 | |
| func (w *_structAssembler) Finish() error {
 | |
| 	fields := w.schemaType.Fields()
 | |
| 	var missing []string
 | |
| 	for i, field := range fields {
 | |
| 		if !field.IsOptional() && !w.doneFields[i] {
 | |
| 			missing = append(missing, field.Name())
 | |
| 		}
 | |
| 	}
 | |
| 	if len(missing) > 0 {
 | |
| 		return schema.ErrMissingRequiredField{Missing: missing}
 | |
| 	}
 | |
| 	if w.finish != nil {
 | |
| 		if err := w.finish(); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (w *_structAssembler) KeyPrototype() datamodel.NodePrototype {
 | |
| 	// TODO: if the user provided their own schema with their own typesystem,
 | |
| 	// the schemaTypeString here may be using the wrong typesystem.
 | |
| 	return &_prototype{cfg: w.cfg, schemaType: schemaTypeString, goType: goTypeString}
 | |
| }
 | |
| 
 | |
| func (w *_structAssembler) ValuePrototype(k string) datamodel.NodePrototype {
 | |
| 	panic("bindnode TODO: struct ValuePrototype")
 | |
| }
 | |
| 
 | |
| type _errorAssembler struct {
 | |
| 	err error
 | |
| }
 | |
| 
 | |
| func (w _errorAssembler) BeginMap(int64) (datamodel.MapAssembler, error)   { return nil, w.err }
 | |
| func (w _errorAssembler) BeginList(int64) (datamodel.ListAssembler, error) { return nil, w.err }
 | |
| func (w _errorAssembler) AssignNull() error                                { return w.err }
 | |
| func (w _errorAssembler) AssignBool(bool) error                            { return w.err }
 | |
| func (w _errorAssembler) AssignInt(int64) error                            { return w.err }
 | |
| func (w _errorAssembler) AssignFloat(float64) error                        { return w.err }
 | |
| func (w _errorAssembler) AssignString(string) error                        { return w.err }
 | |
| func (w _errorAssembler) AssignBytes([]byte) error                         { return w.err }
 | |
| func (w _errorAssembler) AssignLink(datamodel.Link) error                  { return w.err }
 | |
| func (w _errorAssembler) AssignNode(datamodel.Node) error                  { return w.err }
 | |
| func (w _errorAssembler) Prototype() datamodel.NodePrototype               { return nil }
 | |
| 
 | |
| // used for Maps which we can assume are of type: struct{Keys []string, Values map[x]y},
 | |
| // where we have Keys in keysVal and Values in valuesVal
 | |
| type _mapAssembler struct {
 | |
| 	cfg        config
 | |
| 	schemaType *schema.TypeMap
 | |
| 	keysVal    reflect.Value // non-pointer
 | |
| 	valuesVal  reflect.Value // non-pointer
 | |
| 	finish     func() error
 | |
| 
 | |
| 	// TODO: more state checks
 | |
| 
 | |
| 	curKey _assembler
 | |
| }
 | |
| 
 | |
| func (w *_mapAssembler) AssembleKey() datamodel.NodeAssembler {
 | |
| 	w.curKey = _assembler{
 | |
| 		cfg:        w.cfg,
 | |
| 		schemaType: w.schemaType.KeyType(),
 | |
| 		val:        reflect.New(w.valuesVal.Type().Key()).Elem(),
 | |
| 	}
 | |
| 	return &w.curKey
 | |
| }
 | |
| 
 | |
| func (w *_mapAssembler) AssembleValue() datamodel.NodeAssembler {
 | |
| 	kval := w.curKey.val
 | |
| 	val := reflect.New(w.valuesVal.Type().Elem()).Elem()
 | |
| 	finish := func() error {
 | |
| 		// TODO: check for duplicates in keysVal
 | |
| 		w.keysVal.Set(reflect.Append(w.keysVal, kval))
 | |
| 
 | |
| 		w.valuesVal.SetMapIndex(kval, val)
 | |
| 		return nil
 | |
| 	}
 | |
| 	return &_assembler{
 | |
| 		cfg:        w.cfg,
 | |
| 		schemaType: w.schemaType.ValueType(),
 | |
| 		val:        val,
 | |
| 		nullable:   w.schemaType.ValueIsNullable(),
 | |
| 		finish:     finish,
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (w *_mapAssembler) AssembleEntry(k string) (datamodel.NodeAssembler, error) {
 | |
| 	if err := w.AssembleKey().AssignString(k); err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	am := w.AssembleValue()
 | |
| 	return am, nil
 | |
| }
 | |
| 
 | |
| func (w *_mapAssembler) Finish() error {
 | |
| 	if w.finish != nil {
 | |
| 		if err := w.finish(); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (w *_mapAssembler) KeyPrototype() datamodel.NodePrototype {
 | |
| 	return &_prototype{cfg: w.cfg, schemaType: w.schemaType.KeyType(), goType: w.valuesVal.Type().Key()}
 | |
| }
 | |
| 
 | |
| func (w *_mapAssembler) ValuePrototype(k string) datamodel.NodePrototype {
 | |
| 	return &_prototype{cfg: w.cfg, schemaType: w.schemaType.ValueType(), goType: w.valuesVal.Type().Elem()}
 | |
| }
 | |
| 
 | |
| // _listAssembler is for operating directly on slices, which we have in val
 | |
| type _listAssembler struct {
 | |
| 	cfg        config
 | |
| 	schemaType *schema.TypeList
 | |
| 	val        reflect.Value // non-pointer
 | |
| 	finish     func() error
 | |
| }
 | |
| 
 | |
| func (w *_listAssembler) AssembleValue() datamodel.NodeAssembler {
 | |
| 	goType := w.val.Type().Elem()
 | |
| 	// TODO: use a finish func to append
 | |
| 	w.val.Set(reflect.Append(w.val, reflect.New(goType).Elem()))
 | |
| 	return &_assembler{
 | |
| 		cfg:        w.cfg,
 | |
| 		schemaType: w.schemaType.ValueType(),
 | |
| 		val:        w.val.Index(w.val.Len() - 1),
 | |
| 		nullable:   w.schemaType.ValueIsNullable(),
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (w *_listAssembler) Finish() error {
 | |
| 	if w.finish != nil {
 | |
| 		if err := w.finish(); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (w *_listAssembler) ValuePrototype(idx int64) datamodel.NodePrototype {
 | |
| 	return &_prototype{cfg: w.cfg, schemaType: w.schemaType.ValueType(), goType: w.val.Type().Elem()}
 | |
| }
 | |
| 
 | |
| // when assembling as a Map but we anticipate a single value, which we need to
 | |
| // look up in the union members
 | |
| type _unionAssembler struct {
 | |
| 	cfg        config
 | |
| 	schemaType *schema.TypeUnion
 | |
| 	val        reflect.Value // non-pointer
 | |
| 	finish     func() error
 | |
| 
 | |
| 	// TODO: more state checks
 | |
| 
 | |
| 	curKey _assembler
 | |
| }
 | |
| 
 | |
| func (w *_unionAssembler) AssembleKey() datamodel.NodeAssembler {
 | |
| 	w.curKey = _assembler{
 | |
| 		cfg:        w.cfg,
 | |
| 		schemaType: schemaTypeString,
 | |
| 		val:        reflect.New(goTypeString).Elem(),
 | |
| 	}
 | |
| 	return &w.curKey
 | |
| }
 | |
| 
 | |
| func (w *_unionAssembler) AssembleValue() datamodel.NodeAssembler {
 | |
| 	name := w.curKey.val.String()
 | |
| 	var idx int
 | |
| 	var mtyp schema.Type
 | |
| 	for i, member := range w.schemaType.Members() {
 | |
| 		if member.Name() == name {
 | |
| 			idx = i
 | |
| 			mtyp = member
 | |
| 			break
 | |
| 		}
 | |
| 	}
 | |
| 	if mtyp == nil {
 | |
| 		return _errorAssembler{fmt.Errorf("bindnode TODO: missing member %s in %s", name, w.schemaType.Name())}
 | |
| 		// return nil, datamodel.ErrInvalidKey{
 | |
| 		// 	TypeName: w.schemaType.Name(),
 | |
| 		// 	Key:      basicnode.NewString(name),
 | |
| 		// }
 | |
| 	}
 | |
| 
 | |
| 	goType := w.val.Field(idx).Type().Elem()
 | |
| 	valPtr := reflect.New(goType)
 | |
| 	finish := func() error {
 | |
| 		unionSetMember(w.val, idx, valPtr)
 | |
| 		return nil
 | |
| 	}
 | |
| 	return &_assembler{
 | |
| 		cfg:        w.cfg,
 | |
| 		schemaType: mtyp,
 | |
| 		val:        valPtr.Elem(),
 | |
| 		finish:     finish,
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (w *_unionAssembler) AssembleEntry(k string) (datamodel.NodeAssembler, error) {
 | |
| 	if err := w.AssembleKey().AssignString(k); err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	am := w.AssembleValue()
 | |
| 	return am, nil
 | |
| }
 | |
| 
 | |
| func (w *_unionAssembler) Finish() error {
 | |
| 	// TODO(rvagg): I think this might allow setting multiple members of the union
 | |
| 	// we need a test for this.
 | |
| 	haveIdx, _ := unionMember(w.val)
 | |
| 	if haveIdx < 0 {
 | |
| 		return schema.ErrNotUnionStructure{TypeName: w.schemaType.Name(), Detail: "a union must have exactly one entry"}
 | |
| 	}
 | |
| 	if w.finish != nil {
 | |
| 		if err := w.finish(); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (w *_unionAssembler) KeyPrototype() datamodel.NodePrototype {
 | |
| 	return &_prototype{cfg: w.cfg, schemaType: schemaTypeString, goType: goTypeString}
 | |
| }
 | |
| 
 | |
| func (w *_unionAssembler) ValuePrototype(k string) datamodel.NodePrototype {
 | |
| 	panic("bindnode TODO: union ValuePrototype")
 | |
| }
 | |
| 
 | |
| // _structIterator is for iterating over Struct types which operate over Go
 | |
| // structs. The iteration order is dictated by Go field declaration order which
 | |
| // should match the schema for this type.
 | |
| type _structIterator struct {
 | |
| 	// TODO: support embedded fields?
 | |
| 	cfg config
 | |
| 
 | |
| 	schemaType *schema.TypeStruct
 | |
| 	fields     []schema.StructField
 | |
| 	val        reflect.Value // non-pointer
 | |
| 	nextIndex  int
 | |
| 
 | |
| 	// these are only used in repr.go
 | |
| 	reprEnd int
 | |
| }
 | |
| 
 | |
| func (w *_structIterator) Next() (key, value datamodel.Node, _ error) {
 | |
| 	if w.Done() {
 | |
| 		return nil, nil, datamodel.ErrIteratorOverread{}
 | |
| 	}
 | |
| 	field := w.fields[w.nextIndex]
 | |
| 	val := w.val.Field(w.nextIndex)
 | |
| 	w.nextIndex++
 | |
| 	key = basicnode.NewString(field.Name())
 | |
| 	if field.IsOptional() {
 | |
| 		if val.IsNil() {
 | |
| 			return key, datamodel.Absent, nil
 | |
| 		}
 | |
| 		if val.Kind() == reflect.Ptr {
 | |
| 			val = val.Elem()
 | |
| 		}
 | |
| 	}
 | |
| 	_, isAny := field.Type().(*schema.TypeAny)
 | |
| 	if isAny {
 | |
| 		if customConverter := w.cfg.converterFor(val); customConverter != nil {
 | |
| 			// field is an Any and we have an Any converter which takes the underlying
 | |
| 			// struct field value and returns a datamodel.Node
 | |
| 			v, err := customConverter.customToAny(ptrVal(val).Interface())
 | |
| 			if err != nil {
 | |
| 				return nil, nil, err
 | |
| 			}
 | |
| 			return key, v, nil
 | |
| 		}
 | |
| 	}
 | |
| 	if field.IsNullable() {
 | |
| 		if val.IsNil() {
 | |
| 			return key, datamodel.Null, nil
 | |
| 		}
 | |
| 		if val.Kind() == reflect.Ptr {
 | |
| 			val = val.Elem()
 | |
| 		}
 | |
| 	}
 | |
| 	if isAny {
 | |
| 		// field holds a datamodel.Node
 | |
| 		return key, nonPtrVal(val).Interface().(datamodel.Node), nil
 | |
| 	}
 | |
| 	return key, newNode(w.cfg, field.Type(), val), nil
 | |
| }
 | |
| 
 | |
| func (w *_structIterator) Done() bool {
 | |
| 	return w.nextIndex >= len(w.fields)
 | |
| }
 | |
| 
 | |
| // _mapIterator is for iterating over a struct{Keys []string, Values map[x]y},
 | |
| // where we have the Keys in keysVal and Values in valuesVal
 | |
| type _mapIterator struct {
 | |
| 	cfg        config
 | |
| 	schemaType *schema.TypeMap
 | |
| 	keysVal    reflect.Value // non-pointer
 | |
| 	valuesVal  reflect.Value // non-pointer
 | |
| 	nextIndex  int
 | |
| }
 | |
| 
 | |
| func (w *_mapIterator) Next() (key, value datamodel.Node, _ error) {
 | |
| 	if w.Done() {
 | |
| 		return nil, nil, datamodel.ErrIteratorOverread{}
 | |
| 	}
 | |
| 	goKey := w.keysVal.Index(w.nextIndex)
 | |
| 	val := w.valuesVal.MapIndex(goKey)
 | |
| 	w.nextIndex++
 | |
| 
 | |
| 	key = newNode(w.cfg, w.schemaType.KeyType(), goKey)
 | |
| 	_, isAny := w.schemaType.ValueType().(*schema.TypeAny)
 | |
| 	if isAny {
 | |
| 		if customConverter := w.cfg.converterFor(val); customConverter != nil {
 | |
| 			// values of this map are Any and we have an Any converter which takes the
 | |
| 			// underlying map value and returns a datamodel.Node
 | |
| 
 | |
| 			// TODO(rvagg): can't call ptrVal on a map value that's not a pointer
 | |
| 			// so only map[string]*foo will work for the Values map and an Any
 | |
| 			// converter. Should we check in infer.go?
 | |
| 			val, err := customConverter.customToAny(ptrVal(val).Interface())
 | |
| 			return key, val, err
 | |
| 		}
 | |
| 	}
 | |
| 	if w.schemaType.ValueIsNullable() {
 | |
| 		if val.IsNil() {
 | |
| 			return key, datamodel.Null, nil
 | |
| 		}
 | |
| 		val = val.Elem() // nullable entries are pointers
 | |
| 	}
 | |
| 	if isAny {
 | |
| 		// Values holds datamodel.Nodes
 | |
| 		return key, nonPtrVal(val).Interface().(datamodel.Node), nil
 | |
| 	}
 | |
| 	return key, newNode(w.cfg, w.schemaType.ValueType(), val), nil
 | |
| }
 | |
| 
 | |
| func (w *_mapIterator) Done() bool {
 | |
| 	return w.nextIndex >= w.keysVal.Len()
 | |
| }
 | |
| 
 | |
| // _listIterator is for iterating over slices, which is held in val
 | |
| type _listIterator struct {
 | |
| 	cfg        config
 | |
| 	schemaType *schema.TypeList
 | |
| 	val        reflect.Value // non-pointer
 | |
| 	nextIndex  int
 | |
| }
 | |
| 
 | |
| func (w *_listIterator) Next() (index int64, value datamodel.Node, _ error) {
 | |
| 	if w.Done() {
 | |
| 		return 0, nil, datamodel.ErrIteratorOverread{}
 | |
| 	}
 | |
| 	idx := int64(w.nextIndex)
 | |
| 	val := w.val.Index(w.nextIndex)
 | |
| 	w.nextIndex++
 | |
| 	if w.schemaType.ValueIsNullable() {
 | |
| 		if val.IsNil() {
 | |
| 			return idx, datamodel.Null, nil
 | |
| 		}
 | |
| 		val = val.Elem() // nullable values are pointers
 | |
| 	}
 | |
| 	if _, ok := w.schemaType.ValueType().(*schema.TypeAny); ok {
 | |
| 		if customConverter := w.cfg.converterFor(val); customConverter != nil {
 | |
| 			// values are Any and we have an Any converter which can take whatever
 | |
| 			// the underlying Go type in this slice is and return a datamodel.Node
 | |
| 			val, err := customConverter.customToAny(ptrVal(val).Interface())
 | |
| 			return idx, val, err
 | |
| 		}
 | |
| 		// values are Any, assume that they are datamodel.Nodes
 | |
| 		return idx, nonPtrVal(val).Interface().(datamodel.Node), nil
 | |
| 	}
 | |
| 	return idx, newNode(w.cfg, w.schemaType.ValueType(), val), nil
 | |
| }
 | |
| 
 | |
| func (w *_listIterator) Done() bool {
 | |
| 	return w.nextIndex >= w.val.Len()
 | |
| }
 | |
| 
 | |
| type _unionIterator struct {
 | |
| 	// TODO: support embedded fields?
 | |
| 	cfg        config
 | |
| 	schemaType *schema.TypeUnion
 | |
| 	members    []schema.Type
 | |
| 	val        reflect.Value // non-pointer
 | |
| 
 | |
| 	done bool
 | |
| }
 | |
| 
 | |
| func (w *_unionIterator) Next() (key, value datamodel.Node, _ error) {
 | |
| 	// we can only call this once for a union since a union can only have one
 | |
| 	// entry even though it behaves like a Map
 | |
| 	if w.Done() {
 | |
| 		return nil, nil, datamodel.ErrIteratorOverread{}
 | |
| 	}
 | |
| 	w.done = true
 | |
| 
 | |
| 	haveIdx, mval := unionMember(w.val)
 | |
| 	if haveIdx < 0 {
 | |
| 		return nil, nil, fmt.Errorf("bindnode: union %s has no member", w.val.Type())
 | |
| 	}
 | |
| 	mtyp := w.members[haveIdx]
 | |
| 
 | |
| 	node := newNode(w.cfg, mtyp, mval)
 | |
| 	key = basicnode.NewString(mtyp.Name())
 | |
| 	return key, node, nil
 | |
| }
 | |
| 
 | |
| func (w *_unionIterator) Done() bool {
 | |
| 	return w.done
 | |
| }
 | |
| 
 | |
| // --- uint64 special case handling
 | |
| 
 | |
| type _uintNode struct {
 | |
| 	cfg        config
 | |
| 	schemaType schema.Type
 | |
| 
 | |
| 	val reflect.Value // non-pointer
 | |
| }
 | |
| 
 | |
| func (tu *_uintNode) Type() schema.Type {
 | |
| 	return tu.schemaType
 | |
| }
 | |
| func (tu *_uintNode) Representation() datamodel.Node {
 | |
| 	return (*_uintNodeRepr)(tu)
 | |
| }
 | |
| func (_uintNode) Kind() datamodel.Kind {
 | |
| 	return datamodel.Kind_Int
 | |
| }
 | |
| func (_uintNode) LookupByString(string) (datamodel.Node, error) {
 | |
| 	return mixins.Int{TypeName: "int"}.LookupByString("")
 | |
| }
 | |
| func (_uintNode) LookupByNode(key datamodel.Node) (datamodel.Node, error) {
 | |
| 	return mixins.Int{TypeName: "int"}.LookupByNode(nil)
 | |
| }
 | |
| func (_uintNode) LookupByIndex(idx int64) (datamodel.Node, error) {
 | |
| 	return mixins.Int{TypeName: "int"}.LookupByIndex(0)
 | |
| }
 | |
| func (_uintNode) LookupBySegment(seg datamodel.PathSegment) (datamodel.Node, error) {
 | |
| 	return mixins.Int{TypeName: "int"}.LookupBySegment(seg)
 | |
| }
 | |
| func (_uintNode) MapIterator() datamodel.MapIterator {
 | |
| 	return nil
 | |
| }
 | |
| func (_uintNode) ListIterator() datamodel.ListIterator {
 | |
| 	return nil
 | |
| }
 | |
| func (_uintNode) Length() int64 {
 | |
| 	return -1
 | |
| }
 | |
| func (_uintNode) IsAbsent() bool {
 | |
| 	return false
 | |
| }
 | |
| func (_uintNode) IsNull() bool {
 | |
| 	return false
 | |
| }
 | |
| func (_uintNode) AsBool() (bool, error) {
 | |
| 	return mixins.Int{TypeName: "int"}.AsBool()
 | |
| }
 | |
| func (tu *_uintNode) AsInt() (int64, error) {
 | |
| 	return (*_uintNodeRepr)(tu).AsInt()
 | |
| }
 | |
| func (tu *_uintNode) AsUint() (uint64, error) {
 | |
| 	return (*_uintNodeRepr)(tu).AsUint()
 | |
| }
 | |
| func (_uintNode) AsFloat() (float64, error) {
 | |
| 	return mixins.Int{TypeName: "int"}.AsFloat()
 | |
| }
 | |
| func (_uintNode) AsString() (string, error) {
 | |
| 	return mixins.Int{TypeName: "int"}.AsString()
 | |
| }
 | |
| func (_uintNode) AsBytes() ([]byte, error) {
 | |
| 	return mixins.Int{TypeName: "int"}.AsBytes()
 | |
| }
 | |
| func (_uintNode) AsLink() (datamodel.Link, error) {
 | |
| 	return mixins.Int{TypeName: "int"}.AsLink()
 | |
| }
 | |
| func (_uintNode) Prototype() datamodel.NodePrototype {
 | |
| 	return basicnode.Prototype__Int{}
 | |
| }
 | |
| 
 | |
| // we need this for _uintNode#Representation() so we don't return a TypeNode
 | |
| type _uintNodeRepr _uintNode
 | |
| 
 | |
| func (_uintNodeRepr) Kind() datamodel.Kind {
 | |
| 	return datamodel.Kind_Int
 | |
| }
 | |
| func (_uintNodeRepr) LookupByString(string) (datamodel.Node, error) {
 | |
| 	return mixins.Int{TypeName: "int"}.LookupByString("")
 | |
| }
 | |
| func (_uintNodeRepr) LookupByNode(key datamodel.Node) (datamodel.Node, error) {
 | |
| 	return mixins.Int{TypeName: "int"}.LookupByNode(nil)
 | |
| }
 | |
| func (_uintNodeRepr) LookupByIndex(idx int64) (datamodel.Node, error) {
 | |
| 	return mixins.Int{TypeName: "int"}.LookupByIndex(0)
 | |
| }
 | |
| func (_uintNodeRepr) LookupBySegment(seg datamodel.PathSegment) (datamodel.Node, error) {
 | |
| 	return mixins.Int{TypeName: "int"}.LookupBySegment(seg)
 | |
| }
 | |
| func (_uintNodeRepr) MapIterator() datamodel.MapIterator {
 | |
| 	return nil
 | |
| }
 | |
| func (_uintNodeRepr) ListIterator() datamodel.ListIterator {
 | |
| 	return nil
 | |
| }
 | |
| func (_uintNodeRepr) Length() int64 {
 | |
| 	return -1
 | |
| }
 | |
| func (_uintNodeRepr) IsAbsent() bool {
 | |
| 	return false
 | |
| }
 | |
| func (_uintNodeRepr) IsNull() bool {
 | |
| 	return false
 | |
| }
 | |
| func (_uintNodeRepr) AsBool() (bool, error) {
 | |
| 	return mixins.Int{TypeName: "int"}.AsBool()
 | |
| }
 | |
| func (tu *_uintNodeRepr) AsInt() (int64, error) {
 | |
| 	if err := compatibleKind(tu.schemaType, datamodel.Kind_Int); err != nil {
 | |
| 		return 0, err
 | |
| 	}
 | |
| 	if customConverter := tu.cfg.converterFor(tu.val); customConverter != nil {
 | |
| 		// user has registered a converter that takes the underlying type and returns an int
 | |
| 		return customConverter.customToInt(ptrVal(tu.val).Interface())
 | |
| 	}
 | |
| 	val := nonPtrVal(tu.val)
 | |
| 	// we can assume it's a uint64 at this point
 | |
| 	u := val.Uint()
 | |
| 	if u > math.MaxInt64 {
 | |
| 		return 0, fmt.Errorf("bindnode: integer overflow, %d is too large for an int64", u)
 | |
| 	}
 | |
| 	return int64(u), nil
 | |
| }
 | |
| func (tu *_uintNodeRepr) AsUint() (uint64, error) {
 | |
| 	if err := compatibleKind(tu.schemaType, datamodel.Kind_Int); err != nil {
 | |
| 		return 0, err
 | |
| 	}
 | |
| 	// TODO(rvagg): do we want a converter option for uint values? do we combine it
 | |
| 	// with int converters?
 | |
| 	// we can assume it's a uint64 at this point
 | |
| 	return nonPtrVal(tu.val).Uint(), nil
 | |
| }
 | |
| func (_uintNodeRepr) AsFloat() (float64, error) {
 | |
| 	return mixins.Int{TypeName: "int"}.AsFloat()
 | |
| }
 | |
| func (_uintNodeRepr) AsString() (string, error) {
 | |
| 	return mixins.Int{TypeName: "int"}.AsString()
 | |
| }
 | |
| func (_uintNodeRepr) AsBytes() ([]byte, error) {
 | |
| 	return mixins.Int{TypeName: "int"}.AsBytes()
 | |
| }
 | |
| func (_uintNodeRepr) AsLink() (datamodel.Link, error) {
 | |
| 	return mixins.Int{TypeName: "int"}.AsLink()
 | |
| }
 | |
| func (_uintNodeRepr) Prototype() datamodel.NodePrototype {
 | |
| 	return basicnode.Prototype__Int{}
 | |
| }
 |