Files
CHORUS/vendor/github.com/ipld/go-ipld-prime/node/bindnode/node.go
anthonyrawlins 9bdcbe0447 Integrate BACKBEAT SDK and resolve KACHING license validation
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>
2025-09-06 07:56:26 +10:00

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{}
}