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:
anthonyrawlins
2025-09-06 07:56:26 +10:00
parent 543ab216f9
commit 9bdcbe0447
4730 changed files with 1480093 additions and 1916 deletions

View File

@@ -0,0 +1,36 @@
// This is a transitional package: please move your references to `node/basicnode`.
// The new package is identical: we've renamed the import path only.
//
// All content in this package is a thin wrapper around `node/basicnode`.
// Please update at your earliest convenience.
//
// This package will eventually be removed.
package basicnode
import (
"github.com/ipld/go-ipld-prime/datamodel"
"github.com/ipld/go-ipld-prime/linking"
"github.com/ipld/go-ipld-prime/node/basicnode"
)
var Prototype = basicnode.Prototype
func Chooser(_ datamodel.Link, _ linking.LinkContext) (datamodel.NodePrototype, error) {
return basicnode.Chooser(nil, linking.LinkContext{})
}
func NewBool(value bool) datamodel.Node { return basicnode.NewBool(value) }
func NewBytes(value []byte) datamodel.Node { return basicnode.NewBytes(value) }
func NewFloat(value float64) datamodel.Node { return basicnode.NewFloat(value) }
func NewInt(value int64) datamodel.Node { return basicnode.NewInt(value) }
func NewLink(value datamodel.Link) datamodel.Node { return basicnode.NewLink(value) }
func NewString(value string) datamodel.Node { return basicnode.NewString(value) }
type Prototype__Any = basicnode.Prototype__Any
type Prototype__Bool = basicnode.Prototype__Bool
type Prototype__Bytes = basicnode.Prototype__Bytes
type Prototype__Float = basicnode.Prototype__Float
type Prototype__Int = basicnode.Prototype__Int
type Prototype__Link = basicnode.Prototype__Link
type Prototype__List = basicnode.Prototype__List
type Prototype__Map = basicnode.Prototype__Map
type Prototype__String = basicnode.Prototype__String

View File

@@ -0,0 +1,146 @@
hackme
======
Design rationale are documented here.
This doc is not necessary reading for users of this package,
but if you're considering submitting patches -- or just trying to understand
why it was written this way, and check for reasoning that might be dated --
then it might be useful reading.
### scalars are just typedefs
This is noteworthy because in codegen, this is typically *not* the case:
in codegen, even scalar types are boxed in a struct, such that it prevents
casting values into those types.
This casting is not a concern for the node implementations in this package, because
- A) we don't have any kind of validation rules to make such casting worrying; and
- B) since our types are unexported, casting is still blocked by this anyway.
### about builders for scalars
The assembler types for scalars (string, int, etc) are pretty funny-looking.
You might wish to make them work without any state at all!
The reason this doesn't fly is that we have to keep the "wip" value in hand
just long enough to return it from the `NodeBuilder.Build` method -- the
`NodeAssembler` contract for `Assign*` methods doesn't permit just returning
their results immediately.
(Another possible reason is if we expected to use these assemblers on
slab-style allocations (say, `[]plainString`)...
however, this is inapplicable at present, because
A) we don't (except places that have special-case internal paths anyway); and
B) the types aren't exported, so users can't either.)
Does this mean that using `NodeBuilder` for scalars has a completely
unnecessary second allocation, which is laughably inefficient? Yes.
It's unfortunate the interfaces constrain us to this.
**But**: one typically doesn't actually use builders for scalars much;
they're just here for completeness.
So this is less of a problem in practice than it might at first seem.
More often, one will use the "any" builder (which is has a whole different set
of design constraints and tradeoffs);
or, if one is writing code and knows which scalar they need, the exported
direct constructor function for that kind
(e.g., `String("foo")` instead of `Prototype__String{}.NewBuilder().AssignString("foo")`)
will do the right thing and do it in one allocation (and it's less to type, too).
### maps and list keyAssembler and valueAssemblers have custom scalar handling
Related to the above heading.
Maps and lists in this package do their own internal handling of scalars,
using unexported features inside the package, because they can more efficient.
### when to invalidate the 'w' pointers
The 'w' pointer -- short for 'wip' node pointer -- has an interesting lifecycle.
In a NodeAssembler, the 'w' pointer should be intialized before the assembler is used.
This means either the matching NodeBuilder type does so; or,
if we're inside recursive structure, the parent assembler did so.
The 'w' pointer is used throughout the life of the assembler.
Setting the 'w' pointer to nil is one of two mechanisms used internally
to mark that assembly has become "finished" (the other mechanism is using
an internal state enum field).
Setting the 'w' pointer to nil has two advantages:
one is that it makes it *impossible* to continue to mutate the target node;
the other is that we need no *additional* memory to track this state change.
However, we can't use the strategy of nilling 'w' in all cases: in particular,
when in the NodeBuilder at the root of some construction,
we need to continue to hold onto the node between when it becomes "finished"
and when Build is called; otherwise we can't actually return the value!
Different stratgies are therefore used in different parts of this package.
Maps and lists use an internal state enum, because they already have one,
and so they might as well; there's no additional cost to this.
Since they can use this state to guard against additional mutations after "finish",
the map and list assemblers don't bother to nil their own 'w' at all.
During recursion to assemble values _inside_ maps and lists, it's interesting:
the child assembler wrapper type takes reponsibility for nilling out
the 'w' pointer in the child assembler's state, doing this at the same time as
it updates the parent's state machine to clear proceeding with the next entry.
In the case of scalars at the root of a build, we took a shortcut:
we actually don't fence against repeat mutations at all.
*You can actually use the assign method more than once*.
We can do this without breaking safety contracts because the scalars
all have a pass-by-value phase somewhere in their lifecycle
(calling `nb.AssignString("x")`, then `n := nb.Build()`, then `nb.AssignString("y")`
won't error if `nb` is a freestanding builder for strings... but it also
won't result in mutating `n` to contain `"y"`, so overall, it's safe).
We could normalize the case with scalars at the root of a tree so that they
error more aggressively... but currently we haven't bothered, since this would
require adding another piece of memory to the scalar builders; and meanwhile
we're not in trouble on compositional correctness.
Note that these remarks are for the `basicnode` package, but may also
apply to other implementations too (e.g., our codegen output follows similar
overall logic).
### NodePrototypes are available through a singleton
Every NodePrototype available from this package is exposed as a field
in a struct of which there's one public exported instance available,
called 'Prototype'.
This means you can use it like this:
```go
nbm := basicnode.Prototype.Map.NewBuilder()
nbs := basicnode.Prototype.String.NewBuilder()
nba := basicnode.Prototype.Any.NewBuilder()
// etc
```
(If you're interested in the performance of this: it's free!
Methods called at the end of the chain are inlinable.
Since all of the types of the structures on the way there are zero-member
structs, the compiler can effectively treat them as constants,
and thus freely elide any memory dereferences that would
otherwise be necessary to get methods on such a value.)
### NodePrototypes are (also) available as exported concrete types
The 'Prototype' singleton is one way to access the NodePrototype in this package;
their exported types are another equivalent way.
```go
basicnode.Prototype.Map = basicnode.Prototype.Map
```
It is recommended to use the singleton style;
they compile to identical assembly, and the singleton is syntactically prettier.
We may make these concrete types unexported in the future.
A decision on this is deferred until some time has passed and
we can accumulate reasonable certainty that there's no need for an exported type
(such as type assertions, etc).

View File

@@ -0,0 +1,197 @@
package basicnode
import (
"github.com/ipld/go-ipld-prime/datamodel"
"github.com/ipld/go-ipld-prime/linking"
)
var (
//_ datamodel.Node = &anyNode{}
_ datamodel.NodePrototype = Prototype__Any{}
_ datamodel.NodeBuilder = &anyBuilder{}
//_ datamodel.NodeAssembler = &anyAssembler{}
)
// Note that we don't use a "var _" declaration to assert that Chooser
// implements traversal.LinkTargetNodePrototypeChooser, to keep basicnode's
// dependencies fairly light.
// Chooser implements traversal.LinkTargetNodePrototypeChooser.
//
// It can be used directly when loading links into the "any" prototype,
// or with another chooser layer on top, such as:
//
// prototypeChooser := dagpb.AddSupportToChooser(basicnode.Chooser)
func Chooser(_ datamodel.Link, _ linking.LinkContext) (datamodel.NodePrototype, error) {
return Prototype.Any, nil
}
// -- Node interface methods -->
// Unimplemented at present -- see "REVIEW" comment on anyNode.
// -- NodePrototype -->
type Prototype__Any struct{}
func (Prototype__Any) NewBuilder() datamodel.NodeBuilder {
return &anyBuilder{}
}
// -- NodeBuilder -->
// anyBuilder is a builder for any kind of node.
//
// anyBuilder is a little unusual in its internal workings:
// unlike most builders, it doesn't embed the corresponding assembler,
// nor will it end up using anyNode,
// but instead embeds a builder for each of the kinds it might contain.
// This is because we want a more granular return at the end:
// if we used anyNode, and returned a pointer to just the relevant part of it,
// we'd have all the extra bytes of anyNode still reachable in GC terms
// for as long as that handle to the interior of it remains live.
type anyBuilder struct {
// kind is set on first interaction, and used to select which builder to delegate 'Build' to!
// As soon as it's been set to a value other than zero (being "Invalid"), all other Assign/Begin calls will fail since something is already in progress.
// May also be set to the magic value '99', which means "i dunno, I'm just carrying another node of unknown prototype".
kind datamodel.Kind
// Only one of the following ends up being used...
// but we don't know in advance which one, so all are embeded here.
// This uses excessive space, but amortizes allocations, and all will be
// freed as soon as the builder is done.
// Builders are only used for recursives;
// scalars are simple enough we just do them directly.
// 'scalarNode' may also hold another Node of unknown prototype (possibly not even from this package),
// in which case this is indicated by 'kind==99'.
mapBuilder plainMap__Builder
listBuilder plainList__Builder
scalarNode datamodel.Node
}
func (nb *anyBuilder) Reset() {
*nb = anyBuilder{}
}
func (nb *anyBuilder) BeginMap(sizeHint int64) (datamodel.MapAssembler, error) {
if nb.kind != datamodel.Kind_Invalid {
panic("misuse")
}
nb.kind = datamodel.Kind_Map
nb.mapBuilder.w = &plainMap{}
return nb.mapBuilder.BeginMap(sizeHint)
}
func (nb *anyBuilder) BeginList(sizeHint int64) (datamodel.ListAssembler, error) {
if nb.kind != datamodel.Kind_Invalid {
panic("misuse")
}
nb.kind = datamodel.Kind_List
nb.listBuilder.w = &plainList{}
return nb.listBuilder.BeginList(sizeHint)
}
func (nb *anyBuilder) AssignNull() error {
if nb.kind != datamodel.Kind_Invalid {
panic("misuse")
}
nb.kind = datamodel.Kind_Null
return nil
}
func (nb *anyBuilder) AssignBool(v bool) error {
if nb.kind != datamodel.Kind_Invalid {
panic("misuse")
}
nb.kind = datamodel.Kind_Bool
nb.scalarNode = NewBool(v)
return nil
}
func (nb *anyBuilder) AssignInt(v int64) error {
if nb.kind != datamodel.Kind_Invalid {
panic("misuse")
}
nb.kind = datamodel.Kind_Int
nb.scalarNode = NewInt(v)
return nil
}
func (nb *anyBuilder) AssignFloat(v float64) error {
if nb.kind != datamodel.Kind_Invalid {
panic("misuse")
}
nb.kind = datamodel.Kind_Float
nb.scalarNode = NewFloat(v)
return nil
}
func (nb *anyBuilder) AssignString(v string) error {
if nb.kind != datamodel.Kind_Invalid {
panic("misuse")
}
nb.kind = datamodel.Kind_String
nb.scalarNode = NewString(v)
return nil
}
func (nb *anyBuilder) AssignBytes(v []byte) error {
if nb.kind != datamodel.Kind_Invalid {
panic("misuse")
}
nb.kind = datamodel.Kind_Bytes
nb.scalarNode = NewBytes(v)
return nil
}
func (nb *anyBuilder) AssignLink(v datamodel.Link) error {
if nb.kind != datamodel.Kind_Invalid {
panic("misuse")
}
nb.kind = datamodel.Kind_Link
nb.scalarNode = NewLink(v)
return nil
}
func (nb *anyBuilder) AssignNode(v datamodel.Node) error {
if nb.kind != datamodel.Kind_Invalid {
panic("misuse")
}
nb.kind = 99
nb.scalarNode = v
return nil
}
func (anyBuilder) Prototype() datamodel.NodePrototype {
return Prototype.Any
}
func (nb *anyBuilder) Build() datamodel.Node {
switch nb.kind {
case datamodel.Kind_Invalid:
panic("misuse")
case datamodel.Kind_Map:
return nb.mapBuilder.Build()
case datamodel.Kind_List:
return nb.listBuilder.Build()
case datamodel.Kind_Null:
return datamodel.Null
case datamodel.Kind_Bool:
return nb.scalarNode
case datamodel.Kind_Int:
return nb.scalarNode
case datamodel.Kind_Float:
return nb.scalarNode
case datamodel.Kind_String:
return nb.scalarNode
case datamodel.Kind_Bytes:
return nb.scalarNode
case datamodel.Kind_Link:
return nb.scalarNode
case 99:
return nb.scalarNode
default:
panic("unreachable")
}
}
// -- NodeAssembler -->
// ... oddly enough, we seem to be able to put off implementing this
// until we also implement something that goes full-hog on amortization
// and actually has a slab of `anyNode`. Which so far, nothing does.
// See "REVIEW" comment on anyNode.
// type anyAssembler struct {
// w *anyNode
// }

View File

@@ -0,0 +1,144 @@
package basicnode
import (
"github.com/ipld/go-ipld-prime/datamodel"
"github.com/ipld/go-ipld-prime/node/mixins"
)
var (
_ datamodel.Node = plainBool(false)
_ datamodel.NodePrototype = Prototype__Bool{}
_ datamodel.NodeBuilder = &plainBool__Builder{}
_ datamodel.NodeAssembler = &plainBool__Assembler{}
)
func NewBool(value bool) datamodel.Node {
v := plainBool(value)
return &v
}
// plainBool is a simple boxed boolean that complies with datamodel.Node.
type plainBool bool
// -- Node interface methods -->
func (plainBool) Kind() datamodel.Kind {
return datamodel.Kind_Bool
}
func (plainBool) LookupByString(string) (datamodel.Node, error) {
return mixins.Bool{TypeName: "bool"}.LookupByString("")
}
func (plainBool) LookupByNode(key datamodel.Node) (datamodel.Node, error) {
return mixins.Bool{TypeName: "bool"}.LookupByNode(nil)
}
func (plainBool) LookupByIndex(idx int64) (datamodel.Node, error) {
return mixins.Bool{TypeName: "bool"}.LookupByIndex(0)
}
func (plainBool) LookupBySegment(seg datamodel.PathSegment) (datamodel.Node, error) {
return mixins.Bool{TypeName: "bool"}.LookupBySegment(seg)
}
func (plainBool) MapIterator() datamodel.MapIterator {
return nil
}
func (plainBool) ListIterator() datamodel.ListIterator {
return nil
}
func (plainBool) Length() int64 {
return -1
}
func (plainBool) IsAbsent() bool {
return false
}
func (plainBool) IsNull() bool {
return false
}
func (n plainBool) AsBool() (bool, error) {
return bool(n), nil
}
func (plainBool) AsInt() (int64, error) {
return mixins.Bool{TypeName: "bool"}.AsInt()
}
func (plainBool) AsFloat() (float64, error) {
return mixins.Bool{TypeName: "bool"}.AsFloat()
}
func (plainBool) AsString() (string, error) {
return mixins.Bool{TypeName: "bool"}.AsString()
}
func (plainBool) AsBytes() ([]byte, error) {
return mixins.Bool{TypeName: "bool"}.AsBytes()
}
func (plainBool) AsLink() (datamodel.Link, error) {
return mixins.Bool{TypeName: "bool"}.AsLink()
}
func (plainBool) Prototype() datamodel.NodePrototype {
return Prototype__Bool{}
}
// -- NodePrototype -->
type Prototype__Bool struct{}
func (Prototype__Bool) NewBuilder() datamodel.NodeBuilder {
var w plainBool
return &plainBool__Builder{plainBool__Assembler{w: &w}}
}
// -- NodeBuilder -->
type plainBool__Builder struct {
plainBool__Assembler
}
func (nb *plainBool__Builder) Build() datamodel.Node {
return nb.w
}
func (nb *plainBool__Builder) Reset() {
var w plainBool
*nb = plainBool__Builder{plainBool__Assembler{w: &w}}
}
// -- NodeAssembler -->
type plainBool__Assembler struct {
w *plainBool
}
func (plainBool__Assembler) BeginMap(sizeHint int64) (datamodel.MapAssembler, error) {
return mixins.BoolAssembler{TypeName: "bool"}.BeginMap(0)
}
func (plainBool__Assembler) BeginList(sizeHint int64) (datamodel.ListAssembler, error) {
return mixins.BoolAssembler{TypeName: "bool"}.BeginList(0)
}
func (plainBool__Assembler) AssignNull() error {
return mixins.BoolAssembler{TypeName: "bool"}.AssignNull()
}
func (na *plainBool__Assembler) AssignBool(v bool) error {
*na.w = plainBool(v)
return nil
}
func (plainBool__Assembler) AssignInt(int64) error {
return mixins.BoolAssembler{TypeName: "bool"}.AssignInt(0)
}
func (plainBool__Assembler) AssignFloat(float64) error {
return mixins.BoolAssembler{TypeName: "bool"}.AssignFloat(0)
}
func (plainBool__Assembler) AssignString(string) error {
return mixins.BoolAssembler{TypeName: "bool"}.AssignString("")
}
func (plainBool__Assembler) AssignBytes([]byte) error {
return mixins.BoolAssembler{TypeName: "bool"}.AssignBytes(nil)
}
func (plainBool__Assembler) AssignLink(datamodel.Link) error {
return mixins.BoolAssembler{TypeName: "bool"}.AssignLink(nil)
}
func (na *plainBool__Assembler) AssignNode(v datamodel.Node) error {
if v2, err := v.AsBool(); err != nil {
return err
} else {
*na.w = plainBool(v2)
return nil
}
}
func (plainBool__Assembler) Prototype() datamodel.NodePrototype {
return Prototype__Bool{}
}

View File

@@ -0,0 +1,157 @@
package basicnode
import (
"bytes"
"io"
"github.com/ipld/go-ipld-prime/datamodel"
"github.com/ipld/go-ipld-prime/node/mixins"
)
var (
_ datamodel.Node = plainBytes(nil)
_ datamodel.NodePrototype = Prototype__Bytes{}
_ datamodel.NodeBuilder = &plainBytes__Builder{}
_ datamodel.NodeAssembler = &plainBytes__Assembler{}
)
func NewBytes(value []byte) datamodel.Node {
v := plainBytes(value)
return &v
}
// plainBytes is a simple boxed byte slice that complies with datamodel.Node.
type plainBytes []byte
// -- Node interface methods -->
func (plainBytes) Kind() datamodel.Kind {
return datamodel.Kind_Bytes
}
func (plainBytes) LookupByString(string) (datamodel.Node, error) {
return mixins.Bytes{TypeName: "bytes"}.LookupByString("")
}
func (plainBytes) LookupByNode(key datamodel.Node) (datamodel.Node, error) {
return mixins.Bytes{TypeName: "bytes"}.LookupByNode(nil)
}
func (plainBytes) LookupByIndex(idx int64) (datamodel.Node, error) {
return mixins.Bytes{TypeName: "bytes"}.LookupByIndex(0)
}
func (plainBytes) LookupBySegment(seg datamodel.PathSegment) (datamodel.Node, error) {
return mixins.Bytes{TypeName: "bytes"}.LookupBySegment(seg)
}
func (plainBytes) MapIterator() datamodel.MapIterator {
return nil
}
func (plainBytes) ListIterator() datamodel.ListIterator {
return nil
}
func (plainBytes) Length() int64 {
return -1
}
func (plainBytes) IsAbsent() bool {
return false
}
func (plainBytes) IsNull() bool {
return false
}
func (plainBytes) AsBool() (bool, error) {
return mixins.Bytes{TypeName: "bytes"}.AsBool()
}
func (plainBytes) AsInt() (int64, error) {
return mixins.Bytes{TypeName: "bytes"}.AsInt()
}
func (plainBytes) AsFloat() (float64, error) {
return mixins.Bytes{TypeName: "bytes"}.AsFloat()
}
func (plainBytes) AsString() (string, error) {
return mixins.Bytes{TypeName: "bytes"}.AsString()
}
func (n plainBytes) AsBytes() ([]byte, error) {
return []byte(n), nil
}
func (plainBytes) AsLink() (datamodel.Link, error) {
return mixins.Bytes{TypeName: "bytes"}.AsLink()
}
func (plainBytes) Prototype() datamodel.NodePrototype {
return Prototype__Bytes{}
}
func (n plainBytes) AsLargeBytes() (io.ReadSeeker, error) {
return bytes.NewReader(n), nil
}
// -- NodePrototype -->
type Prototype__Bytes struct{}
func (Prototype__Bytes) NewBuilder() datamodel.NodeBuilder {
var w plainBytes
return &plainBytes__Builder{plainBytes__Assembler{w: &w}}
}
// -- NodeBuilder -->
type plainBytes__Builder struct {
plainBytes__Assembler
}
func (nb *plainBytes__Builder) Build() datamodel.Node {
return nb.w
}
func (nb *plainBytes__Builder) Reset() {
var w plainBytes
*nb = plainBytes__Builder{plainBytes__Assembler{w: &w}}
}
// -- NodeAssembler -->
type plainBytes__Assembler struct {
w datamodel.Node
}
func (plainBytes__Assembler) BeginMap(sizeHint int64) (datamodel.MapAssembler, error) {
return mixins.BytesAssembler{TypeName: "bytes"}.BeginMap(0)
}
func (plainBytes__Assembler) BeginList(sizeHint int64) (datamodel.ListAssembler, error) {
return mixins.BytesAssembler{TypeName: "bytes"}.BeginList(0)
}
func (plainBytes__Assembler) AssignNull() error {
return mixins.BytesAssembler{TypeName: "bytes"}.AssignNull()
}
func (plainBytes__Assembler) AssignBool(bool) error {
return mixins.BytesAssembler{TypeName: "bytes"}.AssignBool(false)
}
func (plainBytes__Assembler) AssignInt(int64) error {
return mixins.BytesAssembler{TypeName: "bytes"}.AssignInt(0)
}
func (plainBytes__Assembler) AssignFloat(float64) error {
return mixins.BytesAssembler{TypeName: "bytes"}.AssignFloat(0)
}
func (plainBytes__Assembler) AssignString(string) error {
return mixins.BytesAssembler{TypeName: "bytes"}.AssignString("")
}
func (na *plainBytes__Assembler) AssignBytes(v []byte) error {
na.w = datamodel.Node(plainBytes(v))
return nil
}
func (plainBytes__Assembler) AssignLink(datamodel.Link) error {
return mixins.BytesAssembler{TypeName: "bytes"}.AssignLink(nil)
}
func (na *plainBytes__Assembler) AssignNode(v datamodel.Node) error {
if lb, ok := v.(datamodel.LargeBytesNode); ok {
lbn, err := lb.AsLargeBytes()
if err == nil {
na.w = streamBytes{lbn}
return nil
}
}
if v2, err := v.AsBytes(); err != nil {
return err
} else {
na.w = plainBytes(v2)
return nil
}
}
func (plainBytes__Assembler) Prototype() datamodel.NodePrototype {
return Prototype__Bytes{}
}

View File

@@ -0,0 +1,81 @@
package basicnode
import (
"io"
"github.com/ipld/go-ipld-prime/datamodel"
"github.com/ipld/go-ipld-prime/node/mixins"
)
var (
_ datamodel.Node = streamBytes{nil}
_ datamodel.NodePrototype = Prototype__Bytes{}
_ datamodel.NodeBuilder = &plainBytes__Builder{}
_ datamodel.NodeAssembler = &plainBytes__Assembler{}
)
func NewBytesFromReader(rs io.ReadSeeker) datamodel.Node {
return streamBytes{rs}
}
// streamBytes is a boxed reader that complies with datamodel.Node.
type streamBytes struct {
io.ReadSeeker
}
// -- Node interface methods -->
func (streamBytes) Kind() datamodel.Kind {
return datamodel.Kind_Bytes
}
func (streamBytes) LookupByString(string) (datamodel.Node, error) {
return mixins.Bytes{TypeName: "bytes"}.LookupByString("")
}
func (streamBytes) LookupByNode(key datamodel.Node) (datamodel.Node, error) {
return mixins.Bytes{TypeName: "bytes"}.LookupByNode(nil)
}
func (streamBytes) LookupByIndex(idx int64) (datamodel.Node, error) {
return mixins.Bytes{TypeName: "bytes"}.LookupByIndex(0)
}
func (streamBytes) LookupBySegment(seg datamodel.PathSegment) (datamodel.Node, error) {
return mixins.Bytes{TypeName: "bytes"}.LookupBySegment(seg)
}
func (streamBytes) MapIterator() datamodel.MapIterator {
return nil
}
func (streamBytes) ListIterator() datamodel.ListIterator {
return nil
}
func (streamBytes) Length() int64 {
return -1
}
func (streamBytes) IsAbsent() bool {
return false
}
func (streamBytes) IsNull() bool {
return false
}
func (streamBytes) AsBool() (bool, error) {
return mixins.Bytes{TypeName: "bytes"}.AsBool()
}
func (streamBytes) AsInt() (int64, error) {
return mixins.Bytes{TypeName: "bytes"}.AsInt()
}
func (streamBytes) AsFloat() (float64, error) {
return mixins.Bytes{TypeName: "bytes"}.AsFloat()
}
func (streamBytes) AsString() (string, error) {
return mixins.Bytes{TypeName: "bytes"}.AsString()
}
func (n streamBytes) AsBytes() ([]byte, error) {
return io.ReadAll(n)
}
func (streamBytes) AsLink() (datamodel.Link, error) {
return mixins.Bytes{TypeName: "bytes"}.AsLink()
}
func (streamBytes) Prototype() datamodel.NodePrototype {
return Prototype__Bytes{}
}
func (n streamBytes) AsLargeBytes() (io.ReadSeeker, error) {
return n.ReadSeeker, nil
}

View File

@@ -0,0 +1,144 @@
package basicnode
import (
"github.com/ipld/go-ipld-prime/datamodel"
"github.com/ipld/go-ipld-prime/node/mixins"
)
var (
_ datamodel.Node = plainFloat(0)
_ datamodel.NodePrototype = Prototype__Float{}
_ datamodel.NodeBuilder = &plainFloat__Builder{}
_ datamodel.NodeAssembler = &plainFloat__Assembler{}
)
func NewFloat(value float64) datamodel.Node {
v := plainFloat(value)
return &v
}
// plainFloat is a simple boxed float that complies with datamodel.Node.
type plainFloat float64
// -- Node interface methods -->
func (plainFloat) Kind() datamodel.Kind {
return datamodel.Kind_Float
}
func (plainFloat) LookupByString(string) (datamodel.Node, error) {
return mixins.Float{TypeName: "float"}.LookupByString("")
}
func (plainFloat) LookupByNode(key datamodel.Node) (datamodel.Node, error) {
return mixins.Float{TypeName: "float"}.LookupByNode(nil)
}
func (plainFloat) LookupByIndex(idx int64) (datamodel.Node, error) {
return mixins.Float{TypeName: "float"}.LookupByIndex(0)
}
func (plainFloat) LookupBySegment(seg datamodel.PathSegment) (datamodel.Node, error) {
return mixins.Float{TypeName: "float"}.LookupBySegment(seg)
}
func (plainFloat) MapIterator() datamodel.MapIterator {
return nil
}
func (plainFloat) ListIterator() datamodel.ListIterator {
return nil
}
func (plainFloat) Length() int64 {
return -1
}
func (plainFloat) IsAbsent() bool {
return false
}
func (plainFloat) IsNull() bool {
return false
}
func (plainFloat) AsBool() (bool, error) {
return mixins.Float{TypeName: "float"}.AsBool()
}
func (plainFloat) AsInt() (int64, error) {
return mixins.Float{TypeName: "float"}.AsInt()
}
func (n plainFloat) AsFloat() (float64, error) {
return float64(n), nil
}
func (plainFloat) AsString() (string, error) {
return mixins.Float{TypeName: "float"}.AsString()
}
func (plainFloat) AsBytes() ([]byte, error) {
return mixins.Float{TypeName: "float"}.AsBytes()
}
func (plainFloat) AsLink() (datamodel.Link, error) {
return mixins.Float{TypeName: "float"}.AsLink()
}
func (plainFloat) Prototype() datamodel.NodePrototype {
return Prototype__Float{}
}
// -- NodePrototype -->
type Prototype__Float struct{}
func (Prototype__Float) NewBuilder() datamodel.NodeBuilder {
var w plainFloat
return &plainFloat__Builder{plainFloat__Assembler{w: &w}}
}
// -- NodeBuilder -->
type plainFloat__Builder struct {
plainFloat__Assembler
}
func (nb *plainFloat__Builder) Build() datamodel.Node {
return nb.w
}
func (nb *plainFloat__Builder) Reset() {
var w plainFloat
*nb = plainFloat__Builder{plainFloat__Assembler{w: &w}}
}
// -- NodeAssembler -->
type plainFloat__Assembler struct {
w *plainFloat
}
func (plainFloat__Assembler) BeginMap(sizeHint int64) (datamodel.MapAssembler, error) {
return mixins.FloatAssembler{TypeName: "float"}.BeginMap(0)
}
func (plainFloat__Assembler) BeginList(sizeHint int64) (datamodel.ListAssembler, error) {
return mixins.FloatAssembler{TypeName: "float"}.BeginList(0)
}
func (plainFloat__Assembler) AssignNull() error {
return mixins.FloatAssembler{TypeName: "float"}.AssignNull()
}
func (plainFloat__Assembler) AssignBool(bool) error {
return mixins.FloatAssembler{TypeName: "float"}.AssignBool(false)
}
func (plainFloat__Assembler) AssignInt(int64) error {
return mixins.FloatAssembler{TypeName: "float"}.AssignInt(0)
}
func (na *plainFloat__Assembler) AssignFloat(v float64) error {
*na.w = plainFloat(v)
return nil
}
func (plainFloat__Assembler) AssignString(string) error {
return mixins.FloatAssembler{TypeName: "float"}.AssignString("")
}
func (plainFloat__Assembler) AssignBytes([]byte) error {
return mixins.FloatAssembler{TypeName: "float"}.AssignBytes(nil)
}
func (plainFloat__Assembler) AssignLink(datamodel.Link) error {
return mixins.FloatAssembler{TypeName: "float"}.AssignLink(nil)
}
func (na *plainFloat__Assembler) AssignNode(v datamodel.Node) error {
if v2, err := v.AsFloat(); err != nil {
return err
} else {
*na.w = plainFloat(v2)
return nil
}
}
func (plainFloat__Assembler) Prototype() datamodel.NodePrototype {
return Prototype__Float{}
}

View File

@@ -0,0 +1,226 @@
package basicnode
import (
"fmt"
"math"
"github.com/ipld/go-ipld-prime/datamodel"
"github.com/ipld/go-ipld-prime/node/mixins"
)
var (
_ datamodel.Node = plainInt(0)
_ datamodel.Node = plainUint(0)
_ datamodel.UintNode = plainUint(0)
_ datamodel.NodePrototype = Prototype__Int{}
_ datamodel.NodeBuilder = &plainInt__Builder{}
_ datamodel.NodeAssembler = &plainInt__Assembler{}
)
func NewInt(value int64) datamodel.Node {
return plainInt(value)
}
// NewUint creates a new uint64-backed Node which will behave as a plain Int
// node but also conforms to the datamodel.UintNode interface which can access
// the full uint64 range.
//
// EXPERIMENTAL: this API is experimental and may be changed or removed in a
// future release.
func NewUint(value uint64) datamodel.Node {
return plainUint(value)
}
// plainInt is a simple boxed int that complies with datamodel.Node.
type plainInt int64
// -- Node interface methods for plainInt -->
func (plainInt) Kind() datamodel.Kind {
return datamodel.Kind_Int
}
func (plainInt) LookupByString(string) (datamodel.Node, error) {
return mixins.Int{TypeName: "int"}.LookupByString("")
}
func (plainInt) LookupByNode(key datamodel.Node) (datamodel.Node, error) {
return mixins.Int{TypeName: "int"}.LookupByNode(nil)
}
func (plainInt) LookupByIndex(idx int64) (datamodel.Node, error) {
return mixins.Int{TypeName: "int"}.LookupByIndex(0)
}
func (plainInt) LookupBySegment(seg datamodel.PathSegment) (datamodel.Node, error) {
return mixins.Int{TypeName: "int"}.LookupBySegment(seg)
}
func (plainInt) MapIterator() datamodel.MapIterator {
return nil
}
func (plainInt) ListIterator() datamodel.ListIterator {
return nil
}
func (plainInt) Length() int64 {
return -1
}
func (plainInt) IsAbsent() bool {
return false
}
func (plainInt) IsNull() bool {
return false
}
func (plainInt) AsBool() (bool, error) {
return mixins.Int{TypeName: "int"}.AsBool()
}
func (n plainInt) AsInt() (int64, error) {
return int64(n), nil
}
func (plainInt) AsFloat() (float64, error) {
return mixins.Int{TypeName: "int"}.AsFloat()
}
func (plainInt) AsString() (string, error) {
return mixins.Int{TypeName: "int"}.AsString()
}
func (plainInt) AsBytes() ([]byte, error) {
return mixins.Int{TypeName: "int"}.AsBytes()
}
func (plainInt) AsLink() (datamodel.Link, error) {
return mixins.Int{TypeName: "int"}.AsLink()
}
func (plainInt) Prototype() datamodel.NodePrototype {
return Prototype__Int{}
}
// plainUint is a simple boxed uint64 that complies with datamodel.Node,
// allowing representation of the uint64 range above the int64 maximum via the
// UintNode interface
type plainUint uint64
// -- Node interface methods for plainUint -->
func (plainUint) Kind() datamodel.Kind {
return datamodel.Kind_Int
}
func (plainUint) LookupByString(string) (datamodel.Node, error) {
return mixins.Int{TypeName: "int"}.LookupByString("")
}
func (plainUint) LookupByNode(key datamodel.Node) (datamodel.Node, error) {
return mixins.Int{TypeName: "int"}.LookupByNode(nil)
}
func (plainUint) LookupByIndex(idx int64) (datamodel.Node, error) {
return mixins.Int{TypeName: "int"}.LookupByIndex(0)
}
func (plainUint) LookupBySegment(seg datamodel.PathSegment) (datamodel.Node, error) {
return mixins.Int{TypeName: "int"}.LookupBySegment(seg)
}
func (plainUint) MapIterator() datamodel.MapIterator {
return nil
}
func (plainUint) ListIterator() datamodel.ListIterator {
return nil
}
func (plainUint) Length() int64 {
return -1
}
func (plainUint) IsAbsent() bool {
return false
}
func (plainUint) IsNull() bool {
return false
}
func (plainUint) AsBool() (bool, error) {
return mixins.Int{TypeName: "int"}.AsBool()
}
func (n plainUint) AsInt() (int64, error) {
if uint64(n) > uint64(math.MaxInt64) {
return -1, fmt.Errorf("unsigned integer out of range of int64 type")
}
return int64(n), nil
}
func (plainUint) AsFloat() (float64, error) {
return mixins.Int{TypeName: "int"}.AsFloat()
}
func (plainUint) AsString() (string, error) {
return mixins.Int{TypeName: "int"}.AsString()
}
func (plainUint) AsBytes() ([]byte, error) {
return mixins.Int{TypeName: "int"}.AsBytes()
}
func (plainUint) AsLink() (datamodel.Link, error) {
return mixins.Int{TypeName: "int"}.AsLink()
}
func (plainUint) Prototype() datamodel.NodePrototype {
return Prototype__Int{}
}
// allows plainUint to conform to the plainUint interface
func (n plainUint) AsUint() (uint64, error) {
return uint64(n), nil
}
// -- NodePrototype -->
type Prototype__Int struct{}
func (Prototype__Int) NewBuilder() datamodel.NodeBuilder {
var w plainInt
return &plainInt__Builder{plainInt__Assembler{w: &w}}
}
// -- NodeBuilder -->
type plainInt__Builder struct {
plainInt__Assembler
}
func (nb *plainInt__Builder) Build() datamodel.Node {
return nb.w
}
func (nb *plainInt__Builder) Reset() {
var w plainInt
*nb = plainInt__Builder{plainInt__Assembler{w: &w}}
}
// -- NodeAssembler -->
type plainInt__Assembler struct {
w *plainInt
}
func (plainInt__Assembler) BeginMap(sizeHint int64) (datamodel.MapAssembler, error) {
return mixins.IntAssembler{TypeName: "int"}.BeginMap(0)
}
func (plainInt__Assembler) BeginList(sizeHint int64) (datamodel.ListAssembler, error) {
return mixins.IntAssembler{TypeName: "int"}.BeginList(0)
}
func (plainInt__Assembler) AssignNull() error {
return mixins.IntAssembler{TypeName: "int"}.AssignNull()
}
func (plainInt__Assembler) AssignBool(bool) error {
return mixins.IntAssembler{TypeName: "int"}.AssignBool(false)
}
func (na *plainInt__Assembler) AssignInt(v int64) error {
*na.w = plainInt(v)
return nil
}
func (plainInt__Assembler) AssignFloat(float64) error {
return mixins.IntAssembler{TypeName: "int"}.AssignFloat(0)
}
func (plainInt__Assembler) AssignString(string) error {
return mixins.IntAssembler{TypeName: "int"}.AssignString("")
}
func (plainInt__Assembler) AssignBytes([]byte) error {
return mixins.IntAssembler{TypeName: "int"}.AssignBytes(nil)
}
func (plainInt__Assembler) AssignLink(datamodel.Link) error {
return mixins.IntAssembler{TypeName: "int"}.AssignLink(nil)
}
func (na *plainInt__Assembler) AssignNode(v datamodel.Node) error {
if v2, err := v.AsInt(); err != nil {
return err
} else {
*na.w = plainInt(v2)
return nil
}
}
func (plainInt__Assembler) Prototype() datamodel.NodePrototype {
return Prototype__Int{}
}

View File

@@ -0,0 +1,145 @@
package basicnode
import (
"github.com/ipld/go-ipld-prime/datamodel"
"github.com/ipld/go-ipld-prime/node/mixins"
)
var (
_ datamodel.Node = &plainLink{}
_ datamodel.NodePrototype = Prototype__Link{}
_ datamodel.NodeBuilder = &plainLink__Builder{}
_ datamodel.NodeAssembler = &plainLink__Assembler{}
)
func NewLink(value datamodel.Link) datamodel.Node {
return &plainLink{value}
}
// plainLink is a simple box around a Link that complies with datamodel.Node.
type plainLink struct {
x datamodel.Link
}
// -- Node interface methods -->
func (plainLink) Kind() datamodel.Kind {
return datamodel.Kind_Link
}
func (plainLink) LookupByString(string) (datamodel.Node, error) {
return mixins.Link{TypeName: "link"}.LookupByString("")
}
func (plainLink) LookupByNode(key datamodel.Node) (datamodel.Node, error) {
return mixins.Link{TypeName: "link"}.LookupByNode(nil)
}
func (plainLink) LookupByIndex(idx int64) (datamodel.Node, error) {
return mixins.Link{TypeName: "link"}.LookupByIndex(0)
}
func (plainLink) LookupBySegment(seg datamodel.PathSegment) (datamodel.Node, error) {
return mixins.Link{TypeName: "link"}.LookupBySegment(seg)
}
func (plainLink) MapIterator() datamodel.MapIterator {
return nil
}
func (plainLink) ListIterator() datamodel.ListIterator {
return nil
}
func (plainLink) Length() int64 {
return -1
}
func (plainLink) IsAbsent() bool {
return false
}
func (plainLink) IsNull() bool {
return false
}
func (plainLink) AsBool() (bool, error) {
return mixins.Link{TypeName: "link"}.AsBool()
}
func (plainLink) AsInt() (int64, error) {
return mixins.Link{TypeName: "link"}.AsInt()
}
func (plainLink) AsFloat() (float64, error) {
return mixins.Link{TypeName: "link"}.AsFloat()
}
func (plainLink) AsString() (string, error) {
return mixins.Link{TypeName: "link"}.AsString()
}
func (plainLink) AsBytes() ([]byte, error) {
return mixins.Link{TypeName: "link"}.AsBytes()
}
func (n *plainLink) AsLink() (datamodel.Link, error) {
return n.x, nil
}
func (plainLink) Prototype() datamodel.NodePrototype {
return Prototype__Link{}
}
// -- NodePrototype -->
type Prototype__Link struct{}
func (Prototype__Link) NewBuilder() datamodel.NodeBuilder {
var w plainLink
return &plainLink__Builder{plainLink__Assembler{w: &w}}
}
// -- NodeBuilder -->
type plainLink__Builder struct {
plainLink__Assembler
}
func (nb *plainLink__Builder) Build() datamodel.Node {
return nb.w
}
func (nb *plainLink__Builder) Reset() {
var w plainLink
*nb = plainLink__Builder{plainLink__Assembler{w: &w}}
}
// -- NodeAssembler -->
type plainLink__Assembler struct {
w *plainLink
}
func (plainLink__Assembler) BeginMap(sizeHint int64) (datamodel.MapAssembler, error) {
return mixins.LinkAssembler{TypeName: "link"}.BeginMap(0)
}
func (plainLink__Assembler) BeginList(sizeHint int64) (datamodel.ListAssembler, error) {
return mixins.LinkAssembler{TypeName: "link"}.BeginList(0)
}
func (plainLink__Assembler) AssignNull() error {
return mixins.LinkAssembler{TypeName: "link"}.AssignNull()
}
func (plainLink__Assembler) AssignBool(bool) error {
return mixins.LinkAssembler{TypeName: "link"}.AssignBool(false)
}
func (plainLink__Assembler) AssignInt(int64) error {
return mixins.LinkAssembler{TypeName: "link"}.AssignInt(0)
}
func (plainLink__Assembler) AssignFloat(float64) error {
return mixins.LinkAssembler{TypeName: "link"}.AssignFloat(0)
}
func (plainLink__Assembler) AssignString(string) error {
return mixins.LinkAssembler{TypeName: "link"}.AssignString("")
}
func (plainLink__Assembler) AssignBytes([]byte) error {
return mixins.LinkAssembler{TypeName: "link"}.AssignBytes(nil)
}
func (na *plainLink__Assembler) AssignLink(v datamodel.Link) error {
na.w.x = v
return nil
}
func (na *plainLink__Assembler) AssignNode(v datamodel.Node) error {
if v2, err := v.AsLink(); err != nil {
return err
} else {
na.w.x = v2
return nil
}
}
func (plainLink__Assembler) Prototype() datamodel.NodePrototype {
return Prototype__Link{}
}

View File

@@ -0,0 +1,360 @@
package basicnode
import (
"github.com/ipld/go-ipld-prime/datamodel"
"github.com/ipld/go-ipld-prime/node/mixins"
)
var (
_ datamodel.Node = &plainList{}
_ datamodel.NodePrototype = Prototype__List{}
_ datamodel.NodeBuilder = &plainList__Builder{}
_ datamodel.NodeAssembler = &plainList__Assembler{}
)
// plainList is a concrete type that provides a list-kind datamodel.Node.
// It can contain any kind of value.
// plainList is also embedded in the 'any' struct and usable from there.
type plainList struct {
x []datamodel.Node
}
// -- Node interface methods -->
func (plainList) Kind() datamodel.Kind {
return datamodel.Kind_List
}
func (plainList) LookupByString(string) (datamodel.Node, error) {
return mixins.List{TypeName: "list"}.LookupByString("")
}
func (plainList) LookupByNode(datamodel.Node) (datamodel.Node, error) {
return mixins.List{TypeName: "list"}.LookupByNode(nil)
}
func (n *plainList) LookupByIndex(idx int64) (datamodel.Node, error) {
if n.Length() <= idx {
return nil, datamodel.ErrNotExists{Segment: datamodel.PathSegmentOfInt(idx)}
}
return n.x[idx], nil
}
func (n *plainList) LookupBySegment(seg datamodel.PathSegment) (datamodel.Node, error) {
idx, err := seg.Index()
if err != nil {
return nil, datamodel.ErrInvalidSegmentForList{TroubleSegment: seg, Reason: err}
}
return n.LookupByIndex(idx)
}
func (plainList) MapIterator() datamodel.MapIterator {
return nil
}
func (n *plainList) ListIterator() datamodel.ListIterator {
return &plainList_ListIterator{n, 0}
}
func (n *plainList) Length() int64 {
return int64(len(n.x))
}
func (plainList) IsAbsent() bool {
return false
}
func (plainList) IsNull() bool {
return false
}
func (plainList) AsBool() (bool, error) {
return mixins.List{TypeName: "list"}.AsBool()
}
func (plainList) AsInt() (int64, error) {
return mixins.List{TypeName: "list"}.AsInt()
}
func (plainList) AsFloat() (float64, error) {
return mixins.List{TypeName: "list"}.AsFloat()
}
func (plainList) AsString() (string, error) {
return mixins.List{TypeName: "list"}.AsString()
}
func (plainList) AsBytes() ([]byte, error) {
return mixins.List{TypeName: "list"}.AsBytes()
}
func (plainList) AsLink() (datamodel.Link, error) {
return mixins.List{TypeName: "list"}.AsLink()
}
func (plainList) Prototype() datamodel.NodePrototype {
return Prototype.List
}
type plainList_ListIterator struct {
n *plainList
idx int
}
func (itr *plainList_ListIterator) Next() (idx int64, v datamodel.Node, _ error) {
if itr.Done() {
return -1, nil, datamodel.ErrIteratorOverread{}
}
v = itr.n.x[itr.idx]
idx = int64(itr.idx)
itr.idx++
return
}
func (itr *plainList_ListIterator) Done() bool {
return itr.idx >= len(itr.n.x)
}
// -- NodePrototype -->
type Prototype__List struct{}
func (Prototype__List) NewBuilder() datamodel.NodeBuilder {
return &plainList__Builder{plainList__Assembler{w: &plainList{}}}
}
// -- NodeBuilder -->
type plainList__Builder struct {
plainList__Assembler
}
func (nb *plainList__Builder) Build() datamodel.Node {
if nb.state != laState_finished {
panic("invalid state: assembler must be 'finished' before Build can be called!")
}
return nb.w
}
func (nb *plainList__Builder) Reset() {
*nb = plainList__Builder{}
nb.w = &plainList{}
}
// -- NodeAssembler -->
type plainList__Assembler struct {
w *plainList
va plainList__ValueAssembler
state laState
}
type plainList__ValueAssembler struct {
la *plainList__Assembler
}
// laState is an enum of the state machine for a list assembler.
// (this might be something to export reusably, but it's also very much an impl detail that need not be seen, so, dubious.)
// it's similar to maState for maps, but has fewer states because we never have keys to assemble.
type laState uint8
const (
laState_initial laState = iota // also the 'expect value or finish' state
laState_midValue // waiting for a 'finished' state in the ValueAssembler.
laState_finished // 'w' will also be nil, but this is a politer statement
)
func (plainList__Assembler) BeginMap(sizeHint int64) (datamodel.MapAssembler, error) {
return mixins.ListAssembler{TypeName: "list"}.BeginMap(0)
}
func (na *plainList__Assembler) BeginList(sizeHint int64) (datamodel.ListAssembler, error) {
if sizeHint < 0 {
sizeHint = 0
}
// Allocate storage space.
na.w.x = make([]datamodel.Node, 0, sizeHint)
// That's it; return self as the ListAssembler. We already have all the right methods on this structure.
return na, nil
}
func (plainList__Assembler) AssignNull() error {
return mixins.ListAssembler{TypeName: "list"}.AssignNull()
}
func (plainList__Assembler) AssignBool(bool) error {
return mixins.ListAssembler{TypeName: "list"}.AssignBool(false)
}
func (plainList__Assembler) AssignInt(int64) error {
return mixins.ListAssembler{TypeName: "list"}.AssignInt(0)
}
func (plainList__Assembler) AssignFloat(float64) error {
return mixins.ListAssembler{TypeName: "list"}.AssignFloat(0)
}
func (plainList__Assembler) AssignString(string) error {
return mixins.ListAssembler{TypeName: "list"}.AssignString("")
}
func (plainList__Assembler) AssignBytes([]byte) error {
return mixins.ListAssembler{TypeName: "list"}.AssignBytes(nil)
}
func (plainList__Assembler) AssignLink(datamodel.Link) error {
return mixins.ListAssembler{TypeName: "list"}.AssignLink(nil)
}
func (na *plainList__Assembler) AssignNode(v datamodel.Node) error {
// Sanity check, then update, assembler state.
// Update of state to 'finished' comes later; where exactly depends on if shortcuts apply.
if na.state != laState_initial {
panic("misuse")
}
// Copy the content.
if v2, ok := v.(*plainList); ok { // if our own type: shortcut.
// Copy the structure by value.
// This means we'll have pointers into the same internal maps and slices;
// this is okay, because the Node type promises it's immutable, and we are going to instantly finish ourselves to also maintain that.
// FIXME: the shortcut behaves differently than the long way: it discards any existing progress. Doesn't violate immut, but is odd.
*na.w = *v2
na.state = laState_finished
return nil
}
// If the above shortcut didn't work, resort to a generic copy.
// We call AssignNode for all the child values, giving them a chance to hit shortcuts even if we didn't.
if v.Kind() != datamodel.Kind_List {
return datamodel.ErrWrongKind{TypeName: "list", MethodName: "AssignNode", AppropriateKind: datamodel.KindSet_JustList, ActualKind: v.Kind()}
}
itr := v.ListIterator()
for !itr.Done() {
_, v, err := itr.Next()
if err != nil {
return err
}
if err := na.AssembleValue().AssignNode(v); err != nil {
return err
}
}
return na.Finish()
}
func (plainList__Assembler) Prototype() datamodel.NodePrototype {
return Prototype.List
}
// -- ListAssembler -->
// AssembleValue is part of conforming to ListAssembler, which we do on
// plainList__Assembler so that BeginList can just return a retyped pointer rather than new object.
func (la *plainList__Assembler) AssembleValue() datamodel.NodeAssembler {
// Sanity check, then update, assembler state.
if la.state != laState_initial {
panic("misuse")
}
la.state = laState_midValue
// Make value assembler valid by giving it pointer back to whole 'la'; yield it.
la.va.la = la
return &la.va
}
// Finish is part of conforming to ListAssembler, which we do on
// plainList__Assembler so that BeginList can just return a retyped pointer rather than new object.
func (la *plainList__Assembler) Finish() error {
// Sanity check, then update, assembler state.
if la.state != laState_initial {
panic("misuse")
}
la.state = laState_finished
// validators could run and report errors promptly, if this type had any.
return nil
}
func (plainList__Assembler) ValuePrototype(_ int64) datamodel.NodePrototype {
return Prototype.Any
}
// -- ListAssembler.ValueAssembler -->
func (lva *plainList__ValueAssembler) BeginMap(sizeHint int64) (datamodel.MapAssembler, error) {
ma := plainList__ValueAssemblerMap{}
ma.ca.w = &plainMap{}
ma.p = lva.la
_, err := ma.ca.BeginMap(sizeHint)
return &ma, err
}
func (lva *plainList__ValueAssembler) BeginList(sizeHint int64) (datamodel.ListAssembler, error) {
la := plainList__ValueAssemblerList{}
la.ca.w = &plainList{}
la.p = lva.la
_, err := la.ca.BeginList(sizeHint)
return &la, err
}
func (lva *plainList__ValueAssembler) AssignNull() error {
return lva.AssignNode(datamodel.Null)
}
func (lva *plainList__ValueAssembler) AssignBool(v bool) error {
vb := plainBool(v)
return lva.AssignNode(&vb)
}
func (lva *plainList__ValueAssembler) AssignInt(v int64) error {
vb := plainInt(v)
return lva.AssignNode(&vb)
}
func (lva *plainList__ValueAssembler) AssignFloat(v float64) error {
vb := plainFloat(v)
return lva.AssignNode(&vb)
}
func (lva *plainList__ValueAssembler) AssignString(v string) error {
vb := plainString(v)
return lva.AssignNode(&vb)
}
func (lva *plainList__ValueAssembler) AssignBytes(v []byte) error {
vb := plainBytes(v)
return lva.AssignNode(&vb)
}
func (lva *plainList__ValueAssembler) AssignLink(v datamodel.Link) error {
vb := plainLink{v}
return lva.AssignNode(&vb)
}
func (lva *plainList__ValueAssembler) AssignNode(v datamodel.Node) error {
lva.la.w.x = append(lva.la.w.x, v)
lva.la.state = laState_initial
lva.la = nil // invalidate self to prevent further incorrect use.
return nil
}
func (plainList__ValueAssembler) Prototype() datamodel.NodePrototype {
return Prototype.Any
}
type plainList__ValueAssemblerMap struct {
ca plainMap__Assembler
p *plainList__Assembler // pointer back to parent, for final insert and state bump
}
// we briefly state only the methods we need to delegate here.
// just embedding plainMap__Assembler also behaves correctly,
// but causes a lot of unnecessary autogenerated functions in the final binary.
func (ma *plainList__ValueAssemblerMap) AssembleEntry(k string) (datamodel.NodeAssembler, error) {
return ma.ca.AssembleEntry(k)
}
func (ma *plainList__ValueAssemblerMap) AssembleKey() datamodel.NodeAssembler {
return ma.ca.AssembleKey()
}
func (ma *plainList__ValueAssemblerMap) AssembleValue() datamodel.NodeAssembler {
return ma.ca.AssembleValue()
}
func (plainList__ValueAssemblerMap) KeyPrototype() datamodel.NodePrototype {
return Prototype__String{}
}
func (plainList__ValueAssemblerMap) ValuePrototype(_ string) datamodel.NodePrototype {
return Prototype.Any
}
func (ma *plainList__ValueAssemblerMap) Finish() error {
if err := ma.ca.Finish(); err != nil {
return err
}
w := ma.ca.w
ma.ca.w = nil
return ma.p.va.AssignNode(w)
}
type plainList__ValueAssemblerList struct {
ca plainList__Assembler
p *plainList__Assembler // pointer back to parent, for final insert and state bump
}
// we briefly state only the methods we need to delegate here.
// just embedding plainList__Assembler also behaves correctly,
// but causes a lot of unnecessary autogenerated functions in the final binary.
func (la *plainList__ValueAssemblerList) AssembleValue() datamodel.NodeAssembler {
return la.ca.AssembleValue()
}
func (plainList__ValueAssemblerList) ValuePrototype(_ int64) datamodel.NodePrototype {
return Prototype.Any
}
func (la *plainList__ValueAssemblerList) Finish() error {
if err := la.ca.Finish(); err != nil {
return err
}
w := la.ca.w
la.ca.w = nil
return la.p.va.AssignNode(w)
}

View File

@@ -0,0 +1,474 @@
package basicnode
import (
"fmt"
"github.com/ipld/go-ipld-prime/datamodel"
"github.com/ipld/go-ipld-prime/node/mixins"
)
var (
_ datamodel.Node = &plainMap{}
_ datamodel.NodePrototype = Prototype__Map{}
_ datamodel.NodeBuilder = &plainMap__Builder{}
_ datamodel.NodeAssembler = &plainMap__Assembler{}
)
// plainMap is a concrete type that provides a map-kind datamodel.Node.
// It can contain any kind of value.
// plainMap is also embedded in the 'any' struct and usable from there.
type plainMap struct {
m map[string]datamodel.Node // string key -- even if a runtime schema wrapper is using us for storage, we must have a comparable type here, and string is all we know.
t []plainMap__Entry // table for fast iteration, order keeping, and yielding pointers to enable alloc/conv amortization.
}
type plainMap__Entry struct {
k plainString // address of this used when we return keys as nodes, such as in iterators. Need in one place to amortize shifts to heap when ptr'ing for iface.
v datamodel.Node // identical to map values. keeping them here simplifies iteration. (in codegen'd maps, this position is also part of amortization, but in this implementation, that's less useful.)
// note on alternate implementations: 'v' could also use the 'any' type, and thus amortize value allocations. the memory size trade would be large however, so we don't, here.
}
// -- Node interface methods -->
func (plainMap) Kind() datamodel.Kind {
return datamodel.Kind_Map
}
func (n *plainMap) LookupByString(key string) (datamodel.Node, error) {
v, exists := n.m[key]
if !exists {
return nil, datamodel.ErrNotExists{Segment: datamodel.PathSegmentOfString(key)}
}
return v, nil
}
func (n *plainMap) LookupByNode(key datamodel.Node) (datamodel.Node, error) {
ks, err := key.AsString()
if err != nil {
return nil, err
}
return n.LookupByString(ks)
}
func (plainMap) LookupByIndex(idx int64) (datamodel.Node, error) {
return mixins.Map{TypeName: "map"}.LookupByIndex(0)
}
func (n *plainMap) LookupBySegment(seg datamodel.PathSegment) (datamodel.Node, error) {
return n.LookupByString(seg.String())
}
func (n *plainMap) MapIterator() datamodel.MapIterator {
return &plainMap_MapIterator{n, 0}
}
func (plainMap) ListIterator() datamodel.ListIterator {
return nil
}
func (n *plainMap) Length() int64 {
return int64(len(n.t))
}
func (plainMap) IsAbsent() bool {
return false
}
func (plainMap) IsNull() bool {
return false
}
func (plainMap) AsBool() (bool, error) {
return mixins.Map{TypeName: "map"}.AsBool()
}
func (plainMap) AsInt() (int64, error) {
return mixins.Map{TypeName: "map"}.AsInt()
}
func (plainMap) AsFloat() (float64, error) {
return mixins.Map{TypeName: "map"}.AsFloat()
}
func (plainMap) AsString() (string, error) {
return mixins.Map{TypeName: "map"}.AsString()
}
func (plainMap) AsBytes() ([]byte, error) {
return mixins.Map{TypeName: "map"}.AsBytes()
}
func (plainMap) AsLink() (datamodel.Link, error) {
return mixins.Map{TypeName: "map"}.AsLink()
}
func (plainMap) Prototype() datamodel.NodePrototype {
return Prototype.Map
}
type plainMap_MapIterator struct {
n *plainMap
idx int
}
func (itr *plainMap_MapIterator) Next() (k datamodel.Node, v datamodel.Node, _ error) {
if itr.Done() {
return nil, nil, datamodel.ErrIteratorOverread{}
}
k = &itr.n.t[itr.idx].k
v = itr.n.t[itr.idx].v
itr.idx++
return
}
func (itr *plainMap_MapIterator) Done() bool {
return itr.idx >= len(itr.n.t)
}
// -- NodePrototype -->
type Prototype__Map struct{}
func (Prototype__Map) NewBuilder() datamodel.NodeBuilder {
return &plainMap__Builder{plainMap__Assembler{w: &plainMap{}}}
}
// -- NodeBuilder -->
type plainMap__Builder struct {
plainMap__Assembler
}
func (nb *plainMap__Builder) Build() datamodel.Node {
if nb.state != maState_finished {
panic("invalid state: assembler must be 'finished' before Build can be called!")
}
return nb.w
}
func (nb *plainMap__Builder) Reset() {
*nb = plainMap__Builder{}
nb.w = &plainMap{}
}
// -- NodeAssembler -->
type plainMap__Assembler struct {
w *plainMap
ka plainMap__KeyAssembler
va plainMap__ValueAssembler
state maState
}
type plainMap__KeyAssembler struct {
ma *plainMap__Assembler
}
type plainMap__ValueAssembler struct {
ma *plainMap__Assembler
}
// maState is an enum of the state machine for a map assembler.
// (this might be something to export reusably, but it's also very much an impl detail that need not be seen, so, dubious.)
type maState uint8
const (
maState_initial maState = iota // also the 'expect key or finish' state
maState_midKey // waiting for a 'finished' state in the KeyAssembler.
maState_expectValue // 'AssembleValue' is the only valid next step
maState_midValue // waiting for a 'finished' state in the ValueAssembler.
maState_finished // 'w' will also be nil, but this is a politer statement
)
func (na *plainMap__Assembler) BeginMap(sizeHint int64) (datamodel.MapAssembler, error) {
if sizeHint < 0 {
sizeHint = 0
}
// Allocate storage space.
na.w.t = make([]plainMap__Entry, 0, sizeHint)
na.w.m = make(map[string]datamodel.Node, sizeHint)
// That's it; return self as the MapAssembler. We already have all the right methods on this structure.
return na, nil
}
func (plainMap__Assembler) BeginList(sizeHint int64) (datamodel.ListAssembler, error) {
return mixins.MapAssembler{TypeName: "map"}.BeginList(0)
}
func (plainMap__Assembler) AssignNull() error {
return mixins.MapAssembler{TypeName: "map"}.AssignNull()
}
func (plainMap__Assembler) AssignBool(bool) error {
return mixins.MapAssembler{TypeName: "map"}.AssignBool(false)
}
func (plainMap__Assembler) AssignInt(int64) error {
return mixins.MapAssembler{TypeName: "map"}.AssignInt(0)
}
func (plainMap__Assembler) AssignFloat(float64) error {
return mixins.MapAssembler{TypeName: "map"}.AssignFloat(0)
}
func (plainMap__Assembler) AssignString(string) error {
return mixins.MapAssembler{TypeName: "map"}.AssignString("")
}
func (plainMap__Assembler) AssignBytes([]byte) error {
return mixins.MapAssembler{TypeName: "map"}.AssignBytes(nil)
}
func (plainMap__Assembler) AssignLink(datamodel.Link) error {
return mixins.MapAssembler{TypeName: "map"}.AssignLink(nil)
}
func (na *plainMap__Assembler) AssignNode(v datamodel.Node) error {
// Sanity check assembler state.
// Update of state to 'finished' comes later; where exactly depends on if shortcuts apply.
if na.state != maState_initial {
panic("misuse")
}
// Copy the content.
if v2, ok := v.(*plainMap); ok { // if our own type: shortcut.
// Copy the structure by value.
// This means we'll have pointers into the same internal maps and slices;
// this is okay, because the Node type promises it's immutable, and we are going to instantly finish ourselves to also maintain that.
// FIXME: the shortcut behaves differently than the long way: it discards any existing progress. Doesn't violate immut, but is odd.
*na.w = *v2
na.state = maState_finished
return nil
}
// If the above shortcut didn't work, resort to a generic copy.
// We call AssignNode for all the child values, giving them a chance to hit shortcuts even if we didn't.
if v.Kind() != datamodel.Kind_Map {
return datamodel.ErrWrongKind{TypeName: "map", MethodName: "AssignNode", AppropriateKind: datamodel.KindSet_JustMap, ActualKind: v.Kind()}
}
itr := v.MapIterator()
for !itr.Done() {
k, v, err := itr.Next()
if err != nil {
return err
}
if err := na.AssembleKey().AssignNode(k); err != nil {
return err
}
if err := na.AssembleValue().AssignNode(v); err != nil {
return err
}
}
return na.Finish()
}
func (plainMap__Assembler) Prototype() datamodel.NodePrototype {
return Prototype.Map
}
// -- MapAssembler -->
// AssembleEntry is part of conforming to MapAssembler, which we do on
// plainMap__Assembler so that BeginMap can just return a retyped pointer rather than new object.
func (ma *plainMap__Assembler) AssembleEntry(k string) (datamodel.NodeAssembler, error) {
// Sanity check assembler state.
// Update of state comes after possible key rejection.
if ma.state != maState_initial {
panic("misuse")
}
// Check for dup keys; error if so.
_, exists := ma.w.m[k]
if exists {
return nil, datamodel.ErrRepeatedMapKey{Key: plainString(k)}
}
ma.state = maState_midValue
ma.w.t = append(ma.w.t, plainMap__Entry{k: plainString(k)})
// Make value assembler valid by giving it pointer back to whole 'ma'; yield it.
ma.va.ma = ma
return &ma.va, nil
}
// AssembleKey is part of conforming to MapAssembler, which we do on
// plainMap__Assembler so that BeginMap can just return a retyped pointer rather than new object.
func (ma *plainMap__Assembler) AssembleKey() datamodel.NodeAssembler {
// Sanity check, then update, assembler state.
if ma.state != maState_initial {
panic("misuse")
}
ma.state = maState_midKey
// Make key assembler valid by giving it pointer back to whole 'ma'; yield it.
ma.ka.ma = ma
return &ma.ka
}
// AssembleValue is part of conforming to MapAssembler, which we do on
// plainMap__Assembler so that BeginMap can just return a retyped pointer rather than new object.
func (ma *plainMap__Assembler) AssembleValue() datamodel.NodeAssembler {
// Sanity check, then update, assembler state.
if ma.state != maState_expectValue {
panic("misuse")
}
ma.state = maState_midValue
// Make value assembler valid by giving it pointer back to whole 'ma'; yield it.
ma.va.ma = ma
return &ma.va
}
// Finish is part of conforming to MapAssembler, which we do on
// plainMap__Assembler so that BeginMap can just return a retyped pointer rather than new object.
func (ma *plainMap__Assembler) Finish() error {
// Sanity check, then update, assembler state.
if ma.state != maState_initial {
panic("misuse")
}
ma.state = maState_finished
// validators could run and report errors promptly, if this type had any.
return nil
}
func (plainMap__Assembler) KeyPrototype() datamodel.NodePrototype {
return Prototype__String{}
}
func (plainMap__Assembler) ValuePrototype(_ string) datamodel.NodePrototype {
return Prototype.Any
}
// -- MapAssembler.KeyAssembler -->
func (plainMap__KeyAssembler) BeginMap(sizeHint int64) (datamodel.MapAssembler, error) {
return mixins.StringAssembler{TypeName: "string"}.BeginMap(0)
}
func (plainMap__KeyAssembler) BeginList(sizeHint int64) (datamodel.ListAssembler, error) {
return mixins.StringAssembler{TypeName: "string"}.BeginList(0)
}
func (plainMap__KeyAssembler) AssignNull() error {
return mixins.StringAssembler{TypeName: "string"}.AssignNull()
}
func (plainMap__KeyAssembler) AssignBool(bool) error {
return mixins.StringAssembler{TypeName: "string"}.AssignBool(false)
}
func (plainMap__KeyAssembler) AssignInt(int64) error {
return mixins.StringAssembler{TypeName: "string"}.AssignInt(0)
}
func (plainMap__KeyAssembler) AssignFloat(float64) error {
return mixins.StringAssembler{TypeName: "string"}.AssignFloat(0)
}
func (mka *plainMap__KeyAssembler) AssignString(v string) error {
// Check for dup keys; error if so.
// (And, backtrack state to accepting keys again so we don't get eternally wedged here.)
_, exists := mka.ma.w.m[v]
if exists {
mka.ma.state = maState_initial
mka.ma = nil // invalidate self to prevent further incorrect use.
return datamodel.ErrRepeatedMapKey{Key: plainString(v)}
}
// Assign the key into the end of the entry table;
// we'll be doing map insertions after we get the value in hand.
// (There's no need to delegate to another assembler for the key type,
// because we're just at Data Model level here, which only regards plain strings.)
mka.ma.w.t = append(mka.ma.w.t, plainMap__Entry{})
mka.ma.w.t[len(mka.ma.w.t)-1].k = plainString(v)
// Update parent assembler state: clear to proceed.
mka.ma.state = maState_expectValue
mka.ma = nil // invalidate self to prevent further incorrect use.
return nil
}
func (plainMap__KeyAssembler) AssignBytes([]byte) error {
return mixins.StringAssembler{TypeName: "string"}.AssignBytes(nil)
}
func (plainMap__KeyAssembler) AssignLink(datamodel.Link) error {
return mixins.StringAssembler{TypeName: "string"}.AssignLink(nil)
}
func (mka *plainMap__KeyAssembler) AssignNode(v datamodel.Node) error {
vs, err := v.AsString()
if err != nil {
return fmt.Errorf("cannot assign non-string node into map key assembler") // FIXME:errors: this doesn't quite fit in ErrWrongKind cleanly; new error type?
}
return mka.AssignString(vs)
}
func (plainMap__KeyAssembler) Prototype() datamodel.NodePrototype {
return Prototype__String{}
}
// -- MapAssembler.ValueAssembler -->
func (mva *plainMap__ValueAssembler) BeginMap(sizeHint int64) (datamodel.MapAssembler, error) {
ma := plainMap__ValueAssemblerMap{}
ma.ca.w = &plainMap{}
ma.p = mva.ma
_, err := ma.ca.BeginMap(sizeHint)
return &ma, err
}
func (mva *plainMap__ValueAssembler) BeginList(sizeHint int64) (datamodel.ListAssembler, error) {
la := plainMap__ValueAssemblerList{}
la.ca.w = &plainList{}
la.p = mva.ma
_, err := la.ca.BeginList(sizeHint)
return &la, err
}
func (mva *plainMap__ValueAssembler) AssignNull() error {
return mva.AssignNode(datamodel.Null)
}
func (mva *plainMap__ValueAssembler) AssignBool(v bool) error {
vb := plainBool(v)
return mva.AssignNode(&vb)
}
func (mva *plainMap__ValueAssembler) AssignInt(v int64) error {
vb := plainInt(v)
return mva.AssignNode(&vb)
}
func (mva *plainMap__ValueAssembler) AssignFloat(v float64) error {
vb := plainFloat(v)
return mva.AssignNode(&vb)
}
func (mva *plainMap__ValueAssembler) AssignString(v string) error {
vb := plainString(v)
return mva.AssignNode(&vb)
}
func (mva *plainMap__ValueAssembler) AssignBytes(v []byte) error {
vb := plainBytes(v)
return mva.AssignNode(&vb)
}
func (mva *plainMap__ValueAssembler) AssignLink(v datamodel.Link) error {
vb := plainLink{v}
return mva.AssignNode(&vb)
}
func (mva *plainMap__ValueAssembler) AssignNode(v datamodel.Node) error {
l := len(mva.ma.w.t) - 1
mva.ma.w.t[l].v = v
mva.ma.w.m[string(mva.ma.w.t[l].k)] = v
mva.ma.state = maState_initial
mva.ma = nil // invalidate self to prevent further incorrect use.
return nil
}
func (plainMap__ValueAssembler) Prototype() datamodel.NodePrototype {
return Prototype.Any
}
type plainMap__ValueAssemblerMap struct {
ca plainMap__Assembler
p *plainMap__Assembler // pointer back to parent, for final insert and state bump
}
// we briefly state only the methods we need to delegate here.
// just embedding plainMap__Assembler also behaves correctly,
// but causes a lot of unnecessary autogenerated functions in the final binary.
func (ma *plainMap__ValueAssemblerMap) AssembleEntry(k string) (datamodel.NodeAssembler, error) {
return ma.ca.AssembleEntry(k)
}
func (ma *plainMap__ValueAssemblerMap) AssembleKey() datamodel.NodeAssembler {
return ma.ca.AssembleKey()
}
func (ma *plainMap__ValueAssemblerMap) AssembleValue() datamodel.NodeAssembler {
return ma.ca.AssembleValue()
}
func (plainMap__ValueAssemblerMap) KeyPrototype() datamodel.NodePrototype {
return Prototype__String{}
}
func (plainMap__ValueAssemblerMap) ValuePrototype(_ string) datamodel.NodePrototype {
return Prototype.Any
}
func (ma *plainMap__ValueAssemblerMap) Finish() error {
if err := ma.ca.Finish(); err != nil {
return err
}
w := ma.ca.w
ma.ca.w = nil
return ma.p.va.AssignNode(w)
}
type plainMap__ValueAssemblerList struct {
ca plainList__Assembler
p *plainMap__Assembler // pointer back to parent, for final insert and state bump
}
// we briefly state only the methods we need to delegate here.
// just embedding plainList__Assembler also behaves correctly,
// but causes a lot of unnecessary autogenerated functions in the final binary.
func (la *plainMap__ValueAssemblerList) AssembleValue() datamodel.NodeAssembler {
return la.ca.AssembleValue()
}
func (plainMap__ValueAssemblerList) ValuePrototype(_ int64) datamodel.NodePrototype {
return Prototype.Any
}
func (la *plainMap__ValueAssemblerList) Finish() error {
if err := la.ca.Finish(); err != nil {
return err
}
w := la.ca.w
la.ca.w = nil
return la.p.va.AssignNode(w)
}

View File

@@ -0,0 +1,26 @@
package basicnode
// Prototype embeds a NodePrototype for every kind of Node implementation in this package.
// You can use it like this:
//
// basicnode.Prototype.Map.NewBuilder().BeginMap() //...
//
// and:
//
// basicnode.Prototype.String.NewBuilder().AssignString("x") // ...
//
// Most of the prototypes are for one particular Kind of node (e.g. string, int, etc);
// you can use the "Any" style if you want a builder that can accept any kind of data.
var Prototype prototype
type prototype struct {
Any Prototype__Any
Map Prototype__Map
List Prototype__List
Bool Prototype__Bool
Int Prototype__Int
Float Prototype__Float
String Prototype__String
Bytes Prototype__Bytes
Link Prototype__Link
}

View File

@@ -0,0 +1,149 @@
package basicnode
import (
"github.com/ipld/go-ipld-prime/datamodel"
"github.com/ipld/go-ipld-prime/node/mixins"
)
var (
_ datamodel.Node = plainString("")
_ datamodel.NodePrototype = Prototype__String{}
_ datamodel.NodeBuilder = &plainString__Builder{}
_ datamodel.NodeAssembler = &plainString__Assembler{}
)
func NewString(value string) datamodel.Node {
v := plainString(value)
return &v
}
// plainString is a simple boxed string that complies with datamodel.Node.
// It's useful for many things, such as boxing map keys.
//
// The implementation is a simple typedef of a string;
// handling it as a Node incurs 'runtime.convTstring',
// which is about the best we can do.
type plainString string
// -- Node interface methods -->
func (plainString) Kind() datamodel.Kind {
return datamodel.Kind_String
}
func (plainString) LookupByString(string) (datamodel.Node, error) {
return mixins.String{TypeName: "string"}.LookupByString("")
}
func (plainString) LookupByNode(key datamodel.Node) (datamodel.Node, error) {
return mixins.String{TypeName: "string"}.LookupByNode(nil)
}
func (plainString) LookupByIndex(idx int64) (datamodel.Node, error) {
return mixins.String{TypeName: "string"}.LookupByIndex(0)
}
func (plainString) LookupBySegment(seg datamodel.PathSegment) (datamodel.Node, error) {
return mixins.String{TypeName: "string"}.LookupBySegment(seg)
}
func (plainString) MapIterator() datamodel.MapIterator {
return nil
}
func (plainString) ListIterator() datamodel.ListIterator {
return nil
}
func (plainString) Length() int64 {
return -1
}
func (plainString) IsAbsent() bool {
return false
}
func (plainString) IsNull() bool {
return false
}
func (plainString) AsBool() (bool, error) {
return mixins.String{TypeName: "string"}.AsBool()
}
func (plainString) AsInt() (int64, error) {
return mixins.String{TypeName: "string"}.AsInt()
}
func (plainString) AsFloat() (float64, error) {
return mixins.String{TypeName: "string"}.AsFloat()
}
func (x plainString) AsString() (string, error) {
return string(x), nil
}
func (plainString) AsBytes() ([]byte, error) {
return mixins.String{TypeName: "string"}.AsBytes()
}
func (plainString) AsLink() (datamodel.Link, error) {
return mixins.String{TypeName: "string"}.AsLink()
}
func (plainString) Prototype() datamodel.NodePrototype {
return Prototype__String{}
}
// -- NodePrototype -->
type Prototype__String struct{}
func (Prototype__String) NewBuilder() datamodel.NodeBuilder {
var w plainString
return &plainString__Builder{plainString__Assembler{w: &w}}
}
// -- NodeBuilder -->
type plainString__Builder struct {
plainString__Assembler
}
func (nb *plainString__Builder) Build() datamodel.Node {
return nb.w
}
func (nb *plainString__Builder) Reset() {
var w plainString
*nb = plainString__Builder{plainString__Assembler{w: &w}}
}
// -- NodeAssembler -->
type plainString__Assembler struct {
w *plainString
}
func (plainString__Assembler) BeginMap(sizeHint int64) (datamodel.MapAssembler, error) {
return mixins.StringAssembler{TypeName: "string"}.BeginMap(0)
}
func (plainString__Assembler) BeginList(sizeHint int64) (datamodel.ListAssembler, error) {
return mixins.StringAssembler{TypeName: "string"}.BeginList(0)
}
func (plainString__Assembler) AssignNull() error {
return mixins.StringAssembler{TypeName: "string"}.AssignNull()
}
func (plainString__Assembler) AssignBool(bool) error {
return mixins.StringAssembler{TypeName: "string"}.AssignBool(false)
}
func (plainString__Assembler) AssignInt(int64) error {
return mixins.StringAssembler{TypeName: "string"}.AssignInt(0)
}
func (plainString__Assembler) AssignFloat(float64) error {
return mixins.StringAssembler{TypeName: "string"}.AssignFloat(0)
}
func (na *plainString__Assembler) AssignString(v string) error {
*na.w = plainString(v)
return nil
}
func (plainString__Assembler) AssignBytes([]byte) error {
return mixins.StringAssembler{TypeName: "string"}.AssignBytes(nil)
}
func (plainString__Assembler) AssignLink(datamodel.Link) error {
return mixins.StringAssembler{TypeName: "string"}.AssignLink(nil)
}
func (na *plainString__Assembler) AssignNode(v datamodel.Node) error {
if v2, err := v.AsString(); err != nil {
return err
} else {
*na.w = plainString(v2)
return nil
}
}
func (plainString__Assembler) Prototype() datamodel.NodePrototype {
return Prototype__String{}
}

View 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()
}

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

View 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,
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,37 @@
node mixins and how to use them
===============================
These mixins are here to:
1. reduce the amount of code you need to write to create a new Node implementation, and
2. standardize a lot of the error handling for common cases (especially, around kinds).
"Reduce the amount of code" also has an application in codegen,
where while it doesn't save any human effort, it does reduce GLOC size.
(Or more precisely, it doesn't save *lines*, since we use them in verbose style,
but it does make those lines an awful lot shorter.)
Note that these mixins are _not_ particularly here to help with performance.
- all `ErrWrongKind` error are returned by value, which means a `runtime.convT2I` which means a heap allocation.
The error paths will therefore never be "fast"; it will *always* be cheaper
to check `kind` in advance than to probe and handle errors, if efficiency is your goal.
- in general, there's really no way to improve upon the performance of having these methods simply writen directlyon your type.
These mixins will affect struct size if you use them via embed.
They can also be used without any effect on struct size if used more verbosely.
The binary/assembly output size is not affected by use of the mixins.
(If using them verbosely -- e.g. still declaring methods on your type
and using `return mixins.Kind{"TypeName"}.Method()` in the method body --
the end result is the inliner kicks in, and the end result is almost
identical binary size.)
Summary:
- SLOC: good, or neutral depending on use
- GLOC: good
- standardized: good
- speed: neutral
- mem size: neutral if used verbosely, bad if used most tersely
- asm size: neutral

View File

@@ -0,0 +1,97 @@
package mixins
import (
"github.com/ipld/go-ipld-prime/datamodel"
)
// Bool can be embedded in a struct to provide all the methods that
// have fixed output for any int-kinded nodes.
// (Mostly this includes all the methods which simply return ErrWrongKind.)
// Other methods will still need to be implemented to finish conforming to Node.
//
// To conserve memory and get a TypeName in errors without embedding,
// write methods on your type with a body that simply initializes this struct
// and immediately uses the relevant method;
// this is more verbose in source, but compiles to a tighter result:
// in memory, there's no embed; and in runtime, the calls will be inlined
// and thus have no cost in execution time.
type Bool struct {
TypeName string
}
func (Bool) Kind() datamodel.Kind {
return datamodel.Kind_Bool
}
func (x Bool) LookupByString(string) (datamodel.Node, error) {
return nil, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "LookupByString", AppropriateKind: datamodel.KindSet_JustMap, ActualKind: datamodel.Kind_Bool}
}
func (x Bool) LookupByNode(key datamodel.Node) (datamodel.Node, error) {
return nil, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "LookupByNode", AppropriateKind: datamodel.KindSet_JustMap, ActualKind: datamodel.Kind_Bool}
}
func (x Bool) LookupByIndex(idx int64) (datamodel.Node, error) {
return nil, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "LookupByIndex", AppropriateKind: datamodel.KindSet_JustList, ActualKind: datamodel.Kind_Bool}
}
func (x Bool) LookupBySegment(datamodel.PathSegment) (datamodel.Node, error) {
return nil, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "LookupBySegment", AppropriateKind: datamodel.KindSet_Recursive, ActualKind: datamodel.Kind_Bool}
}
func (Bool) MapIterator() datamodel.MapIterator {
return nil
}
func (Bool) ListIterator() datamodel.ListIterator {
return nil
}
func (Bool) Length() int64 {
return -1
}
func (Bool) IsAbsent() bool {
return false
}
func (Bool) IsNull() bool {
return false
}
func (x Bool) AsInt() (int64, error) {
return 0, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AsInt", AppropriateKind: datamodel.KindSet_JustInt, ActualKind: datamodel.Kind_Bool}
}
func (x Bool) AsFloat() (float64, error) {
return 0, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AsFloat", AppropriateKind: datamodel.KindSet_JustFloat, ActualKind: datamodel.Kind_Bool}
}
func (x Bool) AsString() (string, error) {
return "", datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AsString", AppropriateKind: datamodel.KindSet_JustString, ActualKind: datamodel.Kind_Bool}
}
func (x Bool) AsBytes() ([]byte, error) {
return nil, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AsBytes", AppropriateKind: datamodel.KindSet_JustBytes, ActualKind: datamodel.Kind_Bool}
}
func (x Bool) AsLink() (datamodel.Link, error) {
return nil, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AsLink", AppropriateKind: datamodel.KindSet_JustLink, ActualKind: datamodel.Kind_Bool}
}
// BoolAssembler has similar purpose as Bool, but for (you guessed it)
// the NodeAssembler interface rather than the Node interface.
type BoolAssembler struct {
TypeName string
}
func (x BoolAssembler) BeginMap(sizeHint int64) (datamodel.MapAssembler, error) {
return nil, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "BeginMap", AppropriateKind: datamodel.KindSet_JustMap, ActualKind: datamodel.Kind_Bool}
}
func (x BoolAssembler) BeginList(sizeHint int64) (datamodel.ListAssembler, error) {
return nil, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "BeginList", AppropriateKind: datamodel.KindSet_JustList, ActualKind: datamodel.Kind_Bool}
}
func (x BoolAssembler) AssignNull() error {
return datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AssignNull", AppropriateKind: datamodel.KindSet_JustNull, ActualKind: datamodel.Kind_Bool}
}
func (x BoolAssembler) AssignInt(int64) error {
return datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AssignInt", AppropriateKind: datamodel.KindSet_JustInt, ActualKind: datamodel.Kind_Bool}
}
func (x BoolAssembler) AssignFloat(float64) error {
return datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AssignFloat", AppropriateKind: datamodel.KindSet_JustFloat, ActualKind: datamodel.Kind_Bool}
}
func (x BoolAssembler) AssignString(string) error {
return datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AssignString", AppropriateKind: datamodel.KindSet_JustString, ActualKind: datamodel.Kind_Bool}
}
func (x BoolAssembler) AssignBytes([]byte) error {
return datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AssignBytes", AppropriateKind: datamodel.KindSet_JustBytes, ActualKind: datamodel.Kind_Bool}
}
func (x BoolAssembler) AssignLink(datamodel.Link) error {
return datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AssignLink", AppropriateKind: datamodel.KindSet_JustLink, ActualKind: datamodel.Kind_Bool}
}

View File

@@ -0,0 +1,97 @@
package mixins
import (
"github.com/ipld/go-ipld-prime/datamodel"
)
// Bytes can be embedded in a struct to provide all the methods that
// have fixed output for any int-kinded nodes.
// (Mostly this includes all the methods which simply return ErrWrongKind.)
// Other methods will still need to be implemented to finish conforming to Node.
//
// To conserve memory and get a TypeName in errors without embedding,
// write methods on your type with a body that simply initializes this struct
// and immediately uses the relevant method;
// this is more verbose in source, but compiles to a tighter result:
// in memory, there's no embed; and in runtime, the calls will be inlined
// and thus have no cost in execution time.
type Bytes struct {
TypeName string
}
func (Bytes) Kind() datamodel.Kind {
return datamodel.Kind_Bytes
}
func (x Bytes) LookupByString(string) (datamodel.Node, error) {
return nil, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "LookupByString", AppropriateKind: datamodel.KindSet_JustMap, ActualKind: datamodel.Kind_Bytes}
}
func (x Bytes) LookupByNode(key datamodel.Node) (datamodel.Node, error) {
return nil, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "LookupByNode", AppropriateKind: datamodel.KindSet_JustMap, ActualKind: datamodel.Kind_Bytes}
}
func (x Bytes) LookupByIndex(idx int64) (datamodel.Node, error) {
return nil, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "LookupByIndex", AppropriateKind: datamodel.KindSet_JustList, ActualKind: datamodel.Kind_Bytes}
}
func (x Bytes) LookupBySegment(datamodel.PathSegment) (datamodel.Node, error) {
return nil, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "LookupBySegment", AppropriateKind: datamodel.KindSet_Recursive, ActualKind: datamodel.Kind_Bytes}
}
func (Bytes) MapIterator() datamodel.MapIterator {
return nil
}
func (Bytes) ListIterator() datamodel.ListIterator {
return nil
}
func (Bytes) Length() int64 {
return -1
}
func (Bytes) IsAbsent() bool {
return false
}
func (Bytes) IsNull() bool {
return false
}
func (x Bytes) AsBool() (bool, error) {
return false, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AsBool", AppropriateKind: datamodel.KindSet_JustBool, ActualKind: datamodel.Kind_Bytes}
}
func (x Bytes) AsInt() (int64, error) {
return 0, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AsInt", AppropriateKind: datamodel.KindSet_JustInt, ActualKind: datamodel.Kind_Bytes}
}
func (x Bytes) AsFloat() (float64, error) {
return 0, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AsFloat", AppropriateKind: datamodel.KindSet_JustFloat, ActualKind: datamodel.Kind_Bytes}
}
func (x Bytes) AsString() (string, error) {
return "", datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AsString", AppropriateKind: datamodel.KindSet_JustString, ActualKind: datamodel.Kind_Bytes}
}
func (x Bytes) AsLink() (datamodel.Link, error) {
return nil, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AsLink", AppropriateKind: datamodel.KindSet_JustLink, ActualKind: datamodel.Kind_Bytes}
}
// BytesAssembler has similar purpose as Bytes, but for (you guessed it)
// the NodeAssembler interface rather than the Node interface.
type BytesAssembler struct {
TypeName string
}
func (x BytesAssembler) BeginMap(sizeHint int64) (datamodel.MapAssembler, error) {
return nil, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "BeginMap", AppropriateKind: datamodel.KindSet_JustMap, ActualKind: datamodel.Kind_Bytes}
}
func (x BytesAssembler) BeginList(sizeHint int64) (datamodel.ListAssembler, error) {
return nil, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "BeginList", AppropriateKind: datamodel.KindSet_JustList, ActualKind: datamodel.Kind_Bytes}
}
func (x BytesAssembler) AssignNull() error {
return datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AssignNull", AppropriateKind: datamodel.KindSet_JustNull, ActualKind: datamodel.Kind_Bytes}
}
func (x BytesAssembler) AssignBool(bool) error {
return datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AssignBool", AppropriateKind: datamodel.KindSet_JustBool, ActualKind: datamodel.Kind_Bytes}
}
func (x BytesAssembler) AssignInt(int64) error {
return datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AssignInt", AppropriateKind: datamodel.KindSet_JustInt, ActualKind: datamodel.Kind_Bytes}
}
func (x BytesAssembler) AssignFloat(float64) error {
return datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AssignFloat", AppropriateKind: datamodel.KindSet_JustFloat, ActualKind: datamodel.Kind_Bytes}
}
func (x BytesAssembler) AssignString(string) error {
return datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AssignString", AppropriateKind: datamodel.KindSet_JustString, ActualKind: datamodel.Kind_Bytes}
}
func (x BytesAssembler) AssignLink(datamodel.Link) error {
return datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AssignLink", AppropriateKind: datamodel.KindSet_JustLink, ActualKind: datamodel.Kind_Bytes}
}

View File

@@ -0,0 +1,40 @@
package mixins
// This file is a little different than most of its siblings in this package.
// It's not really much of a "mixin". More of a util function junkdrawer.
//
// Implementations of Data Model Nodes are unlikely to need these.
// Implementations of Schema-level Node *are* likely to need these, however.
//
// Our codegen implementation emits calls to these functions.
// (And having these functions in a package that's already an unconditional
// import in files emitted by codegen makes the codegen significantly simpler.)
import (
"fmt"
"strings"
)
// SplitExact is much like strings.Split but will error if the number of
// substrings is other than the expected count.
//
// SplitExact is used by the 'stringjoin' representation for structs.
//
// The 'count' parameter is a length. In other words, if you expect
// the zero'th index to be present in the result, you should ask for
// a count of at least '1'.
// Using this function with 'count' less than 2 is rather strange.
func SplitExact(s string, sep string, count int) ([]string, error) {
ss := strings.Split(s, sep)
if len(ss) != count {
return nil, fmt.Errorf("expected %d instances of the delimiter, found %d", count-1, len(ss)-1)
}
return ss, nil
}
// SplitN is an alias of strings.SplitN, which is only present here to
// make it usable in codegen packages without requiring conditional imports
// in the generation process.
func SplitN(s, sep string, n int) []string {
return strings.SplitN(s, sep, n)
}

View File

@@ -0,0 +1,97 @@
package mixins
import (
"github.com/ipld/go-ipld-prime/datamodel"
)
// Float can be embedded in a struct to provide all the methods that
// have fixed output for any int-kinded nodes.
// (Mostly this includes all the methods which simply return ErrWrongKind.)
// Other methods will still need to be implemented to finish conforming to Node.
//
// To conserve memory and get a TypeName in errors without embedding,
// write methods on your type with a body that simply initializes this struct
// and immediately uses the relevant method;
// this is more verbose in source, but compiles to a tighter result:
// in memory, there's no embed; and in runtime, the calls will be inlined
// and thus have no cost in execution time.
type Float struct {
TypeName string
}
func (Float) Kind() datamodel.Kind {
return datamodel.Kind_Float
}
func (x Float) LookupByString(string) (datamodel.Node, error) {
return nil, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "LookupByString", AppropriateKind: datamodel.KindSet_JustMap, ActualKind: datamodel.Kind_Float}
}
func (x Float) LookupByNode(key datamodel.Node) (datamodel.Node, error) {
return nil, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "LookupByNode", AppropriateKind: datamodel.KindSet_JustMap, ActualKind: datamodel.Kind_Float}
}
func (x Float) LookupByIndex(idx int64) (datamodel.Node, error) {
return nil, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "LookupByIndex", AppropriateKind: datamodel.KindSet_JustList, ActualKind: datamodel.Kind_Float}
}
func (x Float) LookupBySegment(datamodel.PathSegment) (datamodel.Node, error) {
return nil, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "LookupBySegment", AppropriateKind: datamodel.KindSet_Recursive, ActualKind: datamodel.Kind_Float}
}
func (Float) MapIterator() datamodel.MapIterator {
return nil
}
func (Float) ListIterator() datamodel.ListIterator {
return nil
}
func (Float) Length() int64 {
return -1
}
func (Float) IsAbsent() bool {
return false
}
func (Float) IsNull() bool {
return false
}
func (x Float) AsBool() (bool, error) {
return false, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AsBool", AppropriateKind: datamodel.KindSet_JustBool, ActualKind: datamodel.Kind_Float}
}
func (x Float) AsInt() (int64, error) {
return 0, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AsInt", AppropriateKind: datamodel.KindSet_JustInt, ActualKind: datamodel.Kind_Float}
}
func (x Float) AsString() (string, error) {
return "", datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AsString", AppropriateKind: datamodel.KindSet_JustString, ActualKind: datamodel.Kind_Float}
}
func (x Float) AsBytes() ([]byte, error) {
return nil, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AsBytes", AppropriateKind: datamodel.KindSet_JustBytes, ActualKind: datamodel.Kind_Float}
}
func (x Float) AsLink() (datamodel.Link, error) {
return nil, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AsLink", AppropriateKind: datamodel.KindSet_JustLink, ActualKind: datamodel.Kind_Float}
}
// FloatAssembler has similar purpose as Float, but for (you guessed it)
// the NodeAssembler interface rather than the Node interface.
type FloatAssembler struct {
TypeName string
}
func (x FloatAssembler) BeginMap(sizeHint int64) (datamodel.MapAssembler, error) {
return nil, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "BeginMap", AppropriateKind: datamodel.KindSet_JustMap, ActualKind: datamodel.Kind_Float}
}
func (x FloatAssembler) BeginList(sizeHint int64) (datamodel.ListAssembler, error) {
return nil, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "BeginList", AppropriateKind: datamodel.KindSet_JustList, ActualKind: datamodel.Kind_Float}
}
func (x FloatAssembler) AssignNull() error {
return datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AssignNull", AppropriateKind: datamodel.KindSet_JustNull, ActualKind: datamodel.Kind_Float}
}
func (x FloatAssembler) AssignBool(bool) error {
return datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AssignBool", AppropriateKind: datamodel.KindSet_JustBool, ActualKind: datamodel.Kind_Float}
}
func (x FloatAssembler) AssignInt(int64) error {
return datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AssignInt", AppropriateKind: datamodel.KindSet_JustInt, ActualKind: datamodel.Kind_Float}
}
func (x FloatAssembler) AssignString(string) error {
return datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AssignString", AppropriateKind: datamodel.KindSet_JustString, ActualKind: datamodel.Kind_Float}
}
func (x FloatAssembler) AssignBytes([]byte) error {
return datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AssignBytes", AppropriateKind: datamodel.KindSet_JustBytes, ActualKind: datamodel.Kind_Float}
}
func (x FloatAssembler) AssignLink(datamodel.Link) error {
return datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AssignLink", AppropriateKind: datamodel.KindSet_JustLink, ActualKind: datamodel.Kind_Float}
}

View File

@@ -0,0 +1,97 @@
package mixins
import (
"github.com/ipld/go-ipld-prime/datamodel"
)
// Int can be embedded in a struct to provide all the methods that
// have fixed output for any int-kinded nodes.
// (Mostly this includes all the methods which simply return ErrWrongKind.)
// Other methods will still need to be implemented to finish conforming to Node.
//
// To conserve memory and get a TypeName in errors without embedding,
// write methods on your type with a body that simply initializes this struct
// and immediately uses the relevant method;
// this is more verbose in source, but compiles to a tighter result:
// in memory, there's no embed; and in runtime, the calls will be inlined
// and thus have no cost in execution time.
type Int struct {
TypeName string
}
func (Int) Kind() datamodel.Kind {
return datamodel.Kind_Int
}
func (x Int) LookupByString(string) (datamodel.Node, error) {
return nil, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "LookupByString", AppropriateKind: datamodel.KindSet_JustMap, ActualKind: datamodel.Kind_Int}
}
func (x Int) LookupByNode(key datamodel.Node) (datamodel.Node, error) {
return nil, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "LookupByNode", AppropriateKind: datamodel.KindSet_JustMap, ActualKind: datamodel.Kind_Int}
}
func (x Int) LookupByIndex(idx int64) (datamodel.Node, error) {
return nil, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "LookupByIndex", AppropriateKind: datamodel.KindSet_JustList, ActualKind: datamodel.Kind_Int}
}
func (x Int) LookupBySegment(datamodel.PathSegment) (datamodel.Node, error) {
return nil, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "LookupBySegment", AppropriateKind: datamodel.KindSet_Recursive, ActualKind: datamodel.Kind_Int}
}
func (Int) MapIterator() datamodel.MapIterator {
return nil
}
func (Int) ListIterator() datamodel.ListIterator {
return nil
}
func (Int) Length() int64 {
return -1
}
func (Int) IsAbsent() bool {
return false
}
func (Int) IsNull() bool {
return false
}
func (x Int) AsBool() (bool, error) {
return false, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AsBool", AppropriateKind: datamodel.KindSet_JustBool, ActualKind: datamodel.Kind_Int}
}
func (x Int) AsFloat() (float64, error) {
return 0, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AsFloat", AppropriateKind: datamodel.KindSet_JustFloat, ActualKind: datamodel.Kind_Int}
}
func (x Int) AsString() (string, error) {
return "", datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AsString", AppropriateKind: datamodel.KindSet_JustString, ActualKind: datamodel.Kind_Int}
}
func (x Int) AsBytes() ([]byte, error) {
return nil, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AsBytes", AppropriateKind: datamodel.KindSet_JustBytes, ActualKind: datamodel.Kind_Int}
}
func (x Int) AsLink() (datamodel.Link, error) {
return nil, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AsLink", AppropriateKind: datamodel.KindSet_JustLink, ActualKind: datamodel.Kind_Int}
}
// IntAssembler has similar purpose as Int, but for (you guessed it)
// the NodeAssembler interface rather than the Node interface.
type IntAssembler struct {
TypeName string
}
func (x IntAssembler) BeginMap(sizeHint int64) (datamodel.MapAssembler, error) {
return nil, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "BeginMap", AppropriateKind: datamodel.KindSet_JustMap, ActualKind: datamodel.Kind_Int}
}
func (x IntAssembler) BeginList(sizeHint int64) (datamodel.ListAssembler, error) {
return nil, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "BeginList", AppropriateKind: datamodel.KindSet_JustList, ActualKind: datamodel.Kind_Int}
}
func (x IntAssembler) AssignNull() error {
return datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AssignNull", AppropriateKind: datamodel.KindSet_JustNull, ActualKind: datamodel.Kind_Int}
}
func (x IntAssembler) AssignBool(bool) error {
return datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AssignBool", AppropriateKind: datamodel.KindSet_JustBool, ActualKind: datamodel.Kind_Int}
}
func (x IntAssembler) AssignFloat(float64) error {
return datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AssignFloat", AppropriateKind: datamodel.KindSet_JustFloat, ActualKind: datamodel.Kind_Int}
}
func (x IntAssembler) AssignString(string) error {
return datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AssignString", AppropriateKind: datamodel.KindSet_JustString, ActualKind: datamodel.Kind_Int}
}
func (x IntAssembler) AssignBytes([]byte) error {
return datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AssignBytes", AppropriateKind: datamodel.KindSet_JustBytes, ActualKind: datamodel.Kind_Int}
}
func (x IntAssembler) AssignLink(datamodel.Link) error {
return datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AssignLink", AppropriateKind: datamodel.KindSet_JustLink, ActualKind: datamodel.Kind_Int}
}

View File

@@ -0,0 +1,97 @@
package mixins
import (
"github.com/ipld/go-ipld-prime/datamodel"
)
// Link can be embedded in a struct to provide all the methods that
// have fixed output for any int-kinded nodes.
// (Mostly this includes all the methods which simply return ErrWrongKind.)
// Other methods will still need to be implemented to finish conforming to Node.
//
// To conserve memory and get a TypeName in errors without embedding,
// write methods on your type with a body that simply initializes this struct
// and immediately uses the relevant method;
// this is more verbose in source, but compiles to a tighter result:
// in memory, there's no embed; and in runtime, the calls will be inlined
// and thus have no cost in execution time.
type Link struct {
TypeName string
}
func (Link) Kind() datamodel.Kind {
return datamodel.Kind_Link
}
func (x Link) LookupByString(string) (datamodel.Node, error) {
return nil, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "LookupByString", AppropriateKind: datamodel.KindSet_JustMap, ActualKind: datamodel.Kind_Link}
}
func (x Link) LookupByNode(key datamodel.Node) (datamodel.Node, error) {
return nil, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "LookupByNode", AppropriateKind: datamodel.KindSet_JustMap, ActualKind: datamodel.Kind_Link}
}
func (x Link) LookupByIndex(idx int64) (datamodel.Node, error) {
return nil, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "LookupByIndex", AppropriateKind: datamodel.KindSet_JustList, ActualKind: datamodel.Kind_Link}
}
func (x Link) LookupBySegment(datamodel.PathSegment) (datamodel.Node, error) {
return nil, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "LookupBySegment", AppropriateKind: datamodel.KindSet_Recursive, ActualKind: datamodel.Kind_Link}
}
func (Link) MapIterator() datamodel.MapIterator {
return nil
}
func (Link) ListIterator() datamodel.ListIterator {
return nil
}
func (Link) Length() int64 {
return -1
}
func (Link) IsAbsent() bool {
return false
}
func (Link) IsNull() bool {
return false
}
func (x Link) AsBool() (bool, error) {
return false, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AsBool", AppropriateKind: datamodel.KindSet_JustBool, ActualKind: datamodel.Kind_Link}
}
func (x Link) AsInt() (int64, error) {
return 0, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AsInt", AppropriateKind: datamodel.KindSet_JustInt, ActualKind: datamodel.Kind_Link}
}
func (x Link) AsFloat() (float64, error) {
return 0, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AsFloat", AppropriateKind: datamodel.KindSet_JustFloat, ActualKind: datamodel.Kind_Link}
}
func (x Link) AsString() (string, error) {
return "", datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AsString", AppropriateKind: datamodel.KindSet_JustString, ActualKind: datamodel.Kind_Link}
}
func (x Link) AsBytes() ([]byte, error) {
return nil, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AsBytes", AppropriateKind: datamodel.KindSet_JustBytes, ActualKind: datamodel.Kind_Link}
}
// LinkAssembler has similar purpose as Link, but for (you guessed it)
// the NodeAssembler interface rather than the Node interface.
type LinkAssembler struct {
TypeName string
}
func (x LinkAssembler) BeginMap(sizeHint int64) (datamodel.MapAssembler, error) {
return nil, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "BeginMap", AppropriateKind: datamodel.KindSet_JustMap, ActualKind: datamodel.Kind_Link}
}
func (x LinkAssembler) BeginList(sizeHint int64) (datamodel.ListAssembler, error) {
return nil, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "BeginList", AppropriateKind: datamodel.KindSet_JustList, ActualKind: datamodel.Kind_Link}
}
func (x LinkAssembler) AssignNull() error {
return datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AssignNull", AppropriateKind: datamodel.KindSet_JustNull, ActualKind: datamodel.Kind_Link}
}
func (x LinkAssembler) AssignBool(bool) error {
return datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AssignBool", AppropriateKind: datamodel.KindSet_JustBool, ActualKind: datamodel.Kind_Link}
}
func (x LinkAssembler) AssignInt(int64) error {
return datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AssignInt", AppropriateKind: datamodel.KindSet_JustInt, ActualKind: datamodel.Kind_Link}
}
func (x LinkAssembler) AssignFloat(float64) error {
return datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AssignFloat", AppropriateKind: datamodel.KindSet_JustFloat, ActualKind: datamodel.Kind_Link}
}
func (x LinkAssembler) AssignString(string) error {
return datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AssignString", AppropriateKind: datamodel.KindSet_JustString, ActualKind: datamodel.Kind_Link}
}
func (x LinkAssembler) AssignBytes([]byte) error {
return datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AssignBytes", AppropriateKind: datamodel.KindSet_JustBytes, ActualKind: datamodel.Kind_Link}
}

View File

@@ -0,0 +1,88 @@
package mixins
import (
"github.com/ipld/go-ipld-prime/datamodel"
)
// List can be embedded in a struct to provide all the methods that
// have fixed output for any int-kinded nodes.
// (Mostly this includes all the methods which simply return ErrWrongKind.)
// Other methods will still need to be implemented to finish conforming to Node.
//
// To conserve memory and get a TypeName in errors without embedding,
// write methods on your type with a body that simply initializes this struct
// and immediately uses the relevant method;
// this is more verbose in source, but compiles to a tighter result:
// in memory, there's no embed; and in runtime, the calls will be inlined
// and thus have no cost in execution time.
type List struct {
TypeName string
}
func (List) Kind() datamodel.Kind {
return datamodel.Kind_List
}
func (x List) LookupByString(string) (datamodel.Node, error) {
return nil, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "LookupByString", AppropriateKind: datamodel.KindSet_JustMap, ActualKind: datamodel.Kind_List}
}
func (x List) LookupByNode(key datamodel.Node) (datamodel.Node, error) {
return nil, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "LookupByNode", AppropriateKind: datamodel.KindSet_JustMap, ActualKind: datamodel.Kind_List}
}
func (List) MapIterator() datamodel.MapIterator {
return nil
}
func (List) IsAbsent() bool {
return false
}
func (List) IsNull() bool {
return false
}
func (x List) AsBool() (bool, error) {
return false, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AsBool", AppropriateKind: datamodel.KindSet_JustBool, ActualKind: datamodel.Kind_List}
}
func (x List) AsInt() (int64, error) {
return 0, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AsInt", AppropriateKind: datamodel.KindSet_JustInt, ActualKind: datamodel.Kind_List}
}
func (x List) AsFloat() (float64, error) {
return 0, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AsFloat", AppropriateKind: datamodel.KindSet_JustFloat, ActualKind: datamodel.Kind_List}
}
func (x List) AsString() (string, error) {
return "", datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AsString", AppropriateKind: datamodel.KindSet_JustString, ActualKind: datamodel.Kind_List}
}
func (x List) AsBytes() ([]byte, error) {
return nil, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AsBytes", AppropriateKind: datamodel.KindSet_JustBytes, ActualKind: datamodel.Kind_List}
}
func (x List) AsLink() (datamodel.Link, error) {
return nil, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AsLink", AppropriateKind: datamodel.KindSet_JustLink, ActualKind: datamodel.Kind_List}
}
// ListAssembler has similar purpose as List, but for (you guessed it)
// the NodeAssembler interface rather than the Node interface.
type ListAssembler struct {
TypeName string
}
func (x ListAssembler) BeginMap(sizeHint int64) (datamodel.MapAssembler, error) {
return nil, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "BeginMap", AppropriateKind: datamodel.KindSet_JustMap, ActualKind: datamodel.Kind_List}
}
func (x ListAssembler) AssignNull() error {
return datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AssignNull", AppropriateKind: datamodel.KindSet_JustNull, ActualKind: datamodel.Kind_List}
}
func (x ListAssembler) AssignBool(bool) error {
return datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AssignBool", AppropriateKind: datamodel.KindSet_JustBool, ActualKind: datamodel.Kind_List}
}
func (x ListAssembler) AssignInt(int64) error {
return datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AssignInt", AppropriateKind: datamodel.KindSet_JustInt, ActualKind: datamodel.Kind_List}
}
func (x ListAssembler) AssignFloat(float64) error {
return datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AssignFloat", AppropriateKind: datamodel.KindSet_JustFloat, ActualKind: datamodel.Kind_List}
}
func (x ListAssembler) AssignString(string) error {
return datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AssignString", AppropriateKind: datamodel.KindSet_JustString, ActualKind: datamodel.Kind_List}
}
func (x ListAssembler) AssignBytes([]byte) error {
return datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AssignBytes", AppropriateKind: datamodel.KindSet_JustBytes, ActualKind: datamodel.Kind_List}
}
func (x ListAssembler) AssignLink(datamodel.Link) error {
return datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AssignLink", AppropriateKind: datamodel.KindSet_JustLink, ActualKind: datamodel.Kind_List}
}

View File

@@ -0,0 +1,85 @@
package mixins
import (
"github.com/ipld/go-ipld-prime/datamodel"
)
// Map can be embedded in a struct to provide all the methods that
// have fixed output for any map-kinded nodes.
// (Mostly this includes all the methods which simply return ErrWrongKind.)
// Other methods will still need to be implemented to finish conforming to Node.
//
// To conserve memory and get a TypeName in errors without embedding,
// write methods on your type with a body that simply initializes this struct
// and immediately uses the relevant method;
// this is more verbose in source, but compiles to a tighter result:
// in memory, there's no embed; and in runtime, the calls will be inlined
// and thus have no cost in execution time.
type Map struct {
TypeName string
}
func (Map) Kind() datamodel.Kind {
return datamodel.Kind_Map
}
func (x Map) LookupByIndex(idx int64) (datamodel.Node, error) {
return nil, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "LookupByIndex", AppropriateKind: datamodel.KindSet_JustList, ActualKind: datamodel.Kind_Map}
}
func (Map) ListIterator() datamodel.ListIterator {
return nil
}
func (Map) IsAbsent() bool {
return false
}
func (Map) IsNull() bool {
return false
}
func (x Map) AsBool() (bool, error) {
return false, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AsBool", AppropriateKind: datamodel.KindSet_JustBool, ActualKind: datamodel.Kind_Map}
}
func (x Map) AsInt() (int64, error) {
return 0, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AsInt", AppropriateKind: datamodel.KindSet_JustInt, ActualKind: datamodel.Kind_Map}
}
func (x Map) AsFloat() (float64, error) {
return 0, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AsFloat", AppropriateKind: datamodel.KindSet_JustFloat, ActualKind: datamodel.Kind_Map}
}
func (x Map) AsString() (string, error) {
return "", datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AsString", AppropriateKind: datamodel.KindSet_JustString, ActualKind: datamodel.Kind_Map}
}
func (x Map) AsBytes() ([]byte, error) {
return nil, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AsBytes", AppropriateKind: datamodel.KindSet_JustBytes, ActualKind: datamodel.Kind_Map}
}
func (x Map) AsLink() (datamodel.Link, error) {
return nil, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AsLink", AppropriateKind: datamodel.KindSet_JustLink, ActualKind: datamodel.Kind_Map}
}
// MapAssembler has similar purpose as Map, but for (you guessed it)
// the NodeAssembler interface rather than the Node interface.
type MapAssembler struct {
TypeName string
}
func (x MapAssembler) BeginList(sizeHint int64) (datamodel.ListAssembler, error) {
return nil, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "BeginList", AppropriateKind: datamodel.KindSet_JustList, ActualKind: datamodel.Kind_Map}
}
func (x MapAssembler) AssignNull() error {
return datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AssignNull", AppropriateKind: datamodel.KindSet_JustNull, ActualKind: datamodel.Kind_Map}
}
func (x MapAssembler) AssignBool(bool) error {
return datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AssignBool", AppropriateKind: datamodel.KindSet_JustBool, ActualKind: datamodel.Kind_Map}
}
func (x MapAssembler) AssignInt(int64) error {
return datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AssignInt", AppropriateKind: datamodel.KindSet_JustInt, ActualKind: datamodel.Kind_Map}
}
func (x MapAssembler) AssignFloat(float64) error {
return datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AssignFloat", AppropriateKind: datamodel.KindSet_JustFloat, ActualKind: datamodel.Kind_Map}
}
func (x MapAssembler) AssignString(string) error {
return datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AssignString", AppropriateKind: datamodel.KindSet_JustString, ActualKind: datamodel.Kind_Map}
}
func (x MapAssembler) AssignBytes([]byte) error {
return datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AssignBytes", AppropriateKind: datamodel.KindSet_JustBytes, ActualKind: datamodel.Kind_Map}
}
func (x MapAssembler) AssignLink(datamodel.Link) error {
return datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AssignLink", AppropriateKind: datamodel.KindSet_JustLink, ActualKind: datamodel.Kind_Map}
}

View File

@@ -0,0 +1,97 @@
package mixins
import (
"github.com/ipld/go-ipld-prime/datamodel"
)
// String can be embedded in a struct to provide all the methods that
// have fixed output for any string-kinded nodes.
// (Mostly this includes all the methods which simply return ErrWrongKind.)
// Other methods will still need to be implemented to finish conforming to Node.
//
// To conserve memory and get a TypeName in errors without embedding,
// write methods on your type with a body that simply initializes this struct
// and immediately uses the relevant method;
// this is more verbose in source, but compiles to a tighter result:
// in memory, there's no embed; and in runtime, the calls will be inlined
// and thus have no cost in execution time.
type String struct {
TypeName string
}
func (String) Kind() datamodel.Kind {
return datamodel.Kind_String
}
func (x String) LookupByString(string) (datamodel.Node, error) {
return nil, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "LookupByString", AppropriateKind: datamodel.KindSet_JustMap, ActualKind: datamodel.Kind_String}
}
func (x String) LookupByNode(key datamodel.Node) (datamodel.Node, error) {
return nil, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "LookupByNode", AppropriateKind: datamodel.KindSet_JustMap, ActualKind: datamodel.Kind_String}
}
func (x String) LookupByIndex(idx int64) (datamodel.Node, error) {
return nil, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "LookupByIndex", AppropriateKind: datamodel.KindSet_JustList, ActualKind: datamodel.Kind_String}
}
func (x String) LookupBySegment(datamodel.PathSegment) (datamodel.Node, error) {
return nil, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "LookupBySegment", AppropriateKind: datamodel.KindSet_Recursive, ActualKind: datamodel.Kind_String}
}
func (String) MapIterator() datamodel.MapIterator {
return nil
}
func (String) ListIterator() datamodel.ListIterator {
return nil
}
func (String) Length() int64 {
return -1
}
func (String) IsAbsent() bool {
return false
}
func (String) IsNull() bool {
return false
}
func (x String) AsBool() (bool, error) {
return false, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AsBool", AppropriateKind: datamodel.KindSet_JustBool, ActualKind: datamodel.Kind_String}
}
func (x String) AsInt() (int64, error) {
return 0, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AsInt", AppropriateKind: datamodel.KindSet_JustInt, ActualKind: datamodel.Kind_String}
}
func (x String) AsFloat() (float64, error) {
return 0, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AsFloat", AppropriateKind: datamodel.KindSet_JustFloat, ActualKind: datamodel.Kind_String}
}
func (x String) AsBytes() ([]byte, error) {
return nil, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AsBytes", AppropriateKind: datamodel.KindSet_JustBytes, ActualKind: datamodel.Kind_String}
}
func (x String) AsLink() (datamodel.Link, error) {
return nil, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AsLink", AppropriateKind: datamodel.KindSet_JustLink, ActualKind: datamodel.Kind_String}
}
// StringAssembler has similar purpose as String, but for (you guessed it)
// the NodeAssembler interface rather than the Node interface.
type StringAssembler struct {
TypeName string
}
func (x StringAssembler) BeginMap(sizeHint int64) (datamodel.MapAssembler, error) {
return nil, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "BeginMap", AppropriateKind: datamodel.KindSet_JustMap, ActualKind: datamodel.Kind_String}
}
func (x StringAssembler) BeginList(sizeHint int64) (datamodel.ListAssembler, error) {
return nil, datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "BeginList", AppropriateKind: datamodel.KindSet_JustList, ActualKind: datamodel.Kind_String}
}
func (x StringAssembler) AssignNull() error {
return datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AssignNull", AppropriateKind: datamodel.KindSet_JustNull, ActualKind: datamodel.Kind_String}
}
func (x StringAssembler) AssignBool(bool) error {
return datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AssignBool", AppropriateKind: datamodel.KindSet_JustBool, ActualKind: datamodel.Kind_String}
}
func (x StringAssembler) AssignInt(int64) error {
return datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AssignInt", AppropriateKind: datamodel.KindSet_JustInt, ActualKind: datamodel.Kind_String}
}
func (x StringAssembler) AssignFloat(float64) error {
return datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AssignFloat", AppropriateKind: datamodel.KindSet_JustFloat, ActualKind: datamodel.Kind_String}
}
func (x StringAssembler) AssignBytes([]byte) error {
return datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AssignBytes", AppropriateKind: datamodel.KindSet_JustBytes, ActualKind: datamodel.Kind_String}
}
func (x StringAssembler) AssignLink(datamodel.Link) error {
return datamodel.ErrWrongKind{TypeName: x.TypeName, MethodName: "AssignLink", AppropriateKind: datamodel.KindSet_JustLink, ActualKind: datamodel.Kind_String}
}

View File

@@ -0,0 +1,107 @@
// copy this and remove methods that aren't relevant to your kind.
// this has not been scripted.
// (the first part is trivial; the second part is not; and this updates rarely. https://xkcd.com/1205/ applies.)
package mixins
import (
"github.com/ipld/go-ipld-prime/datamodel"
)
// @Kind@ can be embedded in a struct to provide all the methods that
// have fixed output for any int-kinded nodes.
// (Mostly this includes all the methods which simply return ErrWrongKind.)
// Other methods will still need to be implemented to finish conforming to Node.
//
// To conserve memory and get a TypeName in errors without embedding,
// write methods on your type with a body that simply initializes this struct
// and immediately uses the relevant method;
// this is more verbose in source, but compiles to a tighter result:
// in memory, there's no embed; and in runtime, the calls will be inlined
// and thus have no cost in execution time.
type @Kind@ struct {
TypeName string
}
func (@Kind@) Kind() ipld.Kind {
return ipld.Kind_@Kind@
}
func (x @Kind@) LookupByString(string) (ipld.Node, error) {
return nil, ipld.ErrWrongKind{TypeName: x.TypeName, MethodName: "LookupByString", AppropriateKind: ipld.KindSet_JustMap, ActualKind: ipld.Kind_@Kind@}
}
func (x @Kind@) LookupByNode(key ipld.Node) (ipld.Node, error) {
return nil, ipld.ErrWrongKind{TypeName: x.TypeName, MethodName: "LookupByNode", AppropriateKind: ipld.KindSet_JustMap, ActualKind: ipld.Kind_@Kind@}
}
func (x @Kind@) LookupByIndex(idx int) (ipld.Node, error) {
return nil, ipld.ErrWrongKind{TypeName: x.TypeName, MethodName: "LookupByIndex", AppropriateKind: ipld.KindSet_JustList, ActualKind: ipld.Kind_@Kind@}
}
func (x @Kind@) LookupBySegment(ipld.PathSegment) (ipld.Node, error) {
return nil, ipld.ErrWrongKind{TypeName: x.TypeName, MethodName: "LookupBySegment", AppropriateKind: ipld.KindSet_Recursive, ActualKind: ipld.Kind_@Kind@}
}
func (@Kind@) MapIterator() ipld.MapIterator {
return nil
}
func (@Kind@) ListIterator() ipld.ListIterator {
return nil
}
func (@Kind@) Length() int {
return -1
}
func (@Kind@) IsAbsent() bool {
return false
}
func (@Kind@) IsNull() bool {
return false
}
func (x @Kind@) AsBool() (bool, error) {
return false, ipld.ErrWrongKind{TypeName: x.TypeName, MethodName: "AsBool", AppropriateKind: ipld.KindSet_JustBool, ActualKind: ipld.Kind_@Kind@}
}
func (x @Kind@) AsInt() (int, error) {
return 0, ipld.ErrWrongKind{TypeName: x.TypeName, MethodName: "AsInt", AppropriateKind: ipld.KindSet_JustInt, ActualKind: ipld.Kind_@Kind@}
}
func (x @Kind@) AsFloat() (float64, error) {
return 0, ipld.ErrWrongKind{TypeName: x.TypeName, MethodName: "AsFloat", AppropriateKind: ipld.KindSet_JustFloat, ActualKind: ipld.Kind_@Kind@}
}
func (x @Kind@) AsString() (string, error) {
return "", ipld.ErrWrongKind{TypeName: x.TypeName, MethodName: "AsString", AppropriateKind: ipld.KindSet_JustString, ActualKind: ipld.Kind_@Kind@}
}
func (x @Kind@) AsBytes() ([]byte, error) {
return nil, ipld.ErrWrongKind{TypeName: x.TypeName, MethodName: "AsBytes", AppropriateKind: ipld.KindSet_JustBytes, ActualKind: ipld.Kind_@Kind@}
}
func (x @Kind@) AsLink() (ipld.Link, error) {
return nil, ipld.ErrWrongKind{TypeName: x.TypeName, MethodName: "AsLink", AppropriateKind: ipld.KindSet_JustLink, ActualKind: ipld.Kind_@Kind@}
}
// @Kind@Assembler has similar purpose as @Kind@, but for (you guessed it)
// the NodeAssembler interface rather than the Node interface.
type @Kind@Assembler struct {
TypeName string
}
func (x @Kind@Assembler) BeginMap(sizeHint int) (ipld.MapAssembler, error) {
return nil, ipld.ErrWrongKind{TypeName: x.TypeName, MethodName: "BeginMap", AppropriateKind: ipld.KindSet_JustMap, ActualKind: ipld.Kind_@Kind@}
}
func (x @Kind@Assembler) BeginList(sizeHint int) (ipld.ListAssembler, error) {
return nil, ipld.ErrWrongKind{TypeName: x.TypeName, MethodName: "BeginList", AppropriateKind: ipld.KindSet_JustList, ActualKind: ipld.Kind_@Kind@}
}
func (x @Kind@Assembler) AssignNull() error {
return ipld.ErrWrongKind{TypeName: x.TypeName, MethodName: "AssignNull", AppropriateKind: ipld.KindSet_JustNull, ActualKind: ipld.Kind_@Kind@}
}
func (x @Kind@Assembler) AssignBool(bool) error {
return ipld.ErrWrongKind{TypeName: x.TypeName, MethodName: "AssignBool", AppropriateKind: ipld.KindSet_JustBool, ActualKind: ipld.Kind_@Kind@}
}
func (x @Kind@Assembler) AssignInt(int) error {
return ipld.ErrWrongKind{TypeName: x.TypeName, MethodName: "AssignInt", AppropriateKind: ipld.KindSet_JustInt, ActualKind: ipld.Kind_@Kind@}
}
func (x @Kind@Assembler) AssignFloat(float64) error {
return ipld.ErrWrongKind{TypeName: x.TypeName, MethodName: "AssignFloat", AppropriateKind: ipld.KindSet_JustFloat, ActualKind: ipld.Kind_@Kind@}
}
func (x @Kind@Assembler) AssignString(string) error {
return ipld.ErrWrongKind{TypeName: x.TypeName, MethodName: "AssignString", AppropriateKind: ipld.KindSet_JustString, ActualKind: ipld.Kind_@Kind@}
}
func (x @Kind@Assembler) AssignBytes([]byte) error {
return ipld.ErrWrongKind{TypeName: x.TypeName, MethodName: "AssignBytes", AppropriateKind: ipld.KindSet_JustBytes, ActualKind: ipld.Kind_@Kind@}
}
func (x @Kind@Assembler) AssignLink(ipld.Link) error {
return ipld.ErrWrongKind{TypeName: x.TypeName, MethodName: "AssignLink", AppropriateKind: ipld.KindSet_JustLink, ActualKind: ipld.Kind_@Kind@}
}