144 lines
3.4 KiB
Go
144 lines
3.4 KiB
Go
package schema
|
|
|
|
import (
|
|
"fmt"
|
|
"sort"
|
|
)
|
|
|
|
// ModuleSchema represents the logical schema of a module for purposes of indexing and querying.
|
|
type ModuleSchema struct {
|
|
types map[string]Type
|
|
}
|
|
|
|
// NewModuleSchema constructs a new ModuleSchema and validates it. Any module schema returned without an error
|
|
// is guaranteed to be valid.
|
|
func NewModuleSchema(objectTypes []ObjectType) (ModuleSchema, error) {
|
|
types := map[string]Type{}
|
|
|
|
for _, objectType := range objectTypes {
|
|
types[objectType.Name] = objectType
|
|
}
|
|
|
|
res := ModuleSchema{types: types}
|
|
|
|
// validate adds all enum types to the type map
|
|
err := res.Validate()
|
|
if err != nil {
|
|
return ModuleSchema{}, err
|
|
}
|
|
|
|
return res, nil
|
|
}
|
|
|
|
func addEnumType(types map[string]Type, field Field) error {
|
|
enumDef := field.EnumType
|
|
if enumDef.Name == "" {
|
|
return nil
|
|
}
|
|
|
|
existing, ok := types[enumDef.Name]
|
|
if !ok {
|
|
types[enumDef.Name] = enumDef
|
|
return nil
|
|
}
|
|
|
|
existingEnum, ok := existing.(EnumType)
|
|
if !ok {
|
|
return fmt.Errorf("enum %q already exists as a different non-enum type", enumDef.Name)
|
|
}
|
|
|
|
if len(existingEnum.Values) != len(enumDef.Values) {
|
|
return fmt.Errorf("enum %q has different number of values in different fields", enumDef.Name)
|
|
}
|
|
|
|
existingValues := map[string]bool{}
|
|
for _, value := range existingEnum.Values {
|
|
existingValues[value] = true
|
|
}
|
|
|
|
for _, value := range enumDef.Values {
|
|
_, ok := existingValues[value]
|
|
if !ok {
|
|
return fmt.Errorf("enum %q has different values in different fields", enumDef.Name)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Validate validates the module schema.
|
|
func (s ModuleSchema) Validate() error {
|
|
for _, typ := range s.types {
|
|
objTyp, ok := typ.(ObjectType)
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
// all enum types get added to the type map when we call ObjectType.validate
|
|
err := objTyp.validate(s.types)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// ValidateObjectUpdate validates that the update conforms to the module schema.
|
|
func (s ModuleSchema) ValidateObjectUpdate(update ObjectUpdate) error {
|
|
typ, ok := s.types[update.TypeName]
|
|
if !ok {
|
|
return fmt.Errorf("object type %q not found in module schema", update.TypeName)
|
|
}
|
|
|
|
objTyp, ok := typ.(ObjectType)
|
|
if !ok {
|
|
return fmt.Errorf("type %q is not an object type", update.TypeName)
|
|
}
|
|
|
|
return objTyp.ValidateObjectUpdate(update)
|
|
}
|
|
|
|
// LookupType looks up a type by name in the module schema.
|
|
func (s ModuleSchema) LookupType(name string) (Type, bool) {
|
|
typ, ok := s.types[name]
|
|
return typ, ok
|
|
}
|
|
|
|
// Types calls the provided function for each type in the module schema and stops if the function returns false.
|
|
// The types are iterated over in sorted order by name. This function is compatible with go 1.23 iterators.
|
|
func (s ModuleSchema) Types(f func(Type) bool) {
|
|
keys := make([]string, 0, len(s.types))
|
|
for k := range s.types {
|
|
keys = append(keys, k)
|
|
}
|
|
sort.Strings(keys)
|
|
for _, k := range keys {
|
|
if !f(s.types[k]) {
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
// ObjectTypes iterators over all the object types in the schema in alphabetical order.
|
|
func (s ModuleSchema) ObjectTypes(f func(ObjectType) bool) {
|
|
s.Types(func(t Type) bool {
|
|
objTyp, ok := t.(ObjectType)
|
|
if ok {
|
|
return f(objTyp)
|
|
}
|
|
return true
|
|
})
|
|
}
|
|
|
|
// EnumTypes iterators over all the enum types in the schema in alphabetical order.
|
|
func (s ModuleSchema) EnumTypes(f func(EnumType) bool) {
|
|
s.Types(func(t Type) bool {
|
|
enumType, ok := t.(EnumType)
|
|
if ok {
|
|
return f(enumType)
|
|
}
|
|
return true
|
|
})
|
|
}
|