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>
This commit is contained in:
335
vendor/github.com/ipld/go-ipld-prime/node/bindnode/api.go
generated
vendored
Normal file
335
vendor/github.com/ipld/go-ipld-prime/node/bindnode/api.go
generated
vendored
Normal file
@@ -0,0 +1,335 @@
|
||||
// Package bindnode provides a datamodel.Node implementation via Go reflection.
|
||||
//
|
||||
// This package is EXPERIMENTAL; its behavior and API might change as it's still
|
||||
// in development.
|
||||
package bindnode
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/ipfs/go-cid"
|
||||
"github.com/ipld/go-ipld-prime/datamodel"
|
||||
"github.com/ipld/go-ipld-prime/schema"
|
||||
)
|
||||
|
||||
// Prototype implements a schema.TypedPrototype given a Go pointer type and an
|
||||
// IPLD schema type. Note that the result is also a datamodel.NodePrototype.
|
||||
//
|
||||
// If both the Go type and schema type are supplied, it is assumed that they are
|
||||
// compatible with one another.
|
||||
//
|
||||
// If either the Go type or schema type are nil, we infer the missing type from
|
||||
// the other provided type. For example, we can infer an unnamed Go struct type
|
||||
// for a schema struct type, and we can infer a schema Int type for a Go int64
|
||||
// type. The inferring logic is still a work in progress and subject to change.
|
||||
// At this time, inferring IPLD Unions and Enums from Go types is not supported.
|
||||
//
|
||||
// When supplying a non-nil ptrType, Prototype only obtains the Go pointer type
|
||||
// from it, so its underlying value will typically be nil. For example:
|
||||
//
|
||||
// proto := bindnode.Prototype((*goType)(nil), schemaType)
|
||||
func Prototype(ptrType interface{}, schemaType schema.Type, options ...Option) schema.TypedPrototype {
|
||||
if ptrType == nil && schemaType == nil {
|
||||
panic("bindnode: either ptrType or schemaType must not be nil")
|
||||
}
|
||||
|
||||
cfg := applyOptions(options...)
|
||||
|
||||
// TODO: if both are supplied, verify that they are compatible
|
||||
|
||||
var goType reflect.Type
|
||||
if ptrType == nil {
|
||||
goType = inferGoType(schemaType, make(map[schema.TypeName]inferredStatus), 0)
|
||||
} else {
|
||||
goPtrType := reflect.TypeOf(ptrType)
|
||||
if goPtrType.Kind() != reflect.Ptr {
|
||||
panic("bindnode: ptrType must be a pointer")
|
||||
}
|
||||
goType = goPtrType.Elem()
|
||||
if goType.Kind() == reflect.Ptr {
|
||||
panic("bindnode: ptrType must not be a pointer to a pointer")
|
||||
}
|
||||
|
||||
if schemaType == nil {
|
||||
schemaType = inferSchema(goType, 0)
|
||||
} else {
|
||||
verifyCompatibility(cfg, make(map[seenEntry]bool), goType, schemaType)
|
||||
}
|
||||
}
|
||||
|
||||
return &_prototype{cfg: cfg, schemaType: schemaType, goType: goType}
|
||||
}
|
||||
|
||||
type converter struct {
|
||||
kind schema.TypeKind
|
||||
|
||||
customFromBool func(bool) (interface{}, error)
|
||||
customToBool func(interface{}) (bool, error)
|
||||
|
||||
customFromInt func(int64) (interface{}, error)
|
||||
customToInt func(interface{}) (int64, error)
|
||||
|
||||
customFromFloat func(float64) (interface{}, error)
|
||||
customToFloat func(interface{}) (float64, error)
|
||||
|
||||
customFromString func(string) (interface{}, error)
|
||||
customToString func(interface{}) (string, error)
|
||||
|
||||
customFromBytes func([]byte) (interface{}, error)
|
||||
customToBytes func(interface{}) ([]byte, error)
|
||||
|
||||
customFromLink func(cid.Cid) (interface{}, error)
|
||||
customToLink func(interface{}) (cid.Cid, error)
|
||||
|
||||
customFromAny func(datamodel.Node) (interface{}, error)
|
||||
customToAny func(interface{}) (datamodel.Node, error)
|
||||
}
|
||||
|
||||
type config map[reflect.Type]*converter
|
||||
|
||||
// this mainly exists to short-circuit the nonPtrType() call; the `Type()` variant
|
||||
// exists for completeness
|
||||
func (c config) converterFor(val reflect.Value) *converter {
|
||||
if len(c) == 0 {
|
||||
return nil
|
||||
}
|
||||
return c[nonPtrType(val)]
|
||||
}
|
||||
|
||||
func (c config) converterForType(typ reflect.Type) *converter {
|
||||
if len(c) == 0 {
|
||||
return nil
|
||||
}
|
||||
return c[typ]
|
||||
}
|
||||
|
||||
// Option is able to apply custom options to the bindnode API
|
||||
type Option func(config)
|
||||
|
||||
// TypedBoolConverter adds custom converter functions for a particular
|
||||
// type as identified by a pointer in the first argument.
|
||||
// The fromFunc is of the form: func(bool) (interface{}, error)
|
||||
// and toFunc is of the form: func(interface{}) (bool, error)
|
||||
// where interface{} is a pointer form of the type we are converting.
|
||||
//
|
||||
// TypedBoolConverter is an EXPERIMENTAL API and may be removed or
|
||||
// changed in a future release.
|
||||
func TypedBoolConverter(ptrVal interface{}, from func(bool) (interface{}, error), to func(interface{}) (bool, error)) Option {
|
||||
customType := nonPtrType(reflect.ValueOf(ptrVal))
|
||||
converter := &converter{
|
||||
kind: schema.TypeKind_Bool,
|
||||
customFromBool: from,
|
||||
customToBool: to,
|
||||
}
|
||||
return func(cfg config) {
|
||||
cfg[customType] = converter
|
||||
}
|
||||
}
|
||||
|
||||
// TypedIntConverter adds custom converter functions for a particular
|
||||
// type as identified by a pointer in the first argument.
|
||||
// The fromFunc is of the form: func(int64) (interface{}, error)
|
||||
// and toFunc is of the form: func(interface{}) (int64, error)
|
||||
// where interface{} is a pointer form of the type we are converting.
|
||||
//
|
||||
// TypedIntConverter is an EXPERIMENTAL API and may be removed or
|
||||
// changed in a future release.
|
||||
func TypedIntConverter(ptrVal interface{}, from func(int64) (interface{}, error), to func(interface{}) (int64, error)) Option {
|
||||
customType := nonPtrType(reflect.ValueOf(ptrVal))
|
||||
converter := &converter{
|
||||
kind: schema.TypeKind_Int,
|
||||
customFromInt: from,
|
||||
customToInt: to,
|
||||
}
|
||||
return func(cfg config) {
|
||||
cfg[customType] = converter
|
||||
}
|
||||
}
|
||||
|
||||
// TypedFloatConverter adds custom converter functions for a particular
|
||||
// type as identified by a pointer in the first argument.
|
||||
// The fromFunc is of the form: func(float64) (interface{}, error)
|
||||
// and toFunc is of the form: func(interface{}) (float64, error)
|
||||
// where interface{} is a pointer form of the type we are converting.
|
||||
//
|
||||
// TypedFloatConverter is an EXPERIMENTAL API and may be removed or
|
||||
// changed in a future release.
|
||||
func TypedFloatConverter(ptrVal interface{}, from func(float64) (interface{}, error), to func(interface{}) (float64, error)) Option {
|
||||
customType := nonPtrType(reflect.ValueOf(ptrVal))
|
||||
converter := &converter{
|
||||
kind: schema.TypeKind_Float,
|
||||
customFromFloat: from,
|
||||
customToFloat: to,
|
||||
}
|
||||
return func(cfg config) {
|
||||
cfg[customType] = converter
|
||||
}
|
||||
}
|
||||
|
||||
// TypedStringConverter adds custom converter functions for a particular
|
||||
// type as identified by a pointer in the first argument.
|
||||
// The fromFunc is of the form: func(string) (interface{}, error)
|
||||
// and toFunc is of the form: func(interface{}) (string, error)
|
||||
// where interface{} is a pointer form of the type we are converting.
|
||||
//
|
||||
// TypedStringConverter is an EXPERIMENTAL API and may be removed or
|
||||
// changed in a future release.
|
||||
func TypedStringConverter(ptrVal interface{}, from func(string) (interface{}, error), to func(interface{}) (string, error)) Option {
|
||||
customType := nonPtrType(reflect.ValueOf(ptrVal))
|
||||
converter := &converter{
|
||||
kind: schema.TypeKind_String,
|
||||
customFromString: from,
|
||||
customToString: to,
|
||||
}
|
||||
return func(cfg config) {
|
||||
cfg[customType] = converter
|
||||
}
|
||||
}
|
||||
|
||||
// TypedBytesConverter adds custom converter functions for a particular
|
||||
// type as identified by a pointer in the first argument.
|
||||
// The fromFunc is of the form: func([]byte) (interface{}, error)
|
||||
// and toFunc is of the form: func(interface{}) ([]byte, error)
|
||||
// where interface{} is a pointer form of the type we are converting.
|
||||
//
|
||||
// TypedBytesConverter is an EXPERIMENTAL API and may be removed or
|
||||
// changed in a future release.
|
||||
func TypedBytesConverter(ptrVal interface{}, from func([]byte) (interface{}, error), to func(interface{}) ([]byte, error)) Option {
|
||||
customType := nonPtrType(reflect.ValueOf(ptrVal))
|
||||
converter := &converter{
|
||||
kind: schema.TypeKind_Bytes,
|
||||
customFromBytes: from,
|
||||
customToBytes: to,
|
||||
}
|
||||
return func(cfg config) {
|
||||
cfg[customType] = converter
|
||||
}
|
||||
}
|
||||
|
||||
// TypedLinkConverter adds custom converter functions for a particular
|
||||
// type as identified by a pointer in the first argument.
|
||||
// The fromFunc is of the form: func([]byte) (interface{}, error)
|
||||
// and toFunc is of the form: func(interface{}) ([]byte, error)
|
||||
// where interface{} is a pointer form of the type we are converting.
|
||||
//
|
||||
// Beware that this API is only compatible with cidlink.Link types in the data
|
||||
// model and may result in errors if attempting to convert from other
|
||||
// datamodel.Link types.
|
||||
//
|
||||
// TypedLinkConverter is an EXPERIMENTAL API and may be removed or
|
||||
// changed in a future release.
|
||||
func TypedLinkConverter(ptrVal interface{}, from func(cid.Cid) (interface{}, error), to func(interface{}) (cid.Cid, error)) Option {
|
||||
customType := nonPtrType(reflect.ValueOf(ptrVal))
|
||||
converter := &converter{
|
||||
kind: schema.TypeKind_Link,
|
||||
customFromLink: from,
|
||||
customToLink: to,
|
||||
}
|
||||
return func(cfg config) {
|
||||
cfg[customType] = converter
|
||||
}
|
||||
}
|
||||
|
||||
// TypedAnyConverter adds custom converter functions for a particular
|
||||
// type as identified by a pointer in the first argument.
|
||||
// The fromFunc is of the form: func(datamodel.Node) (interface{}, error)
|
||||
// and toFunc is of the form: func(interface{}) (datamodel.Node, error)
|
||||
// where interface{} is a pointer form of the type we are converting.
|
||||
//
|
||||
// This method should be able to deal with all forms of Any and return an error
|
||||
// if the expected data forms don't match the expected.
|
||||
//
|
||||
// TypedAnyConverter is an EXPERIMENTAL API and may be removed or
|
||||
// changed in a future release.
|
||||
func TypedAnyConverter(ptrVal interface{}, from func(datamodel.Node) (interface{}, error), to func(interface{}) (datamodel.Node, error)) Option {
|
||||
customType := nonPtrType(reflect.ValueOf(ptrVal))
|
||||
converter := &converter{
|
||||
kind: schema.TypeKind_Any,
|
||||
customFromAny: from,
|
||||
customToAny: to,
|
||||
}
|
||||
return func(cfg config) {
|
||||
cfg[customType] = converter
|
||||
}
|
||||
}
|
||||
|
||||
func applyOptions(opt ...Option) config {
|
||||
if len(opt) == 0 {
|
||||
// no need to allocate, we access it via converterFor and converterForType
|
||||
// which are safe for nil maps
|
||||
return nil
|
||||
}
|
||||
cfg := make(map[reflect.Type]*converter)
|
||||
for _, o := range opt {
|
||||
o(cfg)
|
||||
}
|
||||
return cfg
|
||||
}
|
||||
|
||||
// Wrap implements a schema.TypedNode given a non-nil pointer to a Go value and an
|
||||
// IPLD schema type. Note that the result is also a datamodel.Node.
|
||||
//
|
||||
// Wrap is meant to be used when one already has a Go value with data.
|
||||
// As such, ptrVal must not be nil.
|
||||
//
|
||||
// Similar to Prototype, if schemaType is non-nil it is assumed to be compatible
|
||||
// with the Go type, and otherwise it's inferred from the Go type.
|
||||
func Wrap(ptrVal interface{}, schemaType schema.Type, options ...Option) schema.TypedNode {
|
||||
if ptrVal == nil {
|
||||
panic("bindnode: ptrVal must not be nil")
|
||||
}
|
||||
goPtrVal := reflect.ValueOf(ptrVal)
|
||||
if goPtrVal.Kind() != reflect.Ptr {
|
||||
panic("bindnode: ptrVal must be a pointer")
|
||||
}
|
||||
if goPtrVal.IsNil() {
|
||||
// Note that this can happen if ptrVal was a typed nil.
|
||||
panic("bindnode: ptrVal must not be nil")
|
||||
}
|
||||
cfg := applyOptions(options...)
|
||||
goVal := goPtrVal.Elem()
|
||||
if goVal.Kind() == reflect.Ptr {
|
||||
panic("bindnode: ptrVal must not be a pointer to a pointer")
|
||||
}
|
||||
if schemaType == nil {
|
||||
schemaType = inferSchema(goVal.Type(), 0)
|
||||
} else {
|
||||
// TODO(rvagg): explore ways to make this skippable by caching in the schema.Type
|
||||
// passed in to this function; e.g. if you call Prototype(), then you've gone through
|
||||
// this already, then calling .Type() on that could return a bindnode version of
|
||||
// schema.Type that has the config cached and can be assumed to have been checked or
|
||||
// inferred.
|
||||
verifyCompatibility(cfg, make(map[seenEntry]bool), goVal.Type(), schemaType)
|
||||
}
|
||||
return newNode(cfg, schemaType, goVal)
|
||||
}
|
||||
|
||||
// TODO: consider making our own Node interface, like:
|
||||
//
|
||||
// type WrappedNode interface {
|
||||
// datamodel.Node
|
||||
// Unwrap() (ptrVal interface)
|
||||
// }
|
||||
//
|
||||
// Pros: API is easier to understand, harder to mix up with other datamodel.Nodes.
|
||||
// Cons: One usually only has a datamodel.Node, and type assertions can be weird.
|
||||
|
||||
// Unwrap takes a datamodel.Node implemented by Prototype or Wrap,
|
||||
// and returns a pointer to the inner Go value.
|
||||
//
|
||||
// Unwrap returns nil if the node isn't implemented by this package.
|
||||
func Unwrap(node datamodel.Node) (ptrVal interface{}) {
|
||||
var val reflect.Value
|
||||
switch node := node.(type) {
|
||||
case *_node:
|
||||
val = node.val
|
||||
case *_nodeRepr:
|
||||
val = node.val
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
if val.Kind() == reflect.Ptr {
|
||||
panic("bindnode: didn't expect val to be a pointer")
|
||||
}
|
||||
return val.Addr().Interface()
|
||||
}
|
||||
139
vendor/github.com/ipld/go-ipld-prime/node/bindnode/generate.go
generated
vendored
Normal file
139
vendor/github.com/ipld/go-ipld-prime/node/bindnode/generate.go
generated
vendored
Normal file
@@ -0,0 +1,139 @@
|
||||
package bindnode
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/format"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/ipld/go-ipld-prime/schema"
|
||||
)
|
||||
|
||||
// TODO(mvdan): deduplicate with inferGoType once reflect supports creating named types
|
||||
|
||||
func produceGoType(goTypes map[string]string, typ schema.Type) (name, src string) {
|
||||
if typ, ok := typ.(interface{ IsAnonymous() bool }); ok {
|
||||
if typ.IsAnonymous() {
|
||||
panic("TODO: does this ever happen?")
|
||||
}
|
||||
}
|
||||
|
||||
name = string(typ.Name())
|
||||
|
||||
switch typ.(type) {
|
||||
case *schema.TypeBool:
|
||||
return goTypeBool.String(), ""
|
||||
case *schema.TypeInt:
|
||||
return goTypeInt.String(), ""
|
||||
case *schema.TypeFloat:
|
||||
return goTypeFloat.String(), ""
|
||||
case *schema.TypeString:
|
||||
return goTypeString.String(), ""
|
||||
case *schema.TypeBytes:
|
||||
return goTypeBytes.String(), ""
|
||||
case *schema.TypeLink:
|
||||
return goTypeLink.String(), "" // datamodel.Link
|
||||
case *schema.TypeAny:
|
||||
return goTypeNode.String(), "" // datamodel.Node
|
||||
}
|
||||
|
||||
// Results are cached in goTypes.
|
||||
if src := goTypes[name]; src != "" {
|
||||
return name, src
|
||||
}
|
||||
|
||||
src = produceGoTypeInner(goTypes, name, typ)
|
||||
goTypes[name] = src
|
||||
return name, src
|
||||
}
|
||||
|
||||
func produceGoTypeInner(goTypes map[string]string, name string, typ schema.Type) (src string) {
|
||||
// Avoid infinite cycles.
|
||||
// produceGoType will fill in the final type later.
|
||||
goTypes[name] = "WIP"
|
||||
|
||||
switch typ := typ.(type) {
|
||||
case *schema.TypeEnum:
|
||||
// TODO: also generate named constants for the members.
|
||||
return goTypeString.String()
|
||||
case *schema.TypeStruct:
|
||||
var b strings.Builder
|
||||
fmt.Fprintf(&b, "struct {\n")
|
||||
fields := typ.Fields()
|
||||
for _, field := range fields {
|
||||
fmt.Fprintf(&b, "%s ", fieldNameFromSchema(field.Name()))
|
||||
ftypGo, _ := produceGoType(goTypes, field.Type())
|
||||
if field.IsNullable() {
|
||||
fmt.Fprintf(&b, "*")
|
||||
}
|
||||
if field.IsOptional() {
|
||||
fmt.Fprintf(&b, "*")
|
||||
}
|
||||
fmt.Fprintf(&b, "%s\n", ftypGo)
|
||||
}
|
||||
fmt.Fprintf(&b, "\n}")
|
||||
return b.String()
|
||||
case *schema.TypeMap:
|
||||
ktyp, _ := produceGoType(goTypes, typ.KeyType())
|
||||
vtyp, _ := produceGoType(goTypes, typ.ValueType())
|
||||
if typ.ValueIsNullable() {
|
||||
vtyp = "*" + vtyp
|
||||
}
|
||||
return fmt.Sprintf(`struct {
|
||||
Keys []%s
|
||||
Values map[%s]%s
|
||||
}`, ktyp, ktyp, vtyp)
|
||||
case *schema.TypeList:
|
||||
etyp, _ := produceGoType(goTypes, typ.ValueType())
|
||||
if typ.ValueIsNullable() {
|
||||
etyp = "*" + etyp
|
||||
}
|
||||
return fmt.Sprintf("[]%s", etyp)
|
||||
case *schema.TypeUnion:
|
||||
var b strings.Builder
|
||||
fmt.Fprintf(&b, "struct{\n")
|
||||
members := typ.Members()
|
||||
for _, ftyp := range members {
|
||||
ftypGo, _ := produceGoType(goTypes, ftyp)
|
||||
fmt.Fprintf(&b, "%s ", fieldNameFromSchema(string(ftyp.Name())))
|
||||
fmt.Fprintf(&b, "*%s\n", ftypGo)
|
||||
}
|
||||
fmt.Fprintf(&b, "\n}")
|
||||
return b.String()
|
||||
}
|
||||
panic(fmt.Sprintf("%T\n", typ))
|
||||
}
|
||||
|
||||
// ProduceGoTypes infers Go types from an IPLD schema in ts
|
||||
// and writes their Go source code type declarations to w.
|
||||
// Note that just the types are written,
|
||||
// without a package declaration nor any imports.
|
||||
//
|
||||
// This gives a good starting point when wanting to use bindnode with Go types,
|
||||
// but users will generally want to own and modify the types afterward,
|
||||
// so they can add documentation or tweak the types as needed.
|
||||
func ProduceGoTypes(w io.Writer, ts *schema.TypeSystem) error {
|
||||
goTypes := make(map[string]string)
|
||||
var buf bytes.Buffer
|
||||
for _, name := range ts.Names() {
|
||||
schemaType := ts.TypeByName(string(name))
|
||||
if name != schemaType.Name() {
|
||||
panic(fmt.Sprintf("%s vs %s", name, schemaType.Name()))
|
||||
}
|
||||
_, src := produceGoType(goTypes, schemaType)
|
||||
if src == "" {
|
||||
continue // scalar type used directly
|
||||
}
|
||||
fmt.Fprintf(&buf, "type %s %s\n", name, src)
|
||||
}
|
||||
|
||||
src, err := format.Source(buf.Bytes())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := w.Write(src); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
507
vendor/github.com/ipld/go-ipld-prime/node/bindnode/infer.go
generated
vendored
Normal file
507
vendor/github.com/ipld/go-ipld-prime/node/bindnode/infer.go
generated
vendored
Normal file
@@ -0,0 +1,507 @@
|
||||
package bindnode
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/token"
|
||||
"reflect"
|
||||
"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/schema"
|
||||
)
|
||||
|
||||
var (
|
||||
goTypeBool = reflect.TypeOf(false)
|
||||
goTypeInt = reflect.TypeOf(int(0))
|
||||
goTypeFloat = reflect.TypeOf(0.0)
|
||||
goTypeString = reflect.TypeOf("")
|
||||
goTypeBytes = reflect.TypeOf([]byte{})
|
||||
goTypeLink = reflect.TypeOf((*datamodel.Link)(nil)).Elem()
|
||||
goTypeNode = reflect.TypeOf((*datamodel.Node)(nil)).Elem()
|
||||
goTypeCidLink = reflect.TypeOf((*cidlink.Link)(nil)).Elem()
|
||||
goTypeCid = reflect.TypeOf((*cid.Cid)(nil)).Elem()
|
||||
|
||||
schemaTypeBool = schema.SpawnBool("Bool")
|
||||
schemaTypeInt = schema.SpawnInt("Int")
|
||||
schemaTypeFloat = schema.SpawnFloat("Float")
|
||||
schemaTypeString = schema.SpawnString("String")
|
||||
schemaTypeBytes = schema.SpawnBytes("Bytes")
|
||||
schemaTypeLink = schema.SpawnLink("Link")
|
||||
schemaTypeAny = schema.SpawnAny("Any")
|
||||
)
|
||||
|
||||
// Consider exposing these APIs later, if they might be useful.
|
||||
|
||||
type seenEntry struct {
|
||||
goType reflect.Type
|
||||
schemaType schema.Type
|
||||
}
|
||||
|
||||
// verifyCompatibility is the primary way we check that the schema type(s)
|
||||
// matches the Go type(s); so we do this before we can proceed operating on it.
|
||||
// verifyCompatibility doesn't return an error, it panics—the errors here are
|
||||
// not runtime errors, they're programmer errors because your schema doesn't
|
||||
// match your Go type
|
||||
func verifyCompatibility(cfg config, seen map[seenEntry]bool, goType reflect.Type, schemaType schema.Type) {
|
||||
// TODO(mvdan): support **T as well?
|
||||
if goType.Kind() == reflect.Ptr {
|
||||
goType = goType.Elem()
|
||||
}
|
||||
|
||||
// Avoid endless loops.
|
||||
//
|
||||
// TODO(mvdan): this is easy but fairly allocation-happy.
|
||||
// Plus, one map per call means we don't reuse work.
|
||||
if seen[seenEntry{goType, schemaType}] {
|
||||
return
|
||||
}
|
||||
seen[seenEntry{goType, schemaType}] = true
|
||||
|
||||
doPanic := func(format string, args ...interface{}) {
|
||||
panicFormat := "bindnode: schema type %s is not compatible with Go type %s"
|
||||
panicArgs := []interface{}{schemaType.Name(), goType.String()}
|
||||
|
||||
if format != "" {
|
||||
panicFormat += ": " + format
|
||||
}
|
||||
panicArgs = append(panicArgs, args...)
|
||||
panic(fmt.Sprintf(panicFormat, panicArgs...))
|
||||
}
|
||||
switch schemaType := schemaType.(type) {
|
||||
case *schema.TypeBool:
|
||||
if customConverter := cfg.converterForType(goType); customConverter != nil {
|
||||
if customConverter.kind != schema.TypeKind_Bool {
|
||||
doPanic("kind mismatch; custom converter for type is not for Bool")
|
||||
}
|
||||
} else if goType.Kind() != reflect.Bool {
|
||||
doPanic("kind mismatch; need boolean")
|
||||
}
|
||||
case *schema.TypeInt:
|
||||
if customConverter := cfg.converterForType(goType); customConverter != nil {
|
||||
if customConverter.kind != schema.TypeKind_Int {
|
||||
doPanic("kind mismatch; custom converter for type is not for Int")
|
||||
}
|
||||
} else if kind := goType.Kind(); !kindInt[kind] && !kindUint[kind] {
|
||||
doPanic("kind mismatch; need integer")
|
||||
}
|
||||
case *schema.TypeFloat:
|
||||
if customConverter := cfg.converterForType(goType); customConverter != nil {
|
||||
if customConverter.kind != schema.TypeKind_Float {
|
||||
doPanic("kind mismatch; custom converter for type is not for Float")
|
||||
}
|
||||
} else {
|
||||
switch goType.Kind() {
|
||||
case reflect.Float32, reflect.Float64:
|
||||
default:
|
||||
doPanic("kind mismatch; need float")
|
||||
}
|
||||
}
|
||||
case *schema.TypeString:
|
||||
// TODO: allow []byte?
|
||||
if customConverter := cfg.converterForType(goType); customConverter != nil {
|
||||
if customConverter.kind != schema.TypeKind_String {
|
||||
doPanic("kind mismatch; custom converter for type is not for String")
|
||||
}
|
||||
} else if goType.Kind() != reflect.String {
|
||||
doPanic("kind mismatch; need string")
|
||||
}
|
||||
case *schema.TypeBytes:
|
||||
// TODO: allow string?
|
||||
if customConverter := cfg.converterForType(goType); customConverter != nil {
|
||||
if customConverter.kind != schema.TypeKind_Bytes {
|
||||
doPanic("kind mismatch; custom converter for type is not for Bytes")
|
||||
}
|
||||
} else if goType.Kind() != reflect.Slice {
|
||||
doPanic("kind mismatch; need slice of bytes")
|
||||
} else if goType.Elem().Kind() != reflect.Uint8 {
|
||||
doPanic("kind mismatch; need slice of bytes")
|
||||
}
|
||||
case *schema.TypeEnum:
|
||||
if _, ok := schemaType.RepresentationStrategy().(schema.EnumRepresentation_Int); ok {
|
||||
if kind := goType.Kind(); kind != reflect.String && !kindInt[kind] && !kindUint[kind] {
|
||||
doPanic("kind mismatch; need string or integer")
|
||||
}
|
||||
} else {
|
||||
if goType.Kind() != reflect.String {
|
||||
doPanic("kind mismatch; need string")
|
||||
}
|
||||
}
|
||||
case *schema.TypeList:
|
||||
if goType.Kind() != reflect.Slice {
|
||||
doPanic("kind mismatch; need slice")
|
||||
}
|
||||
goType = goType.Elem()
|
||||
if schemaType.ValueIsNullable() {
|
||||
if ptr, nilable := ptrOrNilable(goType.Kind()); !nilable {
|
||||
doPanic("nullable types must be nilable")
|
||||
} else if ptr {
|
||||
goType = goType.Elem()
|
||||
}
|
||||
}
|
||||
verifyCompatibility(cfg, seen, goType, schemaType.ValueType())
|
||||
case *schema.TypeMap:
|
||||
// struct {
|
||||
// Keys []K
|
||||
// Values map[K]V
|
||||
// }
|
||||
if goType.Kind() != reflect.Struct {
|
||||
doPanic("kind mismatch; need struct{Keys []K; Values map[K]V}")
|
||||
}
|
||||
if goType.NumField() != 2 {
|
||||
doPanic("%d vs 2 fields", goType.NumField())
|
||||
}
|
||||
|
||||
fieldKeys := goType.Field(0)
|
||||
if fieldKeys.Type.Kind() != reflect.Slice {
|
||||
doPanic("kind mismatch; need struct{Keys []K; Values map[K]V}")
|
||||
}
|
||||
verifyCompatibility(cfg, seen, fieldKeys.Type.Elem(), schemaType.KeyType())
|
||||
|
||||
fieldValues := goType.Field(1)
|
||||
if fieldValues.Type.Kind() != reflect.Map {
|
||||
doPanic("kind mismatch; need struct{Keys []K; Values map[K]V}")
|
||||
}
|
||||
keyType := fieldValues.Type.Key()
|
||||
verifyCompatibility(cfg, seen, keyType, schemaType.KeyType())
|
||||
|
||||
elemType := fieldValues.Type.Elem()
|
||||
if schemaType.ValueIsNullable() {
|
||||
if ptr, nilable := ptrOrNilable(elemType.Kind()); !nilable {
|
||||
doPanic("nullable types must be nilable")
|
||||
} else if ptr {
|
||||
elemType = elemType.Elem()
|
||||
}
|
||||
}
|
||||
verifyCompatibility(cfg, seen, elemType, schemaType.ValueType())
|
||||
case *schema.TypeStruct:
|
||||
if goType.Kind() != reflect.Struct {
|
||||
doPanic("kind mismatch; need struct")
|
||||
}
|
||||
|
||||
schemaFields := schemaType.Fields()
|
||||
if goType.NumField() != len(schemaFields) {
|
||||
doPanic("%d vs %d fields", goType.NumField(), len(schemaFields))
|
||||
}
|
||||
for i, schemaField := range schemaFields {
|
||||
schemaType := schemaField.Type()
|
||||
goType := goType.Field(i).Type
|
||||
switch {
|
||||
case schemaField.IsOptional() && schemaField.IsNullable():
|
||||
// TODO: https://github.com/ipld/go-ipld-prime/issues/340 will
|
||||
// help here, to avoid the double pointer. We can't use nilable
|
||||
// but non-pointer types because that's just one "nil" state.
|
||||
// TODO: deal with custom converters in this case
|
||||
if goType.Kind() != reflect.Ptr {
|
||||
doPanic("optional and nullable fields must use double pointers (**)")
|
||||
}
|
||||
goType = goType.Elem()
|
||||
if goType.Kind() != reflect.Ptr {
|
||||
doPanic("optional and nullable fields must use double pointers (**)")
|
||||
}
|
||||
goType = goType.Elem()
|
||||
case schemaField.IsOptional():
|
||||
if ptr, nilable := ptrOrNilable(goType.Kind()); !nilable {
|
||||
doPanic("optional fields must be nilable")
|
||||
} else if ptr {
|
||||
goType = goType.Elem()
|
||||
}
|
||||
case schemaField.IsNullable():
|
||||
if ptr, nilable := ptrOrNilable(goType.Kind()); !nilable {
|
||||
if customConverter := cfg.converterForType(goType); customConverter == nil {
|
||||
doPanic("nullable fields must be nilable")
|
||||
}
|
||||
} else if ptr {
|
||||
goType = goType.Elem()
|
||||
}
|
||||
}
|
||||
verifyCompatibility(cfg, seen, goType, schemaType)
|
||||
}
|
||||
case *schema.TypeUnion:
|
||||
if goType.Kind() != reflect.Struct {
|
||||
doPanic("kind mismatch; need struct for an union")
|
||||
}
|
||||
|
||||
schemaMembers := schemaType.Members()
|
||||
if goType.NumField() != len(schemaMembers) {
|
||||
doPanic("%d vs %d members", goType.NumField(), len(schemaMembers))
|
||||
}
|
||||
|
||||
for i, schemaType := range schemaMembers {
|
||||
goType := goType.Field(i).Type
|
||||
if ptr, nilable := ptrOrNilable(goType.Kind()); !nilable {
|
||||
doPanic("union members must be nilable")
|
||||
} else if ptr {
|
||||
goType = goType.Elem()
|
||||
}
|
||||
verifyCompatibility(cfg, seen, goType, schemaType)
|
||||
}
|
||||
case *schema.TypeLink:
|
||||
if customConverter := cfg.converterForType(goType); customConverter != nil {
|
||||
if customConverter.kind != schema.TypeKind_Link {
|
||||
doPanic("kind mismatch; custom converter for type is not for Link")
|
||||
}
|
||||
} else if goType != goTypeLink && goType != goTypeCidLink && goType != goTypeCid {
|
||||
doPanic("links in Go must be datamodel.Link, cidlink.Link, or cid.Cid")
|
||||
}
|
||||
case *schema.TypeAny:
|
||||
if customConverter := cfg.converterForType(goType); customConverter != nil {
|
||||
if customConverter.kind != schema.TypeKind_Any {
|
||||
doPanic("kind mismatch; custom converter for type is not for Any")
|
||||
}
|
||||
} else if goType != goTypeNode {
|
||||
doPanic("Any in Go must be datamodel.Node")
|
||||
}
|
||||
default:
|
||||
panic(fmt.Sprintf("%T", schemaType))
|
||||
}
|
||||
}
|
||||
|
||||
func ptrOrNilable(kind reflect.Kind) (ptr, nilable bool) {
|
||||
switch kind {
|
||||
case reflect.Ptr:
|
||||
return true, true
|
||||
case reflect.Interface, reflect.Map, reflect.Slice:
|
||||
return false, true
|
||||
default:
|
||||
return false, false
|
||||
}
|
||||
}
|
||||
|
||||
// If we recurse past a large number of levels, we're mostly stuck in a loop.
|
||||
// Prevent burning CPU or causing OOM crashes.
|
||||
// If a user really wrote an IPLD schema or Go type with such deep nesting,
|
||||
// it's likely they are trying to abuse the system as well.
|
||||
const maxRecursionLevel = 1 << 10
|
||||
|
||||
type inferredStatus int
|
||||
|
||||
const (
|
||||
_ inferredStatus = iota
|
||||
inferringInProcess
|
||||
inferringDone
|
||||
)
|
||||
|
||||
// inferGoType can build a Go type given a schema
|
||||
func inferGoType(typ schema.Type, status map[schema.TypeName]inferredStatus, level int) reflect.Type {
|
||||
if level > maxRecursionLevel {
|
||||
panic(fmt.Sprintf("inferGoType: refusing to recurse past %d levels", maxRecursionLevel))
|
||||
}
|
||||
name := typ.Name()
|
||||
if status[name] == inferringInProcess {
|
||||
panic("bindnode: inferring Go types from cyclic schemas is not supported since Go reflection does not support creating named types")
|
||||
}
|
||||
status[name] = inferringInProcess
|
||||
defer func() { status[name] = inferringDone }()
|
||||
switch typ := typ.(type) {
|
||||
case *schema.TypeBool:
|
||||
return goTypeBool
|
||||
case *schema.TypeInt:
|
||||
return goTypeInt
|
||||
case *schema.TypeFloat:
|
||||
return goTypeFloat
|
||||
case *schema.TypeString:
|
||||
return goTypeString
|
||||
case *schema.TypeBytes:
|
||||
return goTypeBytes
|
||||
case *schema.TypeStruct:
|
||||
fields := typ.Fields()
|
||||
fieldsGo := make([]reflect.StructField, len(fields))
|
||||
for i, field := range fields {
|
||||
ftypGo := inferGoType(field.Type(), status, level+1)
|
||||
if field.IsNullable() {
|
||||
ftypGo = reflect.PtrTo(ftypGo)
|
||||
}
|
||||
if field.IsOptional() {
|
||||
ftypGo = reflect.PtrTo(ftypGo)
|
||||
}
|
||||
fieldsGo[i] = reflect.StructField{
|
||||
Name: fieldNameFromSchema(field.Name()),
|
||||
Type: ftypGo,
|
||||
}
|
||||
}
|
||||
return reflect.StructOf(fieldsGo)
|
||||
case *schema.TypeMap:
|
||||
ktyp := inferGoType(typ.KeyType(), status, level+1)
|
||||
vtyp := inferGoType(typ.ValueType(), status, level+1)
|
||||
if typ.ValueIsNullable() {
|
||||
vtyp = reflect.PtrTo(vtyp)
|
||||
}
|
||||
// We need an extra field to keep the map ordered,
|
||||
// since IPLD maps must have stable iteration order.
|
||||
// We could sort when iterating, but that's expensive.
|
||||
// Keeping the insertion order is easy and intuitive.
|
||||
//
|
||||
// struct {
|
||||
// Keys []K
|
||||
// Values map[K]V
|
||||
// }
|
||||
fieldsGo := []reflect.StructField{
|
||||
{
|
||||
Name: "Keys",
|
||||
Type: reflect.SliceOf(ktyp),
|
||||
},
|
||||
{
|
||||
Name: "Values",
|
||||
Type: reflect.MapOf(ktyp, vtyp),
|
||||
},
|
||||
}
|
||||
return reflect.StructOf(fieldsGo)
|
||||
case *schema.TypeList:
|
||||
etyp := inferGoType(typ.ValueType(), status, level+1)
|
||||
if typ.ValueIsNullable() {
|
||||
etyp = reflect.PtrTo(etyp)
|
||||
}
|
||||
return reflect.SliceOf(etyp)
|
||||
case *schema.TypeUnion:
|
||||
// type goUnion struct {
|
||||
// Type1 *Type1
|
||||
// Type2 *Type2
|
||||
// ...
|
||||
// }
|
||||
members := typ.Members()
|
||||
fieldsGo := make([]reflect.StructField, len(members))
|
||||
for i, ftyp := range members {
|
||||
ftypGo := inferGoType(ftyp, status, level+1)
|
||||
fieldsGo[i] = reflect.StructField{
|
||||
Name: fieldNameFromSchema(ftyp.Name()),
|
||||
Type: reflect.PtrTo(ftypGo),
|
||||
}
|
||||
}
|
||||
return reflect.StructOf(fieldsGo)
|
||||
case *schema.TypeLink:
|
||||
return goTypeLink
|
||||
case *schema.TypeEnum:
|
||||
// TODO: generate int for int reprs by default?
|
||||
return goTypeString
|
||||
case *schema.TypeAny:
|
||||
return goTypeNode
|
||||
case nil:
|
||||
panic("bindnode: unexpected nil schema.Type")
|
||||
}
|
||||
panic(fmt.Sprintf("%T", typ))
|
||||
}
|
||||
|
||||
// from IPLD Schema field names like "foo" to Go field names like "Foo".
|
||||
func fieldNameFromSchema(name string) string {
|
||||
fieldName := strings.Title(name) //lint:ignore SA1019 cases.Title doesn't work for this
|
||||
if !token.IsIdentifier(fieldName) {
|
||||
panic(fmt.Sprintf("bindnode: inferred field name %q is not a valid Go identifier", fieldName))
|
||||
}
|
||||
return fieldName
|
||||
}
|
||||
|
||||
var defaultTypeSystem schema.TypeSystem
|
||||
|
||||
func init() {
|
||||
defaultTypeSystem.Init()
|
||||
|
||||
defaultTypeSystem.Accumulate(schemaTypeBool)
|
||||
defaultTypeSystem.Accumulate(schemaTypeInt)
|
||||
defaultTypeSystem.Accumulate(schemaTypeFloat)
|
||||
defaultTypeSystem.Accumulate(schemaTypeString)
|
||||
defaultTypeSystem.Accumulate(schemaTypeBytes)
|
||||
defaultTypeSystem.Accumulate(schemaTypeLink)
|
||||
defaultTypeSystem.Accumulate(schemaTypeAny)
|
||||
}
|
||||
|
||||
// TODO: support IPLD maps and unions in inferSchema
|
||||
|
||||
// TODO: support bringing your own TypeSystem?
|
||||
|
||||
// TODO: we should probably avoid re-spawning the same types if the TypeSystem
|
||||
// has them, and test that that works as expected
|
||||
|
||||
// inferSchema can build a schema from a Go type
|
||||
func inferSchema(typ reflect.Type, level int) schema.Type {
|
||||
if level > maxRecursionLevel {
|
||||
panic(fmt.Sprintf("inferSchema: refusing to recurse past %d levels", maxRecursionLevel))
|
||||
}
|
||||
switch typ.Kind() {
|
||||
case reflect.Bool:
|
||||
return schemaTypeBool
|
||||
case reflect.Int64:
|
||||
return schemaTypeInt
|
||||
case reflect.Float64:
|
||||
return schemaTypeFloat
|
||||
case reflect.String:
|
||||
return schemaTypeString
|
||||
case reflect.Struct:
|
||||
// these types must match exactly since we need symmetry of being able to
|
||||
// get the values an also assign values to them
|
||||
if typ == goTypeCid || typ == goTypeCidLink {
|
||||
return schemaTypeLink
|
||||
}
|
||||
|
||||
fieldsSchema := make([]schema.StructField, typ.NumField())
|
||||
for i := range fieldsSchema {
|
||||
field := typ.Field(i)
|
||||
ftyp := field.Type
|
||||
ftypSchema := inferSchema(ftyp, level+1)
|
||||
fieldsSchema[i] = schema.SpawnStructField(
|
||||
field.Name, // TODO: allow configuring the name with tags
|
||||
ftypSchema.Name(),
|
||||
|
||||
// TODO: support nullable/optional with tags
|
||||
false,
|
||||
false,
|
||||
)
|
||||
}
|
||||
name := typ.Name()
|
||||
if name == "" {
|
||||
panic("TODO: anonymous composite types")
|
||||
}
|
||||
typSchema := schema.SpawnStruct(name, fieldsSchema, nil)
|
||||
defaultTypeSystem.Accumulate(typSchema)
|
||||
return typSchema
|
||||
case reflect.Slice:
|
||||
if typ.Elem().Kind() == reflect.Uint8 {
|
||||
// Special case for []byte.
|
||||
return schemaTypeBytes
|
||||
}
|
||||
|
||||
nullable := false
|
||||
if typ.Elem().Kind() == reflect.Ptr {
|
||||
nullable = true
|
||||
}
|
||||
etypSchema := inferSchema(typ.Elem(), level+1)
|
||||
name := typ.Name()
|
||||
if name == "" {
|
||||
name = "List_" + etypSchema.Name()
|
||||
}
|
||||
typSchema := schema.SpawnList(name, etypSchema.Name(), nullable)
|
||||
defaultTypeSystem.Accumulate(typSchema)
|
||||
return typSchema
|
||||
case reflect.Interface:
|
||||
// these types must match exactly since we need symmetry of being able to
|
||||
// get the values an also assign values to them
|
||||
if typ == goTypeLink {
|
||||
return schemaTypeLink
|
||||
}
|
||||
if typ == goTypeNode {
|
||||
return schemaTypeAny
|
||||
}
|
||||
panic("bindnode: unable to infer from interface")
|
||||
}
|
||||
panic(fmt.Sprintf("bindnode: unable to infer from type %s", typ.Kind().String()))
|
||||
}
|
||||
|
||||
// There are currently 27 reflect.Kind iota values,
|
||||
// so 32 should be plenty to ensure we don't panic in practice.
|
||||
|
||||
var kindInt = [32]bool{
|
||||
reflect.Int: true,
|
||||
reflect.Int8: true,
|
||||
reflect.Int16: true,
|
||||
reflect.Int32: true,
|
||||
reflect.Int64: true,
|
||||
}
|
||||
|
||||
var kindUint = [32]bool{
|
||||
reflect.Uint: true,
|
||||
reflect.Uint8: true,
|
||||
reflect.Uint16: true,
|
||||
reflect.Uint32: true,
|
||||
reflect.Uint64: true,
|
||||
}
|
||||
1754
vendor/github.com/ipld/go-ipld-prime/node/bindnode/node.go
generated
vendored
Normal file
1754
vendor/github.com/ipld/go-ipld-prime/node/bindnode/node.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1124
vendor/github.com/ipld/go-ipld-prime/node/bindnode/repr.go
generated
vendored
Normal file
1124
vendor/github.com/ipld/go-ipld-prime/node/bindnode/repr.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user