Files
herolib/lib/data/jsonschema/codegen.v
2024-12-25 11:18:08 +01:00

187 lines
4.4 KiB
V

module jsonschema
import freeflowuniverse.herolib.code.codemodel { Alias, Attribute, CodeItem, Struct, StructField, Type }
const vtypes = {
'integer': 'int'
'string': 'string'
}
pub fn (schema Schema) v_encode() !string {
module_name := 'schema.title.'
structs := schema.vstructs_encode()!
// todo: report bug: return $tmpl(...)
encoded := $tmpl('templates/schema.vtemplate')
return encoded
}
// vstructs_encode encodes a schema into V structs.
// if a schema has nested object type schemas or defines object type schemas,
// recrusively 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 Schema) vstructs_encode() ![]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.ref.all_after_last('/')
} else {
property = property_ as Schema
typesymbol = property.vtype_encode()!
// recursively encode property if object
// todo: handle duplicates
if property.typ == 'object' {
structs := property.vstructs_encode()!
schemas << structs
}
}
properties += '\n\t${name} ${typesymbol}'
if name in schema.required {
properties += ' @[required]'
}
}
schemas << $tmpl('templates/struct.vtemplate')
return schemas
}
// code_type generates a typesymbol for the schema
pub fn (schema Schema) vtype_encode() !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: enfore 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 Schema) to_code() !CodeItem {
if schema.typ == 'object' {
return CodeItem(schema.to_struct()!)
}
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: '[]${items_ref.to_type_symbol()}'
}
}
}
}
}
return error('Schema typ ${schema.typ} not supported for code generation')
}
pub fn (schema Schema) to_struct() !Struct {
mut fields := []StructField{}
for key, val in schema.properties {
mut field := val.to_struct_field(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 (schema SchemaRef) to_struct_field(name string) !StructField {
if schema is Reference {
return StructField{
name: name
typ: Type{
symbol: schema.to_type_symbol()
}
}
} else if schema is Schema {
mut field := StructField{
name: name
description: schema.description
}
if schema.typ == 'object' {
// then is anonymous struct
field.anon_struct = schema.to_struct()!
return field
} else if schema.typ in vtypes {
field.typ.symbol = vtypes[schema.typ]
return field
}
return error('Schema typ ${schema.typ} not supported for code generation')
}
return error('Schema typ not supported for code generation')
}
pub fn (sr SchemaRef) to_code() !Type {
return if sr is Reference {
sr.to_type()
} else {
Type{
symbol: (sr as Schema).vtype_encode()!
}
}
}
pub fn (ref Reference) to_type_symbol() string {
return ref.ref.all_after_last('/')
}
pub fn (ref Reference) to_type() Type {
return Type{
symbol: ref.to_type_symbol()
}
}