Skip to content

Commit

Permalink
feat(schema): expose useful functions for building schemas
Browse files Browse the repository at this point in the history
add more minimal form of schema compilation and useful functions for merging one type system into
another.

fix #568
  • Loading branch information
hannahhoward committed Aug 2, 2024
1 parent 198d7db commit 8f7edab
Show file tree
Hide file tree
Showing 2 changed files with 111 additions and 29 deletions.
53 changes: 24 additions & 29 deletions schema/dmt/compile.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,37 +27,14 @@ import (
// It supports several validations for logical coherency of schemas,
// but may not yet successfully reject all invalid schemas.
func Compile(ts *schema.TypeSystem, node *Schema) error {
// Prelude; probably belongs elsewhere.
{
ts.Accumulate(schema.SpawnBool("Bool"))
ts.Accumulate(schema.SpawnInt("Int"))
ts.Accumulate(schema.SpawnFloat("Float"))
ts.Accumulate(schema.SpawnString("String"))
ts.Accumulate(schema.SpawnBytes("Bytes"))
// Add basic types
schema.SpawnDefaultBasicTypes(ts)

ts.Accumulate(schema.SpawnAny("Any"))

ts.Accumulate(schema.SpawnMap("Map", "String", "Any", false))
ts.Accumulate(schema.SpawnList("List", "Any", false))

// Should be &Any, really.
ts.Accumulate(schema.SpawnLink("Link"))

// TODO: schema package lacks support?
// ts.Accumulate(schema.SpawnUnit("Null", NullRepr))
// Add the schema types
err := SpawnSchemaTypes(ts, node)
if err != nil {
return err
}

for _, name := range node.Types.Keys {
defn := node.Types.Values[name]

// TODO: once ./schema supports anonymous/inline types, remove the ts argument.
typ, err := spawnType(ts, name, defn)
if err != nil {
return err
}
ts.Accumulate(typ)
}

// TODO: if this fails and the user forgot to check Compile's returned error,
// we can leave the TypeSystem in an unfortunate broken state:
// they can obtain types out of the TypeSystem and they are non-nil,
Expand All @@ -73,6 +50,24 @@ func Compile(ts *schema.TypeSystem, node *Schema) error {
return nil
}

// SpawnSchemaTypes is a lighter verion of compile that doesn't add basic types and doesn't validate the graph --
// for use when you want to build a type system from multiple sources and validate later
func SpawnSchemaTypes(ts *schema.TypeSystem, node *Schema) error {

for _, name := range node.Types.Keys {
defn := node.Types.Values[name]

// TODO: once ./schema supports anonymous/inline types, remove the ts argument.
typ, err := spawnType(ts, name, defn)
if err != nil {
return err
}
ts.Accumulate(typ)
}

return nil
}

// Note that the parser and compiler support defaults. We're lacking support in bindnode.
func todoFromImplicitlyFalseBool(b *bool) bool {
if b == nil {
Expand Down
87 changes: 87 additions & 0 deletions schema/tmpBuilders.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,93 @@ func SpawnEnum(name TypeName, members []string, repr EnumRepresentation) *TypeEn
return &TypeEnum{typeBase{name, nil}, members, repr}
}

// Utility function adding default basic types to schema type system
func SpawnDefaultBasicTypes(ts *TypeSystem) {
ts.Accumulate(SpawnBool("Bool"))
ts.Accumulate(SpawnInt("Int"))
ts.Accumulate(SpawnFloat("Float"))
ts.Accumulate(SpawnString("String"))
ts.Accumulate(SpawnBytes("Bytes"))

ts.Accumulate(SpawnAny("Any"))

ts.Accumulate(SpawnMap("Map", "String", "Any", false))
ts.Accumulate(SpawnList("List", "Any", false))

// Should be &Any, really.
ts.Accumulate(SpawnLink("Link"))

// TODO: schema package lacks support?
// ts.Accumulate(schema.SpawnUnit("Null", NullRepr))
}

// Clone creates a copy of a type that is not in the original's universe (so it can be used elsewhere safely)
func Clone(typ Type) Type {
switch kindedType := typ.(type) {
case *TypeBool:
return SpawnBool(kindedType.Name())
case *TypeString:
return SpawnString(kindedType.Name())
case *TypeBytes:
return SpawnBytes(kindedType.Name())
case *TypeInt:
return SpawnInt(kindedType.Name())
case *TypeFloat:
return SpawnFloat(kindedType.Name())
case *TypeAny:
return SpawnAny(kindedType.Name())
case *TypeMap:
return SpawnMap(kindedType.Name(),
kindedType.KeyType().Name(),
kindedType.ValueType().Name(),
kindedType.ValueIsNullable())
case *TypeList:
return SpawnList(kindedType.Name(), kindedType.ValueType().Name(), kindedType.ValueIsNullable())
case *TypeLink:
if kindedType.HasReferencedType() {
return SpawnLinkReference(kindedType.Name(), kindedType.ReferencedType().Name())
} else {
return SpawnLink(kindedType.Name())
}
case *TypeUnion:
members := kindedType.Members()
memberNames := make([]TypeName, 0, len(members))
for _, member := range members {
memberNames = append(memberNames, member.Name())
}
return SpawnUnion(kindedType.Name(), memberNames, kindedType.RepresentationStrategy())
case *TypeStruct:
oldFields := kindedType.Fields()
newFields := make([]StructField, 0, len(oldFields))
for _, oldField := range oldFields {
newFields = append(newFields, SpawnStructField(oldField.Name(), oldField.Type().Name(), oldField.IsOptional(), oldField.IsNullable()))
}
return SpawnStruct(kindedType.Name(), newFields, kindedType.RepresentationStrategy())
case *TypeEnum:
return SpawnEnum(kindedType.Name(), kindedType.Members(), kindedType.RepresentationStrategy())
default:
panic("unexpected type, don't know how to clone")

Check warning on line 217 in schema/tmpBuilders.go

View check run for this annotation

Codecov / codecov/patch

schema/tmpBuilders.go#L173-L217

Added lines #L173 - L217 were not covered by tests
}
}

func MergeTypeSystem(target *TypeSystem, source *TypeSystem, ignoreDups bool) {
for _, name := range source.Names() {
typ := Clone(source.TypeByName(name))
if ignoreDups {
accumulateWithRecovery(target, typ)
} else {
target.Accumulate(typ)
}

Check warning on line 228 in schema/tmpBuilders.go

View check run for this annotation

Codecov / codecov/patch

schema/tmpBuilders.go#L221-L228

Added lines #L221 - L228 were not covered by tests
}
}

func accumulateWithRecovery(ts *TypeSystem, typ Type) {
defer func() {
_ = recover()
}()
ts.Accumulate(typ)

Check warning on line 236 in schema/tmpBuilders.go

View check run for this annotation

Codecov / codecov/patch

schema/tmpBuilders.go#L232-L236

Added lines #L232 - L236 were not covered by tests
}

// The methods relating to TypeSystem are also mutation-heavy and placeholdery.

func (ts *TypeSystem) Init() {
Expand Down

0 comments on commit 8f7edab

Please sign in to comment.