cosmos-sdk/schema/module_schema.go
Aaron Craelius ae40e809b9
refactor(schema)!: rename ObjectType -> StateObjectType (#21691)
Co-authored-by: cool-developer <51834436+cool-develope@users.noreply.github.com>
2024-09-16 08:17:52 +00:00

201 lines
4.9 KiB
Go

package schema
import (
"encoding/json"
"fmt"
"sort"
)
// ModuleSchema represents the logical schema of a module for purposes of indexing and querying.
type ModuleSchema struct {
types map[string]Type
}
// CompileModuleSchema compiles the types into a ModuleSchema and validates it.
// Any module schema returned without an error is guaranteed to be valid.
func CompileModuleSchema(types ...Type) (ModuleSchema, error) {
typeMap := map[string]Type{}
for _, typ := range types {
if _, ok := typeMap[typ.TypeName()]; ok {
return ModuleSchema{}, fmt.Errorf("duplicate type %q", typ.TypeName())
}
typeMap[typ.TypeName()] = typ
}
res := ModuleSchema{types: typeMap}
err := res.Validate()
if err != nil {
return ModuleSchema{}, err
}
return res, nil
}
// MustCompileModuleSchema constructs a new ModuleSchema and panics if it is invalid.
// This should only be used in test code or static initialization where it is safe to panic!
func MustCompileModuleSchema(types ...Type) ModuleSchema {
sch, err := CompileModuleSchema(types...)
if err != nil {
panic(err)
}
return sch
}
// Validate validates the module schema.
func (s ModuleSchema) Validate() error {
for _, typ := range s.types {
err := typ.Validate(s)
if err != nil {
return err
}
}
return nil
}
// ValidateObjectUpdate validates that the update conforms to the module schema.
func (s ModuleSchema) ValidateObjectUpdate(update StateObjectUpdate) 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.(StateObjectType)
if !ok {
return fmt.Errorf("type %q is not an object type", update.TypeName)
}
return objTyp.ValidateObjectUpdate(update, s)
}
// 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
}
// LookupEnumType is a convenience method that looks up an EnumType by name.
func (s ModuleSchema) LookupEnumType(name string) (t EnumType, found bool) {
typ, found := s.LookupType(name)
if !found {
return EnumType{}, false
}
t, ok := typ.(EnumType)
if !ok {
return EnumType{}, false
}
return t, true
}
// LookupObjectType is a convenience method that looks up an ObjectType by name.
func (s ModuleSchema) LookupStateObjectType(name string) (t StateObjectType, found bool) {
typ, found := s.LookupType(name)
if !found {
return StateObjectType{}, false
}
t, ok := typ.(StateObjectType)
if !ok {
return StateObjectType{}, false
}
return t, true
}
// AllTypes 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) AllTypes(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) StateObjectTypes(f func(StateObjectType) bool) {
s.AllTypes(func(t Type) bool {
objTyp, ok := t.(StateObjectType)
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.AllTypes(func(t Type) bool {
enumType, ok := t.(EnumType)
if ok {
return f(enumType)
}
return true
})
}
type moduleSchemaJson struct {
ObjectTypes []StateObjectType `json:"object_types"`
EnumTypes []EnumType `json:"enum_types"`
}
// MarshalJSON implements the json.Marshaler interface for ModuleSchema.
// It marshals the module schema into a JSON object with the object types and enum types
// under the keys "object_types" and "enum_types" respectively.
func (s ModuleSchema) MarshalJSON() ([]byte, error) {
asJson := moduleSchemaJson{}
s.StateObjectTypes(func(objType StateObjectType) bool {
asJson.ObjectTypes = append(asJson.ObjectTypes, objType)
return true
})
s.EnumTypes(func(enumType EnumType) bool {
asJson.EnumTypes = append(asJson.EnumTypes, enumType)
return true
})
return json.Marshal(asJson)
}
// UnmarshalJSON implements the json.Unmarshaler interface for ModuleSchema.
// See MarshalJSON for the JSON format.
func (s *ModuleSchema) UnmarshalJSON(data []byte) error {
asJson := moduleSchemaJson{}
err := json.Unmarshal(data, &asJson)
if err != nil {
return err
}
types := map[string]Type{}
for _, objType := range asJson.ObjectTypes {
types[objType.Name] = objType
}
for _, enumType := range asJson.EnumTypes {
types[enumType.Name] = enumType
}
s.types = types
// validate adds all enum types to the type map
err = s.Validate()
if err != nil {
return err
}
return nil
}
func (ModuleSchema) isTypeSet() {}
var _ TypeSet = ModuleSchema{}