From a2ea47066c77221f19a1add49ac8255a84e04e79 Mon Sep 17 00:00:00 2001 From: Eric Myhre Date: Thu, 27 Feb 2020 15:00:32 +0100 Subject: [PATCH] Yank TypedNode interface into schema package. Previously it was in the 'impl/typed' package, next to the runtime-wrapper implementation of the interface. This was strange. Not only should those two things be separated just on principle, this was also causing more import cycle problems down the road: for example, the traversal package needs to consider the *interface* for a schema-typed node in order to gracefully handle some features... and if this also brings in a *concrete* dependency on the runtime-wrapper implementation of typed nodes, not only is that incorrect bloat, it becomes a show stopper because (currently, at least) that implementation also in turn transitively imports the ipldfree package for some of its scalars. Ouchouch. So. Now the interface lives over in the 'schema' package, with all the other interfaces for that feature set. Where it probably always should have been. ('typed.Maybe' also became known as 'schema.Maybe', which... does not roll off the tongue as nicely. But this is a minor concern and we might reconsider the naming and appearance of that thing later anyway.) --- README.md | 4 +- _rsrch/nodesolution/codec/unmarshal.go | 2 +- _rsrch/nodesolution/errors.go | 2 +- _rsrch/nodesolution/node.go | 6 +-- doc/dev/node-implementations.md | 6 +-- doc/schema.md | 2 +- encoding/dagcbor/unmarshal.go | 2 +- encoding/dagjson/unmarshal.go | 2 +- encoding/unmarshal.go | 2 +- errors.go | 4 +- fluent/fluentNodeBuilder.go | 8 ++-- impl/typed/typedLinkNode.go | 2 +- impl/typed/typedNode.go | 66 -------------------------- impl/typed/wrapStruct.go | 12 ++--- must/must.go | 8 ++-- node.go | 6 +-- nodeBuilder.go | 4 +- {impl/typed => schema}/errors.go | 6 +-- schema/gen/go/_test/wow_test.go | 14 +++--- schema/gen/go/gen.go | 3 +- schema/gen/go/genKindBytes.go | 4 +- schema/gen/go/genKindBytesNode.go | 2 +- schema/gen/go/genKindInt.go | 4 +- schema/gen/go/genKindIntNode.go | 2 +- schema/gen/go/genKindLink.go | 4 +- schema/gen/go/genKindLinkNode.go | 2 +- schema/gen/go/genKindList.go | 4 +- schema/gen/go/genKindListNode.go | 10 ++-- schema/gen/go/genKindString.go | 4 +- schema/gen/go/genKindStringNode.go | 2 +- schema/gen/go/genKindStruct.go | 8 ++-- schema/gen/go/genKindStructNode.go | 30 ++++++------ schema/gen/go/genKindStructReprMap.go | 26 +++++----- {impl/typed => schema}/maybe.go | 2 +- schema/tests/doc.go | 2 +- schema/type.go | 4 +- schema/typedNode.go | 52 ++++++++++++++++++++ schema/validate.go | 8 ++-- 38 files changed, 157 insertions(+), 174 deletions(-) delete mode 100644 impl/typed/typedNode.go rename {impl/typed => schema}/errors.go (84%) rename {impl/typed => schema}/maybe.go (87%) create mode 100644 schema/typedNode.go diff --git a/README.md b/README.md index 98c6c14..ffb3732 100644 --- a/README.md +++ b/README.md @@ -35,8 +35,8 @@ or new codecs, or new higher-order order functions!) - `github.com/ipld/go-ipld-prime/encoding/dagcbor` -- implementations of marshalling and unmarshalling as CBOR (a fast, binary serialization format). - `github.com/ipld/go-ipld-prime/encoding/dagjson` -- implementations of marshalling and unmarshalling as JSON (a popular human readable format). - `github.com/ipld/go-ipld-prime/linking/cid` -- imported as `cidlink` -- provides concrete implementations of `Link` as a CID. Also, the multicodec registry. -- `github.com/ipld/go-ipld-prime/schema` -- contains the `schema.Type` declarations, which represent IPLD Schema type information. -- `github.com/ipld/go-ipld-prime/impl/typed` -- contains the `typed.Node` interface, which enhances the basic `Node` to have additional features described by IPLD Schemas. +- `github.com/ipld/go-ipld-prime/schema` -- contains the `schema.Type` and `schema.TypedNode` interface declarations, which represent IPLD Schema type information. +- `github.com/ipld/go-ipld-prime/impl/typed` -- provides concrete implementations of `schema.TypedNode` which decorate a basic `Node` at runtime to have additional features described by IPLD Schemas. diff --git a/_rsrch/nodesolution/codec/unmarshal.go b/_rsrch/nodesolution/codec/unmarshal.go index c700437..504573c 100644 --- a/_rsrch/nodesolution/codec/unmarshal.go +++ b/_rsrch/nodesolution/codec/unmarshal.go @@ -53,7 +53,7 @@ func Unmarshal(na ipld.NodeAssembler, tokSrc shared.TokenSource) error { // starts with the first token already primed. Necessary to get recursion // to flow right without a peek+unpeek system. func unmarshal(na ipld.NodeAssembler, tokSrc shared.TokenSource, tk *tok.Token) error { - // FUTURE: check for typed.NodeBuilder that's going to parse a Link (they can slurp any token kind they want). + // FUTURE: check for schema.TypedNodeBuilder that's going to parse a Link (they can slurp any token kind they want). switch tk.Type { case tok.TMapOpen: expectLen := tk.Length diff --git a/_rsrch/nodesolution/errors.go b/_rsrch/nodesolution/errors.go index 2844f0e..a392c6d 100644 --- a/_rsrch/nodesolution/errors.go +++ b/_rsrch/nodesolution/errors.go @@ -43,7 +43,7 @@ func (e ErrWrongKind) Error() string { // ErrNotExists may be returned from the lookup functions of the Node interface // to indicate a missing value. // -// Note that typed.ErrNoSuchField is another type of error which sometimes +// Note that schema.ErrNoSuchField is another type of error which sometimes // occurs in similar places as ErrNotExists. ErrNoSuchField is preferred // when handling data with constraints provided by a schema that mean that // a field can *never* exist (as differentiated from a map key which is diff --git a/_rsrch/nodesolution/node.go b/_rsrch/nodesolution/node.go index 9a710c1..45834d1 100644 --- a/_rsrch/nodesolution/node.go +++ b/_rsrch/nodesolution/node.go @@ -71,12 +71,12 @@ type Node interface { // Lookup is the equivalent of LookupString, but takes a reified Node // as a parameter instead of a plain string. // This mechanism is useful if working with typed maps (if the key types - // have constraints, and you already have a reified `typed.Node` value, + // have constraints, and you already have a reified `schema.TypedNode` value, // using that value can save parsing and validation costs); // and may simply be convenient if you already have a Node value in hand. // // (When writing generic functions over Node, a good rule of thumb is: - // when handling a map, check for `typed.Node`, and in this case prefer + // when handling a map, check for `schema.TypedNode`, and in this case prefer // the Lookup(Node) method; otherwise, favor LookupString; typically // implementations will have their fastest paths thusly.) Lookup(key Node) (Node, error) @@ -126,7 +126,7 @@ type Node interface { // Undefined nodes are returned when traversing a struct field that is // defined by a schema but unset in the data. (Undefined nodes are not - // possible otherwise; you'll only see them from `typed.Node`.) + // possible otherwise; you'll only see them from `schema.TypedNode`.) // The undefined flag is necessary so iterating over structs can // unambiguously make the distinction between values that are // present-and-null versus values that are absent. diff --git a/doc/dev/node-implementations.md b/doc/dev/node-implementations.md index 135fe93..482dd51 100644 --- a/doc/dev/node-implementations.md +++ b/doc/dev/node-implementations.md @@ -26,7 +26,7 @@ Concerns: - 2. Performance A `Node` implementation must of course conform with the Data Model. -Some nodes (especially, those that also implement `typed.Node`) may also +Some nodes (especially, those that also implement `schema.TypedNode`) may also have additional constraints. A `Node` implementation must maintain immutablity, or it shatters abstractions @@ -70,7 +70,7 @@ Castability for strings is safe when the `Node` is "general" (i.e. has no constr With no constraints, there's no Correctness concern; and since strings are immutable, there's no Immutablity concern. -Castability for strings is often *unsafe* when the `Node` is a `typed.Node`. +Castability for strings is often *unsafe* when the `Node` is a `schema.TypedNode`. Typed nodes may have additional constraints, so we would have a Correctness problem. (Note that the way we handle constraints in codegeneration means users can add them *after* the code is generated, so the generation system can't presume @@ -94,7 +94,7 @@ If the struct type is unexported, the concern is absolved: the zero value can't be initialized outside the package. If the `Node` implementation has no other constraints -(e.g., it's not also a `typed.Node` in addition to just an `ipld.Node`), +(e.g., it's not also a `schema.TypedNode` in addition to just an `ipld.Node`), the concern is (alomst certainly) absolved: the zero value is simply a valid value. diff --git a/doc/schema.md b/doc/schema.md index 057b658..9886b08 100644 --- a/doc/schema.md +++ b/doc/schema.md @@ -98,7 +98,7 @@ allowed to contain pointers (including cyclic references), etc. The reified schema can be computed purely from the schema declaration. The reified schema implementation lives in `go-ipld-prime//typed/system`, -and `go-ipld-prime//typed.Node` notably has a `Type() typesystem.Type` method +and `go-ipld-prime//schema.TypedNode` notably has a `Type() typesystem.Type` method which provides the reified schema information for any typed node. Note that multiple disjoint `typesystem.Universe` instances can exist in the diff --git a/encoding/dagcbor/unmarshal.go b/encoding/dagcbor/unmarshal.go index 2c57e4b..741498c 100644 --- a/encoding/dagcbor/unmarshal.go +++ b/encoding/dagcbor/unmarshal.go @@ -36,7 +36,7 @@ func Unmarshal(nb ipld.NodeBuilder, tokSrc shared.TokenSource) (ipld.Node, error // starts with the first token already primed. Necessary to get recursion // to flow right without a peek+unpeek system. func unmarshal(nb ipld.NodeBuilder, tokSrc shared.TokenSource, tk *tok.Token) (ipld.Node, error) { - // FUTURE: check for typed.NodeBuilder that's going to parse a Link (they can slurp any token kind they want). + // FUTURE: check for schema.TypedNodeBuilder that's going to parse a Link (they can slurp any token kind they want). switch tk.Type { case tok.TMapOpen: mb, err := nb.CreateMap() diff --git a/encoding/dagjson/unmarshal.go b/encoding/dagjson/unmarshal.go index 652bf44..d9a8dec 100644 --- a/encoding/dagjson/unmarshal.go +++ b/encoding/dagjson/unmarshal.go @@ -31,7 +31,7 @@ func Unmarshal(nb ipld.NodeBuilder, tokSrc shared.TokenSource) (ipld.Node, error // starts with the first token already primed. Necessary to get recursion // to flow right without a peek+unpeek system. func unmarshal(nb ipld.NodeBuilder, tokSrc shared.TokenSource, tk *tok.Token) (ipld.Node, error) { - // FUTURE: check for typed.NodeBuilder that's going to parse a Link (they can slurp any token kind they want). + // FUTURE: check for schema.TypedNodeBuilder that's going to parse a Link (they can slurp any token kind they want). switch tk.Type { case tok.TMapOpen: mb, err := nb.CreateMap() diff --git a/encoding/unmarshal.go b/encoding/unmarshal.go index 7e58182..1c5415d 100644 --- a/encoding/unmarshal.go +++ b/encoding/unmarshal.go @@ -42,7 +42,7 @@ func Unmarshal(nb ipld.NodeBuilder, tokSrc shared.TokenSource) (ipld.Node, error // starts with the first token already primed. Necessary to get recursion // to flow right without a peek+unpeek system. func unmarshal(nb ipld.NodeBuilder, tokSrc shared.TokenSource, tk *tok.Token) (ipld.Node, error) { - // FUTURE: check for typed.NodeBuilder that's going to parse a Link (they can slurp any token kind they want). + // FUTURE: check for schema.TypedNodeBuilder that's going to parse a Link (they can slurp any token kind they want). switch tk.Type { case tok.TMapOpen: mb, err := nb.CreateMap() diff --git a/errors.go b/errors.go index 382b0a1..46a7fc4 100644 --- a/errors.go +++ b/errors.go @@ -43,7 +43,7 @@ func (e ErrWrongKind) Error() string { // ErrNotExists may be returned from the lookup functions of the Node interface // to indicate a missing value. // -// Note that typed.ErrNoSuchField is another type of error which sometimes +// Note that schema.ErrNoSuchField is another type of error which sometimes // occurs in similar places as ErrNotExists. ErrNoSuchField is preferred // when handling data with constraints provided by a schema that mean that // a field can *never* exist (as differentiated from a map key which is @@ -64,7 +64,7 @@ func (e ErrNotExists) Error() string { type ErrInvalidKey struct { Reason string - // Perhaps typed.ErrNoSuchField could be folded into this? + // Perhaps schema.ErrNoSuchField could be folded into this? // Perhaps Reason could be replaced by an enum of "NoSuchField"|"NotAString"|"ConstraintRejected"? // Might be hard to get rid of the freetext field entirely -- constraints may be nontrivial to describe. } diff --git a/fluent/fluentNodeBuilder.go b/fluent/fluentNodeBuilder.go index 622cc5f..95352f1 100644 --- a/fluent/fluentNodeBuilder.go +++ b/fluent/fluentNodeBuilder.go @@ -81,7 +81,7 @@ func (nb *nodeBuilder) CreateMap(fn MapBuildingClosure) ipld.Node { if err != nil { panic(Error{err}) } - fn(mapBuilder{mb}, nb, nb) // FUTURE: check for typed.NodeBuilder; need to specialize latter params before calling down if so. + fn(mapBuilder{mb}, nb, nb) // FUTURE: check for schema.TypedNodeBuilder; need to specialize latter params before calling down if so. n, err := mb.Build() if err != nil { panic(Error{err}) @@ -93,7 +93,7 @@ func (nb *nodeBuilder) AmendMap(fn MapBuildingClosure) ipld.Node { if err != nil { panic(Error{err}) } - fn(mapBuilder{mb}, nb, nb) // FUTURE: check for typed.NodeBuilder; need to specialize latter params before calling down if so. + fn(mapBuilder{mb}, nb, nb) // FUTURE: check for schema.TypedNodeBuilder; need to specialize latter params before calling down if so. n, err := mb.Build() if err != nil { panic(Error{err}) @@ -105,7 +105,7 @@ func (nb *nodeBuilder) CreateList(fn ListBuildingClosure) ipld.Node { if err != nil { panic(Error{err}) } - fn(listBuilder{lb}, nb) // FUTURE: check for typed.NodeBuilder; need to specialize latter params before calling down if so. + fn(listBuilder{lb}, nb) // FUTURE: check for schema.TypedNodeBuilder; need to specialize latter params before calling down if so. n, err := lb.Build() if err != nil { panic(Error{err}) @@ -117,7 +117,7 @@ func (nb *nodeBuilder) AmendList(fn ListBuildingClosure) ipld.Node { if err != nil { panic(Error{err}) } - fn(listBuilder{lb}, nb) // FUTURE: check for typed.NodeBuilder; need to specialize latter params before calling down if so. + fn(listBuilder{lb}, nb) // FUTURE: check for schema.TypedNodeBuilder; need to specialize latter params before calling down if so. n, err := lb.Build() if err != nil { panic(Error{err}) diff --git a/impl/typed/typedLinkNode.go b/impl/typed/typedLinkNode.go index 271ebc1..e6f8080 100644 --- a/impl/typed/typedLinkNode.go +++ b/impl/typed/typedLinkNode.go @@ -2,7 +2,7 @@ package typed import "github.com/ipld/go-ipld-prime" -// typed.LinkNode is a superset of the typed.Node interface, and has one additional behavior. +// typed.LinkNode is a superset of the schema.TypedNode interface, and has one additional behavior. // // A typed.LinkNode contains a hint for the appropriate node builder to use for loading data // on the other side of the link contained within the node, so that it can be assembled diff --git a/impl/typed/typedNode.go b/impl/typed/typedNode.go deleted file mode 100644 index 1d9196c..0000000 --- a/impl/typed/typedNode.go +++ /dev/null @@ -1,66 +0,0 @@ -package typed - -import ( - "github.com/ipld/go-ipld-prime" - "github.com/ipld/go-ipld-prime/schema" -) - -// typed.Node is a superset of the ipld.Node interface, and has additional behaviors. -// -// A typed.Node can be inspected for its schema.Type and schema.Kind, -// which conveys much more and richer information than the Data Model layer -// ipld.ReprKind. -// -// There are many different implementations of typed.Node. -// One implementation can wrap any other existing ipld.Node (i.e., it's zero-copy) -// and promises that it has *already* been validated to match the typesystem.Type; -// another implementation similarly wraps any other existing ipld.Node, but -// defers to the typesystem validation checking to fields that are accessed; -// and when using code generation tools, all of the generated native Golang -// types produced by the codegen will each individually implement typed.Node. -// -// Note that typed.Node can wrap *other* typed.Node instances. -// Imagine you have two parts of a very large code base which have codegen'd -// components which are from different versions of a schema. Smooth migrations -// and zero-copy type-safe data sharing between them: We can accommodate that! -// -// Typed nodes sometimes have slightly different behaviors than plain nodes: -// For example, when looking up fields on a typed node that's a struct, -// the error returned for a lookup with a key that's not a field name will -// be ErrNoSuchField (instead of ErrNotExists). -// These behaviors apply to the typed.Node only and not their representations; -// continuing the example, the .Representation().LookupString() method on -// that same node for the same key as plain `.LookupString()` will still -// return ErrNotExists, because the representation isn't a typed.Node! -type Node interface { - // typed.Node acts just like a regular Node for almost all purposes; - // which ReprKind it acts as is determined by the TypeKind. - // (Note that the representation strategy of the type does *not* affect - // the ReprKind of typed.Node -- rather, the representation strategy - // affects the `.Representation().ReprKind()`.) - // - // For example: if the `.Type().Kind()` of this node is "struct", - // it will act like ReprKind() == "map" - // (even if Type().(Struct).ReprStrategy() is "tuple"). - ipld.Node - - // Type returns a reference to the reified schema.Type value. - Type() schema.Type - - // Representation returns an ipld.Node which sees the data in this node - // in its representation form. - // - // For example: if the `.Type().Kind()` of this node is "struct", - // `.Representation().Kind()` may vary based on its representation strategy: - // if the representation strategy is "map", then it will be ReprKind=="map"; - // if the streatgy is "tuple", then it will be ReprKind=="list". - Representation() ipld.Node -} - -// unboxing is... ugh, we probably should codegen an unbox method per concrete type. -// (or, attach them to the non-pointer type, which would namespace in an alloc-free way, but i don't know if that's anything but confusing.) -// there are notes about this from way back at 2019.01; reread to see if any remain relevant and valid. -// main important point is: it's not gonna be casting. -// if casting was sufficient to unbox, it'd mean every method on the Node interface would be difficult to use as a field name on a struct type. undesirable. -// okay, or, alternative, we flip this to `superapi.Footype{}.Fields().FrobFieldName()`. that strikes me as unlikely to be pleasing, though. -// istm we can safely expect direct use of field names much, much more often that flipping back and forth to hypergeneric node; so we should optimize syntax for that accordingly. diff --git a/impl/typed/wrapStruct.go b/impl/typed/wrapStruct.go index 1673a35..817b228 100644 --- a/impl/typed/wrapStruct.go +++ b/impl/typed/wrapStruct.go @@ -8,7 +8,7 @@ import ( "github.com/ipld/go-ipld-prime/schema" ) -var _ Node = wrapnodeStruct{} +var _ schema.TypedNode = wrapnodeStruct{} type wrapnodeStruct struct { ipld.Node @@ -16,7 +16,7 @@ type wrapnodeStruct struct { } // Most of the 'nope' methods from the inner node are fine; -// we add the extra things required for typed.Node; +// we add the extra things required for schema.TypedNode; // we decorate the getters and iterators to handle the distinct path around optionals // and return a different error for missing fields; // length becomes fixed to a constant; @@ -42,7 +42,7 @@ func (tn wrapnodeStruct) LookupString(key string) (ipld.Node, error) { } return nil, e1 } - return nil, ErrNoSuchField{Type: tn.typ, FieldName: key} + return nil, schema.ErrNoSuchField{Type: tn.typ, FieldName: key} } func (tn wrapnodeStruct) MapIterator() ipld.MapIterator { @@ -168,10 +168,10 @@ func (mb *wrapnodeStruct_MapBuilder) Insert(k, v ipld.Node) error { // Check that the field exists at all. field := mb.typ.Field(ks) if field == nil { - return ErrNoSuchField{Type: mb.typ, FieldName: ks} + return schema.ErrNoSuchField{Type: mb.typ, FieldName: ks} } // Check that the value is assignable to this field, or return error. - vt, ok := v.(Node) + vt, ok := v.(schema.TypedNode) switch { case v.IsNull(): if !field.IsNullable() { @@ -184,7 +184,7 @@ func (mb *wrapnodeStruct_MapBuilder) Insert(k, v ipld.Node) error { } // if typed node, and it matches: carry on. default: - return fmt.Errorf("need typed.Node for insertion into struct") // FUTURE: maybe if it's a basic enough thing we sholud attempt coerce? + return fmt.Errorf("need schema.TypedNode for insertion into struct") // FUTURE: maybe if it's a basic enough thing we sholud attempt coerce? } // Insert the value, and note it's now been set. if err := mb.utmb.Insert(k, v); err != nil { diff --git a/must/must.go b/must/must.go index 71528bb..9559c6d 100644 --- a/must/must.go +++ b/must/must.go @@ -18,7 +18,7 @@ package must import ( ipld "github.com/ipld/go-ipld-prime" - "github.com/ipld/go-ipld-prime/impl/typed" + "github.com/ipld/go-ipld-prime/schema" ) // must.NotError simply panics if given an error. @@ -47,18 +47,18 @@ func Node(n ipld.Node, e error) ipld.Node { // must.TypedNode helps write pointfree/chainable-style code // by taking a Node and an error and transforming any error into a panic. -// It will also cast the `ipld.Node` to a `typed.Node`, panicking if impossible. +// It will also cast the `ipld.Node` to a `schema.TypedNode`, panicking if impossible. // // Because golang supports implied destructuring of multiple-return functions // into arguments for another funtion of matching arity, it can be used like this: // // must.TypedNode(SomeNodeBuilder{}.CreateString("a")) // -func TypedNode(n ipld.Node, e error) typed.Node { +func TypedNode(n ipld.Node, e error) schema.TypedNode { if e != nil { panic(e) } - return n.(typed.Node) + return n.(schema.TypedNode) } // must.True panics if the given bool is false. diff --git a/node.go b/node.go index 1d8d50d..7260f43 100644 --- a/node.go +++ b/node.go @@ -71,12 +71,12 @@ type Node interface { // Lookup is the equivalent of LookupString, but takes a reified Node // as a parameter instead of a plain string. // This mechanism is useful if working with typed maps (if the key types - // have constraints, and you already have a reified `typed.Node` value, + // have constraints, and you already have a reified `schema.TypedNode` value, // using that value can save parsing and validation costs); // and may simply be convenient if you already have a Node value in hand. // // (When writing generic functions over Node, a good rule of thumb is: - // when handling a map, check for `typed.Node`, and in this case prefer + // when handling a map, check for `schema.TypedNode`, and in this case prefer // the Lookup(Node) method; otherwise, favor LookupString; typically // implementations will have their fastest paths thusly.) Lookup(key Node) (Node, error) @@ -128,7 +128,7 @@ type Node interface { // Undefined nodes are returned when traversing a struct field that is // defined by a schema but unset in the data. (Undefined nodes are not - // possible otherwise; you'll only see them from `typed.Node`.) + // possible otherwise; you'll only see them from `schema.TypedNode`.) // The undefined flag is necessary so iterating over structs can // unambiguously make the distinction between values that are // present-and-null versus values that are absent. diff --git a/nodeBuilder.go b/nodeBuilder.go index cbed0d7..3add5e5 100644 --- a/nodeBuilder.go +++ b/nodeBuilder.go @@ -31,10 +31,10 @@ package ipld // `reflect.Type` handle it can use to create a new value of that native type; // similarly, schema-typed Nodes will yield a NodeBuilder that keeps the schema // info and type constraints from that Node! -// (Continuing the typed.Node example: if you have a typed.Node that is +// (Continuing the schema.TypedNode example: if you have a schema.TypedNode that is // constrained to be of some `type Foo = {Bar:Baz}` type, then any new Node // produced from its NodeBuilder will still answer -// `n.(typed.Node).Type().Name()` as `Foo`; and if +// `n.(schema.TypedNode).Type().Name()` as `Foo`; and if // `n.NodeBuilder().AmendMap().Insert(...)` is called with nodes of unmatching // type given to the insertion, the builder will error!) // diff --git a/impl/typed/errors.go b/schema/errors.go similarity index 84% rename from impl/typed/errors.go rename to schema/errors.go index 817688a..d0f908e 100644 --- a/impl/typed/errors.go +++ b/schema/errors.go @@ -1,16 +1,14 @@ -package typed +package schema import ( "fmt" - - "github.com/ipld/go-ipld-prime/schema" ) // ErrNoSuchField may be returned from lookup functions on the Node // interface when a field is requested which doesn't exist, or from Insert // on a MapBuilder when a key doesn't match a field name in the structure. type ErrNoSuchField struct { - Type schema.Type + Type Type FieldName string } diff --git a/schema/gen/go/_test/wow_test.go b/schema/gen/go/_test/wow_test.go index bef3e10..3dcacf5 100644 --- a/schema/gen/go/_test/wow_test.go +++ b/schema/gen/go/_test/wow_test.go @@ -13,7 +13,7 @@ import ( "github.com/ipld/go-ipld-prime/encoding" "github.com/ipld/go-ipld-prime/fluent" ipldfree "github.com/ipld/go-ipld-prime/impl/free" - "github.com/ipld/go-ipld-prime/impl/typed" + "github.com/ipld/go-ipld-prime/schema" ) // TokenSourceBucket acts like a TokenSource by yielding tokens from a pre-made @@ -65,7 +65,7 @@ func TestScalarUnmarshal(t *testing.T) { func TestGeneratedStructs(t *testing.T) { t.Run("struct with map repr", func(t *testing.T) { var ( - v0, v1, v2, v3, v4 typed.Node + v0, v1, v2, v3, v4 schema.TypedNode ) t.Run("type-level build and read", func(t *testing.T) { t.Run("all fields set", func(t *testing.T) { @@ -76,7 +76,7 @@ func TestGeneratedStructs(t *testing.T) { mb.Insert(ipldfree.String("f3"), plz(String__NodeBuilder().CreateString("c"))) mb.Insert(ipldfree.String("f4"), plz(String__NodeBuilder().CreateString("d"))) n, err := mb.Build() - v0 = n.(typed.Node) + v0 = n.(schema.TypedNode) Wish(t, err, ShouldEqual, nil) Wish(t, n.ReprKind(), ShouldEqual, ipld.ReprKind_Map) @@ -93,7 +93,7 @@ func TestGeneratedStructs(t *testing.T) { mb.Insert(ipldfree.String("f3"), plz(String__NodeBuilder().CreateString("c"))) mb.Insert(ipldfree.String("f4"), ipld.Null) n, err := mb.Build() - v1 = n.(typed.Node) + v1 = n.(schema.TypedNode) Wish(t, err, ShouldEqual, nil) Wish(t, n.ReprKind(), ShouldEqual, ipld.ReprKind_Map) @@ -111,7 +111,7 @@ func TestGeneratedStructs(t *testing.T) { mb.Insert(ipldfree.String("f3"), ipld.Null) mb.Insert(ipldfree.String("f4"), plz(String__NodeBuilder().CreateString("d"))) n, err := mb.Build() - v2 = n.(typed.Node) + v2 = n.(schema.TypedNode) Wish(t, err, ShouldEqual, nil) Wish(t, n.ReprKind(), ShouldEqual, ipld.ReprKind_Map) @@ -128,7 +128,7 @@ func TestGeneratedStructs(t *testing.T) { mb.Insert(ipldfree.String("f3"), plz(String__NodeBuilder().CreateString("c"))) mb.Insert(ipldfree.String("f4"), plz(String__NodeBuilder().CreateString("d"))) n, err := mb.Build() - v3 = n.(typed.Node) + v3 = n.(schema.TypedNode) Wish(t, err, ShouldEqual, nil) Wish(t, n.ReprKind(), ShouldEqual, ipld.ReprKind_Map) @@ -145,7 +145,7 @@ func TestGeneratedStructs(t *testing.T) { mb.Insert(ipldfree.String("f2"), plz(String__NodeBuilder().CreateString("b"))) mb.Insert(ipldfree.String("f4"), plz(String__NodeBuilder().CreateString("d"))) n, err := mb.Build() - v4 = n.(typed.Node) + v4 = n.(schema.TypedNode) Wish(t, err, ShouldEqual, nil) Wish(t, n.ReprKind(), ShouldEqual, ipld.ReprKind_Map) diff --git a/schema/gen/go/gen.go b/schema/gen/go/gen.go index 951d952..c423cb9 100644 --- a/schema/gen/go/gen.go +++ b/schema/gen/go/gen.go @@ -30,7 +30,7 @@ type typedNodeGenerator interface { EmitNativeBuilder(io.Writer) // typically emits some kind of struct that has a Build method. EmitNativeMaybe(io.Writer) // a pointer-free 'maybe' mechanism is generated for all types. - // -- the typed.Node.Type method and vars --> + // -- the schema.TypedNode.Type method and vars --> EmitTypedNodeMethodType(io.Writer) // these emit dummies for now @@ -100,7 +100,6 @@ func EmitFileHeader(packageName string, w io.Writer) { fmt.Fprintf(w, "package %s\n\n", packageName) fmt.Fprintf(w, "import (\n") fmt.Fprintf(w, "\tipld \"github.com/ipld/go-ipld-prime\"\n") - fmt.Fprintf(w, "\t\"github.com/ipld/go-ipld-prime/impl/typed\"\n") fmt.Fprintf(w, "\t\"github.com/ipld/go-ipld-prime/schema\"\n") fmt.Fprintf(w, ")\n\n") fmt.Fprintf(w, "// Code generated go-ipld-prime DO NOT EDIT.\n\n") diff --git a/schema/gen/go/genKindBytes.go b/schema/gen/go/genKindBytes.go index aaa3a80..3ec09e2 100644 --- a/schema/gen/go/genKindBytes.go +++ b/schema/gen/go/genKindBytes.go @@ -46,12 +46,12 @@ func (gk generateKindBytes) EmitNativeMaybe(w io.Writer) { // TODO this can most likely be extracted and DRY'd, just not 100% sure yet doTemplate(` type Maybe{{ .Type | mungeTypeNodeIdent }} struct { - Maybe typed.Maybe + Maybe schema.Maybe Value {{ .Type | mungeTypeNodeIdent }} } func (m Maybe{{ .Type | mungeTypeNodeIdent }}) Must() {{ .Type | mungeTypeNodeIdent }} { - if m.Maybe != typed.Maybe_Value { + if m.Maybe != schema.Maybe_Value { panic("unbox of a maybe rejected") } return m.Value diff --git a/schema/gen/go/genKindBytesNode.go b/schema/gen/go/genKindBytesNode.go index 99f7e66..7fed94e 100644 --- a/schema/gen/go/genKindBytesNode.go +++ b/schema/gen/go/genKindBytesNode.go @@ -11,7 +11,7 @@ import ( func (gk generateKindBytes) EmitNodeType(w io.Writer) { doTemplate(` var _ ipld.Node = {{ .Type | mungeTypeNodeIdent }}{} - var _ typed.Node = {{ .Type | mungeTypeNodeIdent }}{} + var _ schema.TypedNode = {{ .Type | mungeTypeNodeIdent }}{} `, w, gk) } diff --git a/schema/gen/go/genKindInt.go b/schema/gen/go/genKindInt.go index 3fea5d2..3d97a33 100644 --- a/schema/gen/go/genKindInt.go +++ b/schema/gen/go/genKindInt.go @@ -51,12 +51,12 @@ func (gk generateKindInt) EmitNativeMaybe(w io.Writer) { // TODO this can most likely be extracted and DRY'd, just not 100% sure yet doTemplate(` type Maybe{{ .Type | mungeTypeNodeIdent }} struct { - Maybe typed.Maybe + Maybe schema.Maybe Value {{ .Type | mungeTypeNodeIdent }} } func (m Maybe{{ .Type | mungeTypeNodeIdent }}) Must() {{ .Type | mungeTypeNodeIdent }} { - if m.Maybe != typed.Maybe_Value { + if m.Maybe != schema.Maybe_Value { panic("unbox of a maybe rejected") } return m.Value diff --git a/schema/gen/go/genKindIntNode.go b/schema/gen/go/genKindIntNode.go index 8812305..17d3565 100644 --- a/schema/gen/go/genKindIntNode.go +++ b/schema/gen/go/genKindIntNode.go @@ -11,7 +11,7 @@ import ( func (gk generateKindInt) EmitNodeType(w io.Writer) { doTemplate(` var _ ipld.Node = {{ .Type | mungeTypeNodeIdent }}{} - var _ typed.Node = {{ .Type | mungeTypeNodeIdent }}{} + var _ schema.TypedNode = {{ .Type | mungeTypeNodeIdent }}{} `, w, gk) } diff --git a/schema/gen/go/genKindLink.go b/schema/gen/go/genKindLink.go index a00526e..bfc57f2 100644 --- a/schema/gen/go/genKindLink.go +++ b/schema/gen/go/genKindLink.go @@ -46,12 +46,12 @@ func (gk generateKindLink) EmitNativeMaybe(w io.Writer) { // TODO this can most likely be extracted and DRY'd, just not 100% sure yet doTemplate(` type Maybe{{ .Type | mungeTypeNodeIdent }} struct { - Maybe typed.Maybe + Maybe schema.Maybe Value {{ .Type | mungeTypeNodeIdent }} } func (m Maybe{{ .Type | mungeTypeNodeIdent }}) Must() {{ .Type | mungeTypeNodeIdent }} { - if m.Maybe != typed.Maybe_Value { + if m.Maybe != schema.Maybe_Value { panic("unbox of a maybe rejected") } return m.Value diff --git a/schema/gen/go/genKindLinkNode.go b/schema/gen/go/genKindLinkNode.go index 6bfe465..2dd1a7e 100644 --- a/schema/gen/go/genKindLinkNode.go +++ b/schema/gen/go/genKindLinkNode.go @@ -11,7 +11,7 @@ import ( func (gk generateKindLink) EmitNodeType(w io.Writer) { doTemplate(` var _ ipld.Node = {{ .Type | mungeTypeNodeIdent }}{} - var _ typed.Node = {{ .Type | mungeTypeNodeIdent }}{} + var _ schema.TypedNode = {{ .Type | mungeTypeNodeIdent }}{} `, w, gk) } diff --git a/schema/gen/go/genKindList.go b/schema/gen/go/genKindList.go index 6a19e53..4950ba7 100644 --- a/schema/gen/go/genKindList.go +++ b/schema/gen/go/genKindList.go @@ -50,12 +50,12 @@ func (gk generateKindList) EmitNativeMaybe(w io.Writer) { // TODO this can most likely be extracted and DRY'd, just not 100% sure yet doTemplate(` type Maybe{{ .Type | mungeTypeNodeIdent }} struct { - Maybe typed.Maybe + Maybe schema.Maybe Value {{ .Type | mungeTypeNodeIdent }} } func (m Maybe{{ .Type | mungeTypeNodeIdent }}) Must() {{ .Type | mungeTypeNodeIdent }} { - if m.Maybe != typed.Maybe_Value { + if m.Maybe != schema.Maybe_Value { panic("unbox of a maybe rejected") } return m.Value diff --git a/schema/gen/go/genKindListNode.go b/schema/gen/go/genKindListNode.go index e874915..c51e4ba 100644 --- a/schema/gen/go/genKindListNode.go +++ b/schema/gen/go/genKindListNode.go @@ -11,7 +11,7 @@ import ( func (gk generateKindList) EmitNodeType(w io.Writer) { doTemplate(` var _ ipld.Node = {{ .Type | mungeTypeNodeIdent }}{} - var _ typed.Node = {{ .Type | mungeTypeNodeIdent }}{} + var _ schema.TypedNode = {{ .Type | mungeTypeNodeIdent }}{} `, w, gk) } @@ -151,8 +151,8 @@ func (gk generateNbKindList) EmitNodebuilderMethodCreateList(w io.Writer) { // - This builder, being all about semantics and not at all about serialization, // is order-insensitive. // - We don't specially handle being given 'undef' as a value. - // It just falls into the "need a typed.Node" error bucket. - // - We only accept *codegenerated values* -- a typed.Node created + // It just falls into the "need a schema.TypedNode" error bucket. + // - We only accept *codegenerated values* -- a schema.TypedNode created // in the same schema universe *isn't accepted*. // REVIEW: We could try to accept those, but it might have perf/sloc costs, // and it's hard to imagine a user story that gets here. @@ -214,9 +214,9 @@ func (gk generateNbKindList) EmitNodebuilderMethodCreateList(w io.Writer) { panic("type mismatch on struct field assignment: cannot assign null to non-nullable field") // FIXME need an error type for this } {{- end}} - tv, ok := v.(typed.Node) + tv, ok := v.(schema.TypedNode) if !ok { - panic("need typed.Node for insertion into struct") // FIXME need an error type for this + panic("need schema.TypedNode for insertion into struct") // FIXME need an error type for this } _, ok = v.({{ .Type.ValueType | mungeTypeNodeIdent }}) if !ok { diff --git a/schema/gen/go/genKindString.go b/schema/gen/go/genKindString.go index 7846da6..af342f3 100644 --- a/schema/gen/go/genKindString.go +++ b/schema/gen/go/genKindString.go @@ -72,12 +72,12 @@ func (gk generateKindString) EmitNativeBuilder(w io.Writer) { func (gk generateKindString) EmitNativeMaybe(w io.Writer) { doTemplate(` type Maybe{{ .Type | mungeTypeNodeIdent }} struct { - Maybe typed.Maybe + Maybe schema.Maybe Value {{ .Type | mungeTypeNodeIdent }} } func (m Maybe{{ .Type | mungeTypeNodeIdent }}) Must() {{ .Type | mungeTypeNodeIdent }} { - if m.Maybe != typed.Maybe_Value { + if m.Maybe != schema.Maybe_Value { panic("unbox of a maybe rejected") } return m.Value diff --git a/schema/gen/go/genKindStringNode.go b/schema/gen/go/genKindStringNode.go index af88f0f..0972d8a 100644 --- a/schema/gen/go/genKindStringNode.go +++ b/schema/gen/go/genKindStringNode.go @@ -11,7 +11,7 @@ import ( func (gk generateKindString) EmitNodeType(w io.Writer) { doTemplate(` var _ ipld.Node = {{ .Type | mungeTypeNodeIdent }}{} - var _ typed.Node = {{ .Type | mungeTypeNodeIdent }}{} + var _ schema.TypedNode = {{ .Type | mungeTypeNodeIdent }}{} `, w, gk) } diff --git a/schema/gen/go/genKindStruct.go b/schema/gen/go/genKindStruct.go index c3b05e9..a668c4c 100644 --- a/schema/gen/go/genKindStruct.go +++ b/schema/gen/go/genKindStruct.go @@ -60,11 +60,11 @@ func (gk generateKindStruct) EmitNativeBuilder(w io.Writer) { {{- if or $field.IsOptional $field.IsNullable }} {{- /* if both modifiers present, anything goes */ -}} {{- else if $field.IsOptional }} - if b.{{ $field.Name | titlize }}.Maybe == typed.Maybe_Null { + if b.{{ $field.Name | titlize }}.Maybe == schema.Maybe_Null { return {{ $field.Type | mungeTypeNodeIdent }}{}, fmt.Errorf("cannot be absent") } {{- else if $field.IsNullable }} - if b.{{ $field.Name | titlize }}.Maybe == typed.Maybe_Absent { + if b.{{ $field.Name | titlize }}.Maybe == schema.Maybe_Absent { return {{ $field.Type | mungeTypeNodeIdent }}{}, fmt.Errorf("cannot be null") } {{- end}} @@ -88,12 +88,12 @@ func (gk generateKindStruct) EmitNativeBuilder(w io.Writer) { func (gk generateKindStruct) EmitNativeMaybe(w io.Writer) { doTemplate(` type Maybe{{ .Type | mungeTypeNodeIdent }} struct { - Maybe typed.Maybe + Maybe schema.Maybe Value {{ .Type | mungeTypeNodeIdent }} } func (m Maybe{{ .Type | mungeTypeNodeIdent }}) Must() {{ .Type | mungeTypeNodeIdent }} { - if m.Maybe != typed.Maybe_Value { + if m.Maybe != schema.Maybe_Value { panic("unbox of a maybe rejected") } return m.Value diff --git a/schema/gen/go/genKindStructNode.go b/schema/gen/go/genKindStructNode.go index 9fee6d5..c9f8c8e 100644 --- a/schema/gen/go/genKindStructNode.go +++ b/schema/gen/go/genKindStructNode.go @@ -11,7 +11,7 @@ import ( func (gk generateKindStruct) EmitNodeType(w io.Writer) { doTemplate(` var _ ipld.Node = {{ .Type | mungeTypeNodeIdent }}{} - var _ typed.Node = {{ .Type | mungeTypeNodeIdent }}{} + var _ schema.TypedNode = {{ .Type | mungeTypeNodeIdent }}{} `, w, gk) } @@ -39,12 +39,12 @@ func (gk generateKindStruct) EmitNodeMethodLookupString(w io.Writer) { {{- range $field := .Type.Fields }} case "{{ $field.Name }}": {{- if $field.IsOptional }} - if x.d.{{ $field.Name | titlize }}.Maybe == typed.Maybe_Absent { + if x.d.{{ $field.Name | titlize }}.Maybe == schema.Maybe_Absent { return ipld.Undef, nil } {{- end}} {{- if $field.IsNullable }} - if x.d.{{ $field.Name | titlize }}.Maybe == typed.Maybe_Null { + if x.d.{{ $field.Name | titlize }}.Maybe == schema.Maybe_Null { return ipld.Null, nil } {{- end}} @@ -55,7 +55,7 @@ func (gk generateKindStruct) EmitNodeMethodLookupString(w io.Writer) { {{- end}} {{- end}} default: - return nil, typed.ErrNoSuchField{Type: nil /*TODO*/, FieldName: key} + return nil, schema.ErrNoSuchField{Type: nil /*TODO*/, FieldName: key} } } `, w, gk) @@ -95,13 +95,13 @@ func (gk generateKindStruct) EmitNodeMethodMapIterator(w io.Writer) { case {{ $i }}: k = String{"{{ $field.Name }}"} {{- if $field.IsOptional }} - if itr.node.d.{{ $field.Name | titlize }}.Maybe == typed.Maybe_Absent { + if itr.node.d.{{ $field.Name | titlize }}.Maybe == schema.Maybe_Absent { v = ipld.Undef break } {{- end}} {{- if $field.IsNullable }} - if itr.node.d.{{ $field.Name | titlize }}.Maybe == typed.Maybe_Null { + if itr.node.d.{{ $field.Name | titlize }}.Maybe == schema.Maybe_Null { v = ipld.Null break } @@ -178,8 +178,8 @@ func (gk generateNbKindStruct) EmitNodebuilderMethodCreateMap(w io.Writer) { // - This builder, being all about semantics and not at all about serialization, // is order-insensitive. // - We don't specially handle being given 'undef' as a value. - // It just falls into the "need a typed.Node" error bucket. - // - We only accept *codegenerated values* -- a typed.Node created + // It just falls into the "need a schema.TypedNode" error bucket. + // - We only accept *codegenerated values* -- a schema.TypedNode created // in the same schema universe *isn't accepted*. // REVIEW: We could try to accept those, but it might have perf/sloc costs, // and it's hard to imagine a user story that gets here. @@ -195,7 +195,7 @@ func (gk generateNbKindStruct) EmitNodebuilderMethodCreateMap(w io.Writer) { mb := &{{ .Type | mungeTypeNodeMapBuilderIdent }}{v:&{{ .Type | mungeTypeNodeIdent }}{}} {{- range $field := .Type.Fields }} {{- if $field.IsOptional }} - mb.v.d.{{ $field.Name | titlize }}.Maybe = typed.Maybe_Absent + mb.v.d.{{ $field.Name | titlize }}.Maybe = schema.Maybe_Absent {{- end}} {{- end}} return mb, nil @@ -221,7 +221,7 @@ func (gk generateNbKindStruct) EmitNodebuilderMethodCreateMap(w io.Writer) { case "{{ $field.Name }}": {{- if $field.IsNullable }} if v.IsNull() { - mb.v.d.{{ $field.Name | titlize}}.Maybe = typed.Maybe_Null + mb.v.d.{{ $field.Name | titlize}}.Maybe = schema.Maybe_Null {{- if not $field.IsOptional }} mb.{{ $field.Name }}__isset = true {{- end}} @@ -232,9 +232,9 @@ func (gk generateNbKindStruct) EmitNodebuilderMethodCreateMap(w io.Writer) { panic("type mismatch on struct field assignment: cannot assign null to non-nullable field") // FIXME need an error type for this } {{- end}} - tv, ok := v.(typed.Node) + tv, ok := v.(schema.TypedNode) if !ok { - panic("need typed.Node for insertion into struct") // FIXME need an error type for this + panic("need schema.TypedNode for insertion into struct") // FIXME need an error type for this } x, ok := v.({{ $field.Type | mungeTypeNodeIdent }}) if !ok { @@ -247,13 +247,13 @@ func (gk generateNbKindStruct) EmitNodebuilderMethodCreateMap(w io.Writer) { mb.v.d.{{ $field.Name | titlize}} = x {{- end}} {{- if $field.IsOptional }} - mb.v.d.{{ $field.Name | titlize}}.Maybe = typed.Maybe_Value + mb.v.d.{{ $field.Name | titlize}}.Maybe = schema.Maybe_Value {{- else}} mb.{{ $field.Name }}__isset = true {{- end}} {{- end}} default: - return typed.ErrNoSuchField{Type: nil /*TODO:typelit*/, FieldName: ks} + return schema.ErrNoSuchField{Type: nil /*TODO:typelit*/, FieldName: ks} } return nil } @@ -283,7 +283,7 @@ func (gk generateNbKindStruct) EmitNodebuilderMethodCreateMap(w io.Writer) { return {{ $field.Type | mungeNodebuilderConstructorIdent }}() {{- end}} default: - panic(typed.ErrNoSuchField{Type: nil /*TODO:typelit*/, FieldName: ks}) + panic(schema.ErrNoSuchField{Type: nil /*TODO:typelit*/, FieldName: ks}) } return nil } diff --git a/schema/gen/go/genKindStructReprMap.go b/schema/gen/go/genKindStructReprMap.go index 68888ed..2bfde77 100644 --- a/schema/gen/go/genKindStructReprMap.go +++ b/schema/gen/go/genKindStructReprMap.go @@ -50,12 +50,12 @@ func (gk generateStructReprMapNode) EmitNodeMethodLookupString(w io.Writer) { {{- range $field := .Type.Fields }} case "{{ $field | $type.RepresentationStrategy.GetFieldKey }}": {{- if $field.IsOptional }} - if rn.n.d.{{ $field.Name | titlize}}.Maybe == typed.Maybe_Absent { + if rn.n.d.{{ $field.Name | titlize}}.Maybe == schema.Maybe_Absent { return ipld.Undef, ipld.ErrNotExists{ipld.PathSegmentOfString(key)} } {{- end}} {{- if $field.IsNullable }} - if rn.n.d.{{ $field.Name | titlize}}.Maybe == typed.Maybe_Null { + if rn.n.d.{{ $field.Name | titlize}}.Maybe == schema.Maybe_Null { return ipld.Null, nil } {{- end}} @@ -66,7 +66,7 @@ func (gk generateStructReprMapNode) EmitNodeMethodLookupString(w io.Writer) { {{- end}} {{- end}} default: - return nil, typed.ErrNoSuchField{Type: nil /*TODO*/, FieldName: key} + return nil, schema.ErrNoSuchField{Type: nil /*TODO*/, FieldName: key} } } `, w, gk) @@ -110,13 +110,13 @@ func (gk generateStructReprMapNode) EmitNodeMethodMapIterator(w io.Writer) { case {{ $i }}: k = String{"{{ $field | $type.RepresentationStrategy.GetFieldKey }}"} {{- if $field.IsOptional }} - if itr.node.d.{{ $field.Name | titlize}}.Maybe == typed.Maybe_Absent { + if itr.node.d.{{ $field.Name | titlize}}.Maybe == schema.Maybe_Absent { itr.idx++ continue } {{- end}} {{- if $field.IsNullable }} - if itr.node.d.{{ $field.Name | titlize}}.Maybe == typed.Maybe_Null { + if itr.node.d.{{ $field.Name | titlize}}.Maybe == schema.Maybe_Null { v = ipld.Null break } @@ -150,7 +150,7 @@ func (gk generateStructReprMapNode) EmitNodeMethodLength(w io.Writer) { l := {{ len .Type.Fields }} {{- range $field := .Type.Fields }} {{- if $field.IsOptional }} - if rn.n.d.{{ $field.Name | titlize}}.Maybe == typed.Maybe_Absent { + if rn.n.d.{{ $field.Name | titlize}}.Maybe == schema.Maybe_Absent { l-- } {{- end}} @@ -220,7 +220,7 @@ func (gk generateStructReprMapNb) EmitNodebuilderMethodCreateMap(w io.Writer) { mb := &{{ .Type | mungeTypeReprNodeMapBuilderIdent }}{v:&{{ .Type | mungeTypeNodeIdent }}{}} {{- range $field := .Type.Fields }} {{- if $field.IsOptional }} - mb.v.d.{{ $field.Name | titlize }}.Maybe = typed.Maybe_Absent + mb.v.d.{{ $field.Name | titlize }}.Maybe = schema.Maybe_Absent {{- end}} {{- end}} return mb, nil @@ -247,7 +247,7 @@ func (gk generateStructReprMapNb) EmitNodebuilderMethodCreateMap(w io.Writer) { } {{- if $field.IsNullable }} if v.IsNull() { - mb.v.d.{{ $field.Name | titlize}}.Maybe = typed.Maybe_Null + mb.v.d.{{ $field.Name | titlize}}.Maybe = schema.Maybe_Null mb.{{ $field.Name }}__isset = true return nil } @@ -256,9 +256,9 @@ func (gk generateStructReprMapNb) EmitNodebuilderMethodCreateMap(w io.Writer) { panic("type mismatch on struct field assignment: cannot assign null to non-nullable field") // FIXME need an error type for this } {{- end}} - tv, ok := v.(typed.Node) + tv, ok := v.(schema.TypedNode) if !ok { - panic("need typed.Node for insertion into struct") // FIXME need an error type for this + panic("need schema.TypedNode for insertion into struct") // FIXME need an error type for this } x, ok := v.({{ $field.Type | mungeTypeNodeIdent }}) if !ok { @@ -271,12 +271,12 @@ func (gk generateStructReprMapNb) EmitNodebuilderMethodCreateMap(w io.Writer) { mb.v.d.{{ $field.Name | titlize}} = x {{- end}} {{- if $field.IsOptional }} - mb.v.d.{{ $field.Name | titlize}}.Maybe = typed.Maybe_Value + mb.v.d.{{ $field.Name | titlize}}.Maybe = schema.Maybe_Value {{- end}} mb.{{ $field.Name }}__isset = true {{- end}} default: - return typed.ErrNoSuchField{Type: nil /*TODO:typelit*/, FieldName: ks} + return schema.ErrNoSuchField{Type: nil /*TODO:typelit*/, FieldName: ks} } return nil } @@ -307,7 +307,7 @@ func (gk generateStructReprMapNb) EmitNodebuilderMethodCreateMap(w io.Writer) { return {{ $field.Type | mungeNodebuilderConstructorIdent }}() {{- end}} default: - panic(typed.ErrNoSuchField{Type: nil /*TODO:typelit*/, FieldName: ks}) + panic(schema.ErrNoSuchField{Type: nil /*TODO:typelit*/, FieldName: ks}) } return nil } diff --git a/impl/typed/maybe.go b/schema/maybe.go similarity index 87% rename from impl/typed/maybe.go rename to schema/maybe.go index ab36fac..670a6bc 100644 --- a/impl/typed/maybe.go +++ b/schema/maybe.go @@ -1,4 +1,4 @@ -package typed +package schema type Maybe uint8 diff --git a/schema/tests/doc.go b/schema/tests/doc.go index 8b1efbb..dacc6f7 100644 --- a/schema/tests/doc.go +++ b/schema/tests/doc.go @@ -1,4 +1,4 @@ // The `schema/tests` package contains behavioral tests for type-constrained // Node implementations -- meant to work with either codegenerated Nodes OR -// with the runtime typed.Node wrappers, checking for the same behavior on each. +// with the runtime schema.TypedNode wrappers, checking for the same behavior on each. package tests diff --git a/schema/type.go b/schema/type.go index 0f5f36c..dd6c622 100644 --- a/schema/type.go +++ b/schema/type.go @@ -59,13 +59,13 @@ type Type interface { // // Note that a schema.Kind is a different enum than ipld.ReprKind; // and furthermore, there's no strict relationship between them. - // typed.Node values can be described by *two* distinct ReprKinds: + // schema.TypedNode values can be described by *two* distinct ReprKinds: // one which describes how the Node itself will act, // and another which describes how the Node presents for serialization. // For some combinations of Type and representation strategy, one or both // of the ReprKinds can be determined statically; but not always: // it can sometimes be necessary to inspect the value quite concretely - // (e.g., `typed.Node{}.Representation().ReprKind()`) in order to find + // (e.g., `schema.TypedNode{}.Representation().ReprKind()`) in order to find // out exactly how a node will be serialized! This is because some types // can vary in representation kind based on their value (specifically, // kinded-representation unions have this property). diff --git a/schema/typedNode.go b/schema/typedNode.go new file mode 100644 index 0000000..24dd00e --- /dev/null +++ b/schema/typedNode.go @@ -0,0 +1,52 @@ +package schema + +import ( + "github.com/ipld/go-ipld-prime" +) + +// schema.TypedNode is a superset of the ipld.Node interface, and has additional behaviors. +// +// A schema.TypedNode can be inspected for its schema.Type and schema.Kind, +// which conveys much more and richer information than the Data Model layer +// ipld.ReprKind. +// +// There are many different implementations of schema.TypedNode. +// One implementation can wrap any other existing ipld.Node (i.e., it's zero-copy) +// and promises that it has *already* been validated to match the typesystem.Type; +// another implementation similarly wraps any other existing ipld.Node, but +// defers to the typesystem validation checking to fields that are accessed; +// and when using code generation tools, all of the generated native Golang +// types produced by the codegen will each individually implement schema.TypedNode. +// +// Typed nodes sometimes have slightly different behaviors than plain nodes: +// For example, when looking up fields on a typed node that's a struct, +// the error returned for a lookup with a key that's not a field name will +// be ErrNoSuchField (instead of ErrNotExists). +// These behaviors apply to the schema.TypedNode only and not their representations; +// continuing the example, the .Representation().LookupString() method on +// that same node for the same key as plain `.LookupString()` will still +// return ErrNotExists, because the representation isn't a schema.TypedNode! +type TypedNode interface { + // schema.TypedNode acts just like a regular Node for almost all purposes; + // which ipld.ReprKind it acts as is determined by the TypeKind. + // (Note that the representation strategy of the type does *not* affect + // the ReprKind of schema.TypedNode -- rather, the representation strategy + // affects the `.Representation().ReprKind()`.) + // + // For example: if the `.Type().Kind()` of this node is "struct", + // it will act like ReprKind() == "map" + // (even if Type().(Struct).ReprStrategy() is "tuple"). + ipld.Node + + // Type returns a reference to the reified schema.Type value. + Type() Type + + // Representation returns an ipld.Node which sees the data in this node + // in its representation form. + // + // For example: if the `.Type().Kind()` of this node is "struct", + // `.Representation().Kind()` may vary based on its representation strategy: + // if the representation strategy is "map", then it will be ReprKind=="map"; + // if the streatgy is "tuple", then it will be ReprKind=="list". + Representation() ipld.Node +} diff --git a/schema/validate.go b/schema/validate.go index 32f51db..c946526 100644 --- a/schema/validate.go +++ b/schema/validate.go @@ -26,7 +26,7 @@ package schema --- We may also need to consider distinct reification paths: we may want one - that returns a new node tree which is eagerly converted to typed.Node + that returns a new node tree which is eagerly converted to schema.TypedNode recursively; and another that returns a lazyNode which wraps things with their typed node constraints only as they're requested. (Note that the latter would have interesting implications for any code @@ -37,8 +37,8 @@ package schema A further fun issue which needs consideration: well, I'll just save a snip of prospective docs I wrote while trying to iterate on these functions: - // Note that using Validate on a node that's already a typed.Node is likely - // to be nonsensical. In many schemas, the typed.Node tree is actually a + // Note that using Validate on a node that's already a schema.TypedNode is likely + // to be nonsensical. In many schemas, the schema.TypedNode tree is actually a // different depth than its representational tree (e.g. unions can cause this), ... and that's ... that's a fairly sizable issue that needs resolving. @@ -62,7 +62,7 @@ package schema --- And finally: both "Validate" and "Reify" methods might actually belong - in the typed.Node package -- if they make *any* reference to `typed.Node`, + in the schema.TypedNode package -- if they make *any* reference to `schema.TypedNode`, then they have no choice (otherwise, cyclic imports would occur). If we make a "Validate" that works purely on the schema.Type info, and returns *only* errors: only then we can have it in the schema package. -- GitLab