Files
herolib/lib/schemas/jsonschema/codegen/codegen.v
2025-01-02 01:40:01 -05:00

187 lines
4.6 KiB
V

module codegen
import freeflowuniverse.herolib.core.code { Alias, Attribute, CodeItem, Struct, StructField, Type }
import freeflowuniverse.herolib.schemas.jsonschema { Schema, SchemaRef, Reference }
const vtypes = {
'integer': 'int'
'string': 'string'
}
pub fn schema_to_v(schema Schema) !string {
module_name := 'schema.title.'
structs := schema_to_structs(schema)!
// todo: report bug: return $tmpl(...)
encoded := $tmpl('templates/schema.vtemplate')
return encoded
}
// schema_to_structs encodes a schema into V structs.
// if a schema has nested object type schemas or defines object type schemas,
// recursively encodes object type schemas and pushes to the array of structs.
// returns an array of schemas that have been encoded into V structs.
pub fn schema_to_structs(schema Schema) ![]string {
mut schemas := []string{}
mut properties := ''
// loop over properties
for name, property_ in schema.properties {
mut property := Schema{}
mut typesymbol := ''
if property_ is Reference {
// if reference, set typesymbol as reference name
ref := property_ as Reference
typesymbol = ref_to_symbol(ref)
} else {
property = property_ as Schema
typesymbol = schema_to_type(property)!
// recursively encode property if object
// todo: handle duplicates
if property.typ == 'object' {
structs := schema_to_structs(property)!
schemas << structs
}
}
properties += '\n\t${name} ${typesymbol}'
if name in schema.required {
properties += ' @[required]'
}
}
schemas << $tmpl('templates/struct.vtemplate')
return schemas
}
// schema_to_type generates a typesymbol for the schema
pub fn schema_to_type(schema Schema) !string {
mut property_str := ''
if schema.typ == 'null' {
return ''
}
if schema.typ == 'object' {
if schema.title == '' {
return error('Object schemas must define a title.')
}
// todo: enforce uppercase
property_str = schema.title
} else if schema.typ == 'array' {
// todo: handle multiple item schemas
if schema.items is SchemaRef {
// items := schema.items as SchemaRef
if schema.items is Schema {
items_schema := schema.items as Schema
property_str = '[]${items_schema.typ}'
}
}
} else if schema.typ in vtypes.keys() {
property_str = vtypes[schema.typ]
} else if schema.title != '' {
property_str = schema.title
} else {
return error('unknown type `${schema.typ}` ')
}
return property_str
}
pub fn schema_to_code(schema Schema) !CodeItem {
if schema.typ == 'object' {
return CodeItem(schema_to_struct(schema)!)
}
if schema.typ in vtypes {
return Alias{
name: schema.title
typ: Type{
symbol: vtypes[schema.typ]
}
}
}
if schema.typ == 'array' {
if schema.items is SchemaRef {
if schema.items is Schema {
items_schema := schema.items as Schema
return Alias{
name: schema.title
typ: Type{
symbol: '[]${items_schema.typ}'
}
}
} else if schema.items is Reference {
items_ref := schema.items as Reference
return Alias{
name: schema.title
typ: Type{
symbol: '[]${ref_to_symbol(items_ref)}'
}
}
}
}
}
return error('Schema type ${schema.typ} not supported for code generation')
}
pub fn schema_to_struct(schema Schema) !Struct {
mut fields := []StructField{}
for key, val in schema.properties {
mut field := ref_to_field(val, key)!
if field.name in schema.required {
field.attrs << Attribute{
name: 'required'
}
}
fields << field
}
return Struct{
name: schema.title
description: schema.description
fields: fields
}
}
pub fn ref_to_field(schema_ref SchemaRef, name string) !StructField {
if schema_ref is Reference {
return StructField{
name: name
typ: Type{
symbol: ref_to_symbol(schema_ref)
}
}
} else if schema_ref is Schema {
mut field := StructField{
name: name
description: schema_ref.description
}
if schema_ref.typ == 'object' {
// then it is an anonymous struct
field.anon_struct = schema_to_struct(schema_ref as Schema)!
return field
} else if schema_ref.typ in vtypes {
field.typ.symbol = vtypes[schema_ref.typ]
return field
}
return error('Schema type ${schema_ref.typ} not supported for code generation')
}
return error('Schema type not supported for code generation')
}
pub fn schemaref_to_type(schema_ref SchemaRef) !Type {
return if schema_ref is Reference {
ref_to_type_from_reference(schema_ref as Reference)
} else {
Type{
symbol: schema_to_type(schema_ref as Schema)!
}
}
}
pub fn ref_to_symbol(reference Reference) string {
return reference.ref.all_after_last('/')
}
pub fn ref_to_type_from_reference(reference Reference) Type {
return Type{
symbol: ref_to_symbol(reference)
}
}