Commit de9e49b0 authored by Eric Myhre's avatar Eric Myhre

schema/compiler: move into schema package.

As with parent commit: this is a checkpoint.  CI will not be passing.
parent f1859e77
......@@ -3,12 +3,10 @@
interfaces in the schema package which are used to describe IPLD Schemas,
and it also provides a Compiler type which is used to construct them.
*/
package compiler
package schema
import (
"fmt"
"github.com/ipld/go-ipld-prime/schema"
)
// Compiler creates new TypeSystem instances.
......@@ -40,23 +38,30 @@ import (
// The TypeSystem returned by a successful Compile call will be immutable.
// Many methods on the Compiler type are structured to accept data in a way that works towards this immutability.
// In particular, many methods on Compiler take arguments which are "carrier types" for segments of immutable data,
// which must be produced by constructor functions; for one example of this pattern, see the interplay of Compiler.TypeStruct() and MakeStructFieldList().
// and these "carrier types" are produced by constructor functions.
// For one example of this pattern, see the interplay of compiler.TypeStruct() and MakeStructFieldList().
//
// On code organization:
// Several methods are attached to the Compiler type but don't actually take it as a parameter.
// (All these methods have the name prefix "Make*".)
// These methods are constructors for various intermediate values needed to feed information into the compiler.
// These are attached to the Compiler type purely for organization of the godoc,
// so that they don't clutter up the package with functions that users should never be expected to use.
type Compiler struct {
// ... and if you're wondering why this type is exported at all?
// Well, arguably, it's useful to be able to construct these values without going through the dmt.
// At the end of the day, though? Honestly, import cycle breaking. This was not the first choice.
// An implementation which wraps the schemadmt package to make it fit the schema interfaces was the first choice
// because it would've saved a *lot* of work (it would've removed the need for this compiler system entirely, among other things);
// but that doesn't fly, because the dmt types have to implement schema.Type, and that interface refers to yet more schema.* types.
// but that doesn't fly, because the dmt types have to implement Type, and that interface refers to yet more * types.
// And that would make an import cycle if we tried to put types wrapping the dmt types into the schema package. Whoops.
// So, here we are.
//
// The decision to split out this Compiler type and all its other related Make* functions
// from the schema package is largely cosmetic; it technically could've been placed in the schema package.
// However, the result of splitting the readable and writable types into packages seemed more readable,
// and gives us more elbow room in the godocs to suggest "you probably shouldn't use these directly".
// Compared to the already-forced dmt package split, having the creation stuff in one package
// and the read-only interfaces in another package just isn't much additional burden.
// A separation of this feature into its own "compiler" package was also attempted; this too did not work out well.
// We want immutability in almost all of these values, and in golang, the only way to approach immutability is with unexported symbols and package boundaries;
// there aren't many practical approaches which would allow separating the reading and the creating parts of handling the same structure into two different packages.
// One approach is to simply define all reading via interfaces. This works and is not wildly unusual in golang.
// However, to apply this in practice in this case would result in many interfaces which are just shells around exactly one implementation;
// and it would also suggest various implementations are expected, which... is simply not the case.
// So, here we are.
// ts gathers all the in-progress types (including anonymous ones),
// and is eventually the value we return (if Compile is ultimately successful).
......@@ -69,55 +74,55 @@ type Compiler struct {
func (c *Compiler) Init() {
c.ts = &TypeSystem{
map[schema.TypeReference]schema.Type{},
map[TypeReference]Type{},
nil,
}
}
func (c *Compiler) Compile() (schema.TypeSystem, error) {
func (c *Compiler) Compile() (TypeSystem, error) {
panic("TODO")
}
func (c *Compiler) addType(t schema.Type) {
func (c *Compiler) addType(t Type) {
c.mustHaveNameFree(t.Name())
c.ts.types[schema.TypeReference(t.Name())] = t
c.ts.types[TypeReference(t.Name())] = t
c.ts.list = append(c.ts.list, t)
}
func (c *Compiler) addAnonType(t schema.Type) {
c.ts.types[schema.TypeReference(t.Name())] = t // FIXME it's... probably a bug that the schema.Type.Name() method doesn't return a TypeReference. Yeah, it definitely is. TypeMap and TypeList should have their own name field internally be TypeReference, too, because it's true. wonder if we should have separate methods on the schema.Type interface for this. would probably be a usability trap to do so, though (too many user printfs would use the Name function and get blanks and be surprised).
func (c *Compiler) addAnonType(t Type) {
c.ts.types[TypeReference(t.Name())] = t // FIXME it's... probably a bug that the Type.Name() method doesn't return a TypeReference. Yeah, it definitely is. TypeMap and TypeList should have their own name field internally be TypeReference, too, because it's true. wonder if we should have separate methods on the Type interface for this. would probably be a usability trap to do so, though (too many user printfs would use the Name function and get blanks and be surprised).
}
func (c *Compiler) mustHaveNameFree(name schema.TypeName) {
if _, exists := c.ts.types[schema.TypeReference(name)]; exists {
func (c *Compiler) mustHaveNameFree(name TypeName) {
if _, exists := c.ts.types[TypeReference(name)]; exists {
panic(fmt.Errorf("type name %q already used", name))
}
}
func (c *Compiler) TypeBool(name schema.TypeName) {
func (c *Compiler) TypeBool(name TypeName) {
c.addType(&TypeBool{c.ts, name})
}
func (c *Compiler) TypeString(name schema.TypeName) {
func (c *Compiler) TypeString(name TypeName) {
c.addType(&TypeString{c.ts, name})
}
func (c *Compiler) TypeBytes(name schema.TypeName) {
func (c *Compiler) TypeBytes(name TypeName) {
c.addType(&TypeBytes{c.ts, name})
}
func (c *Compiler) TypeInt(name schema.TypeName) {
func (c *Compiler) TypeInt(name TypeName) {
c.addType(&TypeInt{c.ts, name})
}
func (c *Compiler) TypeFloat(name schema.TypeName) {
func (c *Compiler) TypeFloat(name TypeName) {
c.addType(&TypeFloat{c.ts, name})
}
func (c *Compiler) TypeLink(name schema.TypeName, expectedTypeRef schema.TypeName) {
func (c *Compiler) TypeLink(name TypeName, expectedTypeRef TypeName) {
c.addType(&TypeLink{c.ts, name, expectedTypeRef})
}
func (c *Compiler) TypeStruct(name schema.TypeName, fields structFieldList, rstrat StructRepresentation) {
func (c *Compiler) TypeStruct(name TypeName, fields structFieldList, rstrat StructRepresentation) {
t := TypeStruct{
ts: c.ts,
name: name,
......@@ -142,10 +147,10 @@ type structFieldList struct {
x []StructField
}
func MakeStructFieldList(fields ...StructField) structFieldList {
func (Compiler) MakeStructFieldList(fields ...StructField) structFieldList {
return structFieldList{fields}
}
func MakeStructField(name StructFieldName, typ schema.TypeReference, optional, nullable bool) StructField {
func (Compiler) MakeStructField(name StructFieldName, typ TypeReference, optional, nullable bool) StructField {
return StructField{nil, name, typ, optional, nullable}
}
......@@ -169,15 +174,15 @@ type StructRepresentation_Map_FieldDetailsEntry struct {
Details StructRepresentation_Map_FieldDetails
}
func (c *Compiler) TypeMap(name schema.TypeName, keyTypeRef schema.TypeName, valueTypeRef schema.TypeReference, valueNullable bool) {
func (c *Compiler) TypeMap(name TypeName, keyTypeRef TypeName, valueTypeRef TypeReference, valueNullable bool) {
c.addType(&TypeMap{c.ts, name, keyTypeRef, valueTypeRef, valueNullable})
}
func (c *Compiler) TypeList(name schema.TypeName, valueTypeRef schema.TypeReference, valueNullable bool) {
func (c *Compiler) TypeList(name TypeName, valueTypeRef TypeReference, valueNullable bool) {
c.addType(&TypeList{c.ts, name, valueTypeRef, valueNullable})
}
func (c *Compiler) TypeUnion(name schema.TypeName, members unionMemberList, rstrat UnionRepresentation) {
func (c *Compiler) TypeUnion(name TypeName, members unionMemberList, rstrat UnionRepresentation) {
t := TypeUnion{
ts: c.ts,
name: name,
......@@ -193,25 +198,25 @@ func (c *Compiler) TypeUnion(name schema.TypeName, members unionMemberList, rstr
// unionMemberList is a carrier type that just wraps a slice reference.
// It is used so we can let code outside this package hold a value of this type without letting the slice become mutable.
type unionMemberList struct {
x []schema.TypeName
x []TypeName
}
func MakeUnionMemberList(members ...schema.TypeName) unionMemberList {
func (Compiler) MakeUnionMemberList(members ...TypeName) unionMemberList {
return unionMemberList{members}
}
func MakeUnionRepresentation_Keyed(discriminantTable unionDiscriminantStringTable) UnionRepresentation {
func (Compiler) MakeUnionRepresentation_Keyed(discriminantTable unionDiscriminantStringTable) UnionRepresentation {
return &UnionRepresentation_Keyed{nil, discriminantTable.x}
}
// unionMemberList is a carrier type that just wraps a map reference.
// It is used so we can let code outside this package hold a value of this type without letting the map become mutable.
type unionDiscriminantStringTable struct {
x map[string]schema.TypeName
x map[string]TypeName
}
func MakeUnionDiscriminantStringTable(entries ...UnionDiscriminantStringEntry) unionDiscriminantStringTable {
x := make(map[string]schema.TypeName, len(entries))
func (Compiler) MakeUnionDiscriminantStringTable(entries ...UnionDiscriminantStringEntry) unionDiscriminantStringTable {
x := make(map[string]TypeName, len(entries))
for _, y := range entries {
if _, exists := x[y.Discriminant]; exists {
panic(fmt.Errorf("discriminant string %q duplicated", y.Discriminant))
......@@ -226,5 +231,5 @@ func MakeUnionDiscriminantStringTable(entries ...UnionDiscriminantStringEntry) u
// can build their results without exposing a reference to a map in a way that would make that map mutable.
type UnionDiscriminantStringEntry struct {
Discriminant string
Member schema.TypeName
Member TypeName
}
package compiler
import (
"github.com/ipld/go-ipld-prime/schema"
)
type TypeSystem struct {
// Mind the key type here: TypeReference, not TypeName.
// The key might be a computed anon "name" which is not actually a valid type name itself.
types map[schema.TypeReference]schema.Type
// List of types, retained in the original order they were specified,
// including only those which are named (not any computed anonymous types).
// This is kept so we can do any listing in the order the user expects,
// report any errors during rule validation in the same order as the input, etc.
list []schema.Type
}
......@@ -2,7 +2,6 @@ package schemadmt
import (
"github.com/ipld/go-ipld-prime/schema"
"github.com/ipld/go-ipld-prime/schema/compiler"
)
// This code is broken up into a bunch of individual 'compile' methods,
......@@ -12,7 +11,7 @@ import (
// creating a separate interface per result type seems just not super relevant.
func (schdmt Schema) Compile() (schema.TypeSystem, error) {
c := &compiler.Compiler{}
c := &schema.Compiler{}
typesdmt := schdmt.FieldTypes()
for itr := typesdmt.Iterator(); !itr.Done(); {
tn, t := itr.Next()
......@@ -52,11 +51,11 @@ func (schdmt Schema) Compile() (schema.TypeSystem, error) {
t2.FieldValueType().compile(c)
case TypeStruct:
// Flip fields info from DMT to compiler argument format.
fields := make([]compiler.StructField, t2.FieldFields().Length())
fields := make([]schema.StructField, t2.FieldFields().Length())
for itr := t2.FieldFields().Iterator(); !itr.Done(); {
fname, fdmt := itr.Next()
fields = append(fields, compiler.MakeStructField(
compiler.StructFieldName(fname.String()),
fields = append(fields, schema.Compiler{}.MakeStructField(
schema.StructFieldName(fname.String()),
fdmt.FieldType().TypeReference(),
fdmt.FieldOptional().Bool(),
fdmt.FieldNullable().Bool(),
......@@ -65,7 +64,7 @@ func (schdmt Schema) Compile() (schema.TypeSystem, error) {
fdmt.FieldType().compile(c)
}
// Flip the representaton strategy DMT to compiler argument format.
rstrat := func() compiler.StructRepresentation {
rstrat := func() schema.StructRepresentation {
switch r := t2.FieldRepresentation().AsInterface().(type) {
case StructRepresentation_Map:
return r.compile()
......@@ -84,7 +83,7 @@ func (schdmt Schema) Compile() (schema.TypeSystem, error) {
// Feed it all into the compiler.
c.TypeStruct(
schema.TypeName(tn.String()),
compiler.MakeStructFieldList(fields...),
schema.Compiler{}.MakeStructFieldList(fields...),
rstrat,
)
case TypeUnion:
......@@ -96,7 +95,7 @@ func (schdmt Schema) Compile() (schema.TypeSystem, error) {
// n.b. no need to check for TypeDefnInline here, because schemas don't allow those in union defns.
}
// Flip the representaton strategy DMT to compiler argument format.
rstrat := func() compiler.UnionRepresentation {
rstrat := func() schema.UnionRepresentation {
switch r := t2.FieldRepresentation().AsInterface().(type) {
case UnionRepresentation_Keyed:
return r.compile()
......@@ -117,7 +116,7 @@ func (schdmt Schema) Compile() (schema.TypeSystem, error) {
// Feed it all into the compiler.
c.TypeUnion(
schema.TypeName(tn.String()),
compiler.MakeUnionMemberList(members...),
schema.Compiler{}.MakeUnionMemberList(members...),
rstrat,
)
case TypeEnum:
......@@ -133,23 +132,23 @@ func (schdmt Schema) Compile() (schema.TypeSystem, error) {
// If the typeReference is TypeDefnInline, create the anonymous type and feed it to the compiler.
// It's fine if anonymous type has been seen before; we let dedup of that be handled by the compiler.
func (dmt TypeNameOrInlineDefn) compile(c *compiler.Compiler) {
func (dmt TypeNameOrInlineDefn) compile(c *schema.Compiler) {
switch dmt.AsInterface().(type) {
case TypeDefnInline:
panic("nyi") // TODO this needs to engage in anonymous type spawning.
}
}
func (dmt StructRepresentation_Map) compile() compiler.StructRepresentation {
func (dmt StructRepresentation_Map) compile() schema.StructRepresentation {
if !dmt.FieldFields().Exists() {
return compiler.MakeStructRepresentation_Map()
return schema.MakeStructRepresentation_Map()
}
fields := make([]compiler.StructRepresentation_Map_FieldDetailsEntry, dmt.FieldFields().Must().Length())
fields := make([]schema.StructRepresentation_Map_FieldDetailsEntry, dmt.FieldFields().Must().Length())
for itr := dmt.FieldFields().Must().Iterator(); !itr.Done(); {
fn, det := itr.Next()
fields = append(fields, compiler.StructRepresentation_Map_FieldDetailsEntry{
FieldName: compiler.StructFieldName(fn.String()),
Details: compiler.StructRepresentation_Map_FieldDetails{
fields = append(fields, schema.StructRepresentation_Map_FieldDetailsEntry{
FieldName: schema.StructFieldName(fn.String()),
Details: schema.StructRepresentation_Map_FieldDetails{
Rename: func() string {
if det.FieldRename().Exists() {
return det.FieldRename().Must().String()
......@@ -160,50 +159,50 @@ func (dmt StructRepresentation_Map) compile() compiler.StructRepresentation {
},
})
}
return compiler.MakeStructRepresentation_Map(fields...)
return schema.MakeStructRepresentation_Map(fields...)
}
func (dmt StructRepresentation_Tuple) compile() compiler.StructRepresentation {
func (dmt StructRepresentation_Tuple) compile() schema.StructRepresentation {
panic("TODO")
}
func (dmt StructRepresentation_Stringpairs) compile() compiler.StructRepresentation {
func (dmt StructRepresentation_Stringpairs) compile() schema.StructRepresentation {
panic("TODO")
}
func (dmt StructRepresentation_Stringjoin) compile() compiler.StructRepresentation {
func (dmt StructRepresentation_Stringjoin) compile() schema.StructRepresentation {
panic("TODO")
}
func (dmt StructRepresentation_Listpairs) compile() compiler.StructRepresentation {
func (dmt StructRepresentation_Listpairs) compile() schema.StructRepresentation {
panic("TODO")
}
func (dmt UnionRepresentation_Keyed) compile() compiler.UnionRepresentation {
ents := make([]compiler.UnionDiscriminantStringEntry, 0, dmt.Length())
func (dmt UnionRepresentation_Keyed) compile() schema.UnionRepresentation {
ents := make([]schema.UnionDiscriminantStringEntry, 0, dmt.Length())
for itr := dmt.Iterator(); !itr.Done(); {
k, v := itr.Next()
ents = append(ents, compiler.UnionDiscriminantStringEntry{k.String(), schema.TypeName(v.String())})
ents = append(ents, schema.UnionDiscriminantStringEntry{k.String(), schema.TypeName(v.String())})
}
return compiler.MakeUnionRepresentation_Keyed(compiler.MakeUnionDiscriminantStringTable(ents...))
return schema.Compiler{}.MakeUnionRepresentation_Keyed(schema.Compiler{}.MakeUnionDiscriminantStringTable(ents...))
}
func (dmt UnionRepresentation_Kinded) compile() compiler.UnionRepresentation {
func (dmt UnionRepresentation_Kinded) compile() schema.UnionRepresentation {
panic("TODO")
}
func (dmt UnionRepresentation_Envelope) compile() compiler.UnionRepresentation {
func (dmt UnionRepresentation_Envelope) compile() schema.UnionRepresentation {
panic("TODO")
}
func (dmt UnionRepresentation_Inline) compile() compiler.UnionRepresentation {
func (dmt UnionRepresentation_Inline) compile() schema.UnionRepresentation {
panic("TODO")
}
func (dmt UnionRepresentation_StringPrefix) compile() compiler.UnionRepresentation {
func (dmt UnionRepresentation_StringPrefix) compile() schema.UnionRepresentation {
panic("TODO")
}
func (dmt UnionRepresentation_BytePrefix) compile() compiler.UnionRepresentation {
func (dmt UnionRepresentation_BytePrefix) compile() schema.UnionRepresentation {
panic("TODO")
}
package schema
......@@ -55,7 +55,7 @@ type TypeReference string
// other kind of Type.)
type Type interface {
// Returns a pointer to the TypeSystem this Type is a member of.
TypeSystem() TypeSystem
TypeSystem() *TypeSystem
// Returns the string name of the Type. This name is unique within the
// universe this type is a member of, *unless* this type is Anonymous,
......
package compiler
package schema
import (
"github.com/ipld/go-ipld-prime"
"github.com/ipld/go-ipld-prime/schema"
)
type TypeBool struct {
ts *TypeSystem
name schema.TypeName
name TypeName
}
// -- schema.Type interface satisfaction -->
// -- Type interface satisfaction -->
var _ schema.Type = (*TypeBool)(nil)
var _ Type = (*TypeBool)(nil)
func (t *TypeBool) TypeSystem() schema.TypeSystem {
func (t *TypeBool) TypeSystem() *TypeSystem {
return t.ts
}
func (TypeBool) TypeKind() schema.TypeKind {
return schema.TypeKind_Bool
func (TypeBool) TypeKind() TypeKind {
return TypeKind_Bool
}
func (t *TypeBool) Name() schema.TypeName {
func (t *TypeBool) Name() TypeName {
return t.name
}
......
package compiler
package schema
import (
"github.com/ipld/go-ipld-prime"
"github.com/ipld/go-ipld-prime/schema"
)
type TypeBytes struct {
ts *TypeSystem
name schema.TypeName
name TypeName
}
// -- schema.Type interface satisfaction -->
// -- Type interface satisfaction -->
var _ schema.Type = (*TypeBytes)(nil)
var _ Type = (*TypeBytes)(nil)
func (t *TypeBytes) TypeSystem() schema.TypeSystem {
func (t *TypeBytes) TypeSystem() *TypeSystem {
return t.ts
}
func (TypeBytes) TypeKind() schema.TypeKind {
return schema.TypeKind_Bytes
func (TypeBytes) TypeKind() TypeKind {
return TypeKind_Bytes
}
func (t *TypeBytes) Name() schema.TypeName {
func (t *TypeBytes) Name() TypeName {
return t.name
}
......
package compiler
package schema
import (
"github.com/ipld/go-ipld-prime"
"github.com/ipld/go-ipld-prime/schema"
)
type TypeFloat struct {
ts *TypeSystem
name schema.TypeName
name TypeName
}
// -- schema.Type interface satisfaction -->
// -- Type interface satisfaction -->
var _ schema.Type = (*TypeFloat)(nil)
var _ Type = (*TypeFloat)(nil)
func (t *TypeFloat) TypeSystem() schema.TypeSystem {
func (t *TypeFloat) TypeSystem() *TypeSystem {
return t.ts
}
func (TypeFloat) TypeKind() schema.TypeKind {
return schema.TypeKind_Float
func (TypeFloat) TypeKind() TypeKind {
return TypeKind_Float
}
func (t *TypeFloat) Name() schema.TypeName {
func (t *TypeFloat) Name() TypeName {
return t.name
}
......
package compiler
package schema
import (
"github.com/ipld/go-ipld-prime"
"github.com/ipld/go-ipld-prime/schema"
)
type TypeInt struct {
ts *TypeSystem
name schema.TypeName
name TypeName
}
// -- schema.Type interface satisfaction -->
// -- Type interface satisfaction -->
var _ schema.Type = (*TypeInt)(nil)
var _ Type = (*TypeInt)(nil)
func (t *TypeInt) TypeSystem() schema.TypeSystem {
func (t *TypeInt) TypeSystem() *TypeSystem {
return t.ts
}
func (TypeInt) TypeKind() schema.TypeKind {
return schema.TypeKind_Int
func (TypeInt) TypeKind() TypeKind {
return TypeKind_Int
}
func (t *TypeInt) Name() schema.TypeName {
func (t *TypeInt) Name() TypeName {
return t.name
}
......
package compiler
package schema
import (
"github.com/ipld/go-ipld-prime"
"github.com/ipld/go-ipld-prime/schema"
)
type TypeLink struct {
ts *TypeSystem
name schema.TypeName
expectedTypeRef schema.TypeName // can be empty
name TypeName
expectedTypeRef TypeName // can be empty
}
// -- schema.Type interface satisfaction -->
// -- Type interface satisfaction -->
var _ schema.Type = (*TypeLink)(nil)
var _ Type = (*TypeLink)(nil)
func (t *TypeLink) TypeSystem() schema.TypeSystem {
func (t *TypeLink) TypeSystem() *TypeSystem {
return t.ts
}
func (TypeLink) TypeKind() schema.TypeKind {
return schema.TypeKind_Link
func (TypeLink) TypeKind() TypeKind {
return TypeKind_Link
}
func (t *TypeLink) Name() schema.TypeName {
func (t *TypeLink) Name() TypeName {
return t.name
}
......@@ -41,9 +40,9 @@ func (t *TypeLink) HasExpectedType() bool {
// ExpectedType returns the type which is expected for the node on the other side of the link.
// Nil is returned if there is no information about the expected type
// (which may be interpreted as "any").
func (t *TypeLink) ExpectedType() schema.Type {
func (t *TypeLink) ExpectedType() Type {
if !t.HasExpectedType() {
return nil
}
return t.ts.types[schema.TypeReference(t.expectedTypeRef)]
return t.ts.types[TypeReference(t.expectedTypeRef)]
}
package compiler
package schema
import (
"github.com/ipld/go-ipld-prime"
"github.com/ipld/go-ipld-prime/schema"
)
type TypeList struct {
ts *TypeSystem
name schema.TypeName
valueTypeRef schema.TypeReference
name TypeName
valueTypeRef TypeReference
valueNullable bool
}
// -- schema.Type interface satisfaction -->
// -- Type interface satisfaction -->
var _ schema.Type = (*TypeList)(nil)
var _ Type = (*TypeList)(nil)
func (t *TypeList) TypeSystem() schema.TypeSystem {
func (t *TypeList) TypeSystem() *TypeSystem {
return t.ts
}
func (TypeList) TypeKind() schema.TypeKind {
return schema.TypeKind_List
func (TypeList) TypeKind() TypeKind {
return TypeKind_List
}
func (t *TypeList) Name() schema.TypeName {
func (t *TypeList) Name() TypeName {
return t.name
}
......@@ -35,8 +34,8 @@ func (t TypeList) RepresentationBehavior() ipld.Kind {
// -- specific to TypeList -->
// ValueType returns the Type of the list values.
func (t *TypeList) ValueType() schema.Type {
return t.ts.types[schema.TypeReference(t.valueTypeRef)]
func (t *TypeList) ValueType() Type {
return t.ts.types[TypeReference(t.valueTypeRef)]
}
// ValueIsNullable returns a bool describing if the list values are permitted to be null.
......
package compiler
package schema
import (
"github.com/ipld/go-ipld-prime"
"github.com/ipld/go-ipld-prime/schema"
)
type TypeMap struct {
ts *TypeSystem
name schema.TypeName
keyTypeRef schema.TypeName // is a TypeName and not a TypeReference because it can't be an anon.
valueTypeRef schema.TypeReference
name TypeName
keyTypeRef TypeName // is a TypeName and not a TypeReference because it can't be an anon.
valueTypeRef TypeReference
valueNullable bool
}
// -- schema.Type interface satisfaction -->
// -- Type interface satisfaction -->
var _ schema.Type = (*TypeMap)(nil)
var _ Type = (*TypeMap)(nil)
func (t *TypeMap) TypeSystem() schema.TypeSystem {
func (t *TypeMap) TypeSystem() *TypeSystem {
return t.ts
}
func (TypeMap) TypeKind() schema.TypeKind {
return schema.TypeKind_Map
func (TypeMap) TypeKind() TypeKind {
return TypeKind_Map
}
func (t *TypeMap) Name() schema.TypeName {
func (t *TypeMap) Name() TypeName {
return t.name
}
......@@ -41,13 +40,13 @@ func (t TypeMap) RepresentationBehavior() ipld.Kind {
// string in the IPLD Data Model (e.g. any string type is valid,
// but something with enum typekind and a string representation is also valid,
// and a struct typekind with a representation that has a string kind is also valid, etc).
func (t *TypeMap) KeyType() schema.Type {
return t.ts.types[schema.TypeReference(t.keyTypeRef)]
func (t *TypeMap) KeyType() Type {
return t.ts.types[TypeReference(t.keyTypeRef)]
}
// ValueType returns the Type of the map values.
func (t *TypeMap) ValueType() schema.Type {
return t.ts.types[schema.TypeReference(t.valueTypeRef)]
func (t *TypeMap) ValueType() Type {
return t.ts.types[TypeReference(t.valueTypeRef)]
}
// ValueIsNullable returns a bool describing if the map values are permitted to be null.
......
package compiler
package schema
import (
"github.com/ipld/go-ipld-prime"
"github.com/ipld/go-ipld-prime/schema"
)
type TypeString struct {
ts *TypeSystem
name schema.TypeName
name TypeName
}
// -- schema.Type interface satisfaction -->
// -- Type interface satisfaction -->
var _ schema.Type = (*TypeString)(nil)
var _ Type = (*TypeString)(nil)
func (t *TypeString) TypeSystem() schema.TypeSystem {
func (t *TypeString) TypeSystem() *TypeSystem {
return t.ts
}
func (TypeString) TypeKind() schema.TypeKind {
return schema.TypeKind_String
func (TypeString) TypeKind() TypeKind {
return TypeKind_String
}
func (t *TypeString) Name() schema.TypeName {
func (t *TypeString) Name() TypeName {
return t.name
}
......
package compiler
package schema
import (
"github.com/ipld/go-ipld-prime"
"github.com/ipld/go-ipld-prime/schema"
)
type TypeStruct struct {
ts *TypeSystem
name schema.TypeName
name TypeName
fields []StructField
fieldsMap map[StructFieldName]*StructField // same content, indexed for lookup.
rstrat StructRepresentation
......@@ -16,7 +15,7 @@ type TypeStruct struct {
type StructField struct {
parent *TypeStruct // a pointer back up is used so we can provide the method that gives a reified type instead of just the TypeReference.
name StructFieldName
typeRef schema.TypeReference
typeRef TypeReference
optional bool
nullable bool
}
......@@ -57,19 +56,19 @@ type StructRepresentation_Stringjoin struct {
type StructRepresentation_Listpairs struct {
}
// -- schema.Type interface satisfaction -->
// -- Type interface satisfaction -->
var _ schema.Type = (*TypeStruct)(nil)
var _ Type = (*TypeStruct)(nil)
func (t *TypeStruct) TypeSystem() schema.TypeSystem {
func (t *TypeStruct) TypeSystem() *TypeSystem {
return t.ts
}
func (TypeStruct) TypeKind() schema.TypeKind {
return schema.TypeKind_Struct
func (TypeStruct) TypeKind() TypeKind {
return TypeKind_Struct
}
func (t *TypeStruct) Name() schema.TypeName {
func (t *TypeStruct) Name() TypeName {
return t.name
}
......@@ -123,7 +122,7 @@ func (f *StructField) Name() StructFieldName { return f.name }
// Type returns the Type of this field's value. Note the field may
// also be unset if it is either Optional or Nullable.
func (f *StructField) Type() schema.Type { return f.parent.ts.types[f.typeRef] }
func (f *StructField) Type() Type { return f.parent.ts.types[f.typeRef] }
// IsOptional returns true if the field is allowed to be absent from the object.
// If IsOptional is false, the field may be absent from the serial representation
......
package compiler
package schema
import (
"github.com/ipld/go-ipld-prime"
"github.com/ipld/go-ipld-prime/schema"
)
type TypeUnion struct {
ts *TypeSystem
name schema.TypeName
members []schema.TypeName // all of these are TypeName because we ruled by fiat that unions are not allowed to use anon types (for sheer syntactic complexity boundary reasons).
name TypeName
members []TypeName // all of these are TypeName because we ruled by fiat that unions are not allowed to use anon types (for sheer syntactic complexity boundary reasons).
rstrat UnionRepresentation
}
......@@ -23,45 +22,45 @@ func (UnionRepresentation_BytePrefix) _UnionRepresentation() {}
type UnionRepresentation_Keyed struct {
ts *TypeSystem
discriminantTable map[string]schema.TypeName
discriminantTable map[string]TypeName
}
type UnionRepresentation_Kinded struct {
ts *TypeSystem
discriminantTable map[ipld.Kind]schema.TypeName
discriminantTable map[ipld.Kind]TypeName
}
type UnionRepresentation_Envelope struct {
ts *TypeSystem
discriminantKey string
contentKey string
discriminantTable map[string]schema.TypeName
discriminantTable map[string]TypeName
}
type UnionRepresentation_Inline struct {
ts *TypeSystem
discriminantKey string
discriminantTable map[string]schema.TypeName
discriminantTable map[string]TypeName
}
type UnionRepresentation_StringPrefix struct {
ts *TypeSystem
discriminantTable map[string]schema.TypeName
discriminantTable map[string]TypeName
}
type UnionRepresentation_BytePrefix struct {
ts *TypeSystem
discriminantTable map[string]schema.TypeName
discriminantTable map[string]TypeName
}
// -- schema.Type interface satisfaction -->
// -- Type interface satisfaction -->
var _ schema.Type = (*TypeUnion)(nil)
var _ Type = (*TypeUnion)(nil)
func (t *TypeUnion) TypeSystem() schema.TypeSystem {
func (t *TypeUnion) TypeSystem() *TypeSystem {
return t.ts
}
func (TypeUnion) TypeKind() schema.TypeKind {
return schema.TypeKind_Union
func (TypeUnion) TypeKind() TypeKind {
return TypeKind_Union
}
func (t *TypeUnion) Name() schema.TypeName {
func (t *TypeUnion) Name() TypeName {
return t.name
}
......@@ -92,7 +91,7 @@ func (t *TypeUnion) RepresentationStrategy() UnionRepresentation {
// GetDiscriminantForType looks up the discriminant key for the given type.
// It panics if the given type is not a member of this union.
func (r UnionRepresentation_Keyed) GetDiscriminantForType(t schema.Type) string {
func (r UnionRepresentation_Keyed) GetDiscriminantForType(t Type) string {
if t.TypeSystem() != r.ts {
panic("that type isn't even from the same universe!")
}
......@@ -106,9 +105,9 @@ func (r UnionRepresentation_Keyed) GetDiscriminantForType(t schema.Type) string
// GetMember returns the type info for the member that would be indicated by the given kind,
// or may return nil if that kind is not mapped to a member of this union.
func (r UnionRepresentation_Kinded) GetMember(k ipld.Kind) schema.Type {
func (r UnionRepresentation_Kinded) GetMember(k ipld.Kind) Type {
if tn, exists := r.discriminantTable[k]; exists {
return r.ts.types[schema.TypeReference(tn)]
return r.ts.types[TypeReference(tn)]
}
return nil
}
package schema
type TypeSystem interface {
// FIXME accessor methods will reappear here (just hasn't been forced by the progress of the refactor yet)
type TypeSystem struct {
// Mind the key type here: TypeReference, not TypeName.
// The key might be a computed anon "name" which is not actually a valid type name itself.
types map[TypeReference]Type
// List of types, retained in the original order they were specified,
// including only those which are named (not any computed anonymous types).
// This is kept so we can do any listing in the order the user expects,
// report any errors during rule validation in the same order as the input, etc.
list []Type
}
package schema
/*
Okay, so. There are several fun considerations for a "validate" method.
---
There's two radically different approaches to "validate"/"reify":
- Option 1: Look at the schema.Type info and check if a data node seems
to match it -- recursing on the type info.
- Option 2: Use the schema.Type{}.RepresentationNodeBuilder() to feed data
into it -- recursing on what the nodebuilder already expresses.
(Option 2 also need to take a `memStorage ipld.NodeBuilder` param, btw,
for handling all the cases where we *aren't* doing codegen.)
Option 1 provides a little more opportunity for returning multiple errors.
Option 2 will generally have a hard time with that (nodebuilers are not
necessarily in a valid state after their first error encounter).
As a result of having these two options at all, we may indeed end up with
at least two very different functions -- despite seeming to do similar
things, their interior will radically diverge.
---
We may also need to consider distinct reification paths: we may want one
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
which has expectations about pointer equality consistency.)
---
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 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.
There's a couple of different ways to handle some of the behaviors around
unions, and some of them make the tradeoff described above, and I'm really
unsure if all the implications have been sussed out yet. We should defer
writing code that depends on this issue until gathering some more info.
---
One more note: about returning multiple errors from a Validate function:
there's an upper bound of the utility of the thing. Going farther than the
first parse error is nice, but it will still hit limits: for example,
upon encountering a union and failing to match it, we can't generally
produce further errors from anywhere deeper in the tree without them being
combinatorial "if previous juncture X was type Y, then..." nonsense.
(This applies to all recursive kinds to some degree, but it's especially
rough with unions. For most of the others, it's flatly a missing field,
or an excessive field, or a leaf error; with unions it can be hard to tell.)
---
And finally: both "Validate" and "Reify" methods might actually belong
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.
*/
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment