fix object type parsing from jsonschema, and type code generation

This commit is contained in:
timurgordon
2025-01-08 02:17:58 -05:00
parent c9496f0973
commit 4733e05c58
10 changed files with 483 additions and 549 deletions

View File

@@ -1,6 +1,6 @@
module generator
import freeflowuniverse.herolib.core.code { Folder, IFile, VFile, CodeItem, File, Function, Param, Import, Module, Struct, CustomCode }
import freeflowuniverse.herolib.core.code { Array, Folder, IFile, VFile, CodeItem, File, Function, Param, Import, Module, Struct, CustomCode, Result }
import freeflowuniverse.herolib.core.texttools
import freeflowuniverse.herolib.schemas.openrpc.codegen {content_descriptor_to_parameter}
import freeflowuniverse.herolib.baobab.specification {ActorMethod, ActorSpecification}
@@ -42,10 +42,11 @@ pub fn generate_methods_file(spec ActorSpecification) !VFile {
// returns bodyless method prototype
pub fn generate_method_function(actor_name string, method ActorMethod) !Function {
actor_name_pascal := texttools.name_fix_snake_to_pascal(actor_name)
result_param := content_descriptor_to_parameter(method.result)!
return Function{
name: texttools.name_fix_snake(method.name)
receiver: code.new_param(v: 'mut actor ${actor_name_pascal}Actor')!
result: Param{...content_descriptor_to_parameter(method.result)!, is_result: true}
result: Param {...result_param, typ: Result{result_param.typ}}
summary: method.summary
description: method.description
params: method.parameters.map(content_descriptor_to_parameter(it)!)
@@ -80,7 +81,7 @@ fn generate_base_object_new_body(method ActorMethod) !string {
fn generate_base_object_get_body(method ActorMethod) !string {
parameter := content_descriptor_to_parameter(method.parameters[0])!
result := content_descriptor_to_parameter(method.result)!
return 'return actor.osis.get[${result.typ.vgen()}](${parameter.name})!'
return 'return actor.osis.get[${result.typ.vgen()}](${texttools.name_fix_snake(parameter.name)})!'
}
fn generate_base_object_set_body(method ActorMethod) !string {
@@ -90,10 +91,12 @@ fn generate_base_object_set_body(method ActorMethod) !string {
fn generate_base_object_delete_body(method ActorMethod) !string {
parameter := content_descriptor_to_parameter(method.parameters[0])!
return 'return actor.osis.delete(${parameter.name})!'
return 'actor.osis.delete(${texttools.name_fix_snake(parameter.name)})!'
}
fn generate_base_object_list_body(method ActorMethod) !string {
result := content_descriptor_to_parameter(method.result)!
return 'return actor.osis.list[${result.typ.vgen()}]()!'
base_object_type := (result.typ as Array).typ
return 'return actor.osis.list[${base_object_type.symbol()}]()!'
}

View File

@@ -1,14 +1,12 @@
module generator
import freeflowuniverse.herolib.baobab.specification {BaseObject}
import freeflowuniverse.herolib.core.code { VFile, CodeItem, Function, Import, Param, Param, Struct, StructField, Type }
import freeflowuniverse.herolib.core.code { type_from_symbol, VFile, CodeItem, Function, Import, Param, Param, Struct, StructField, Type }
import freeflowuniverse.herolib.core.texttools
const id_param = Param{
name: 'id'
typ: Type{
symbol: 'u32'
}
typ: type_from_symbol('u32')
}
// pub fn generate_object_code(actor Struct, object BaseObject) VFile {
@@ -47,366 +45,362 @@ const id_param = Param{
// return file
// }
// generate_object_methods generates CRUD actor methods for a provided structure
fn generate_get_method(actor Struct, object BaseObject) Function {
object_name := texttools.name_fix_pascal_to_snake(object.structure.name)
object_type := object.structure.name
get_method := Function{
name: 'get_${object_name}'
description: 'gets the ${object_name} with the given object id'
receiver: Param{
mutable: true
name: 'actor'
typ: Type{
symbol: actor.name
}
}
params: [generator.id_param]
result: Param{
typ: Type{
symbol: object.structure.name
}
is_result: true
}
body: 'return actor.backend.get[${object_type}](id)!'
}
return get_method
}
// generate_object_methods generates CRUD actor methods for a provided structure
fn generate_set_method(actor Struct, object BaseObject) Function {
object_name := texttools.name_fix_pascal_to_snake(object.structure.name)
object_type := object.structure.name
param_getters := generate_param_getters(
structure: object.structure
prefix: ''
only_mutable: true
)
body := 'actor.backend.set[${object_type}](${object_name})!'
get_method := Function{
name: 'set_${object_name}'
description: 'updates the ${object.structure.name} with the given object id'
receiver: Param{
mutable: true
name: 'actor'
typ: Type{
symbol: actor.name
}
}
params: [
Param{
name: object_name
typ: Type{
symbol: object_type
}
},
]
result: Param{
is_result: true
}
body: body
}
return get_method
}
// generate_object_methods generates CRUD actor methods for a provided structure
fn generate_delete_method(actor Struct, object BaseObject) Function {
object_name := texttools.name_fix_pascal_to_snake(object.structure.name)
object_type := object.structure.name
body := 'actor.backend.delete[${object_type}](id)!'
get_method := Function{
name: 'delete_${object_name}'
description: 'deletes the ${object.structure.name} with the given object id'
receiver: Param{
mutable: true
name: 'actor'
typ: Type{
symbol: actor.name
}
}
params: [generator.id_param]
result: Param{
is_result: true
}
body: body
}
return get_method
}
// generate_object_methods generates CRUD actor methods for a provided structure
fn generate_new_method(actor Struct, object BaseObject) Function {
object_name := texttools.name_fix_pascal_to_snake(object.structure.name)
object_type := object.structure.name
param_getters := generate_param_getters(
structure: object.structure
prefix: ''
only_mutable: false
)
body := 'return actor.backend.new[${object_type}](${object_name})!'
new_method := Function{
name: 'new_${object_name}'
description: 'news the ${object.structure.name} with the given object id'
receiver: Param{
name: 'actor'
typ: Type{
symbol: actor.name
}
mutable: true
}
params: [
Param{
name: object_name
typ: Type{
symbol: object_type
}
},
]
result: Param{
is_result: true
typ: Type{
symbol: 'u32'
}
}
body: body
}
return new_method
}
// generate_object_methods generates CRUD actor methods for a provided structure
fn generate_list_result_struct(actor Struct, object BaseObject) Struct {
object_name := texttools.name_fix_pascal_to_snake(object.structure.name)
object_type := object.structure.name
return Struct{
name: '${object_type}List'
is_pub: true
fields: [
StructField{
name: 'items'
typ: Type{
symbol: '[]${object_type}'
}
},
]
}
}
// generate_object_methods generates CRUD actor methods for a provided structure
fn generate_list_method(actor Struct, object BaseObject) Function {
object_name := texttools.name_fix_pascal_to_snake(object.structure.name)
object_type := object.structure.name
list_struct := Struct{
name: '${object_type}List'
fields: [
StructField{
name: 'items'
typ: Type{
symbol: '[]${object_type}'
}
},
]
}
param_getters := generate_param_getters(
structure: object.structure
prefix: ''
only_mutable: false
)
body := 'return ${object_type}List{items:actor.backend.list[${object_type}]()!}'
result_struct := generate_list_result_struct(actor, object)
mut result := Param{}
result.typ.symbol = result_struct.name
result.is_result = true
new_method := Function{
name: 'list_${object_name}'
description: 'lists all of the ${object_name} objects'
receiver: Param{
name: 'actor'
typ: Type{
symbol: actor.name
}
mutable: true
}
params: []
result: result
body: body
}
return new_method
}
fn generate_filter_params(actor Struct, object BaseObject) []Struct {
object_name := texttools.name_fix_pascal_to_snake(object.structure.name)
object_type := object.structure.name
return [
Struct{
name: 'Filter${object_type}Params'
fields: [
StructField{
name: 'filter'
typ: Type{
symbol: '${object_type}Filter'
}
},
StructField{
name: 'params'
typ: Type{
symbol: 'FilterParams'
}
},
]
},
Struct{
name: '${object_type}Filter'
fields: object.structure.fields.filter(it.attrs.any(it.name == 'index'))
},
]
}
// generate_object_methods generates CRUD actor methods for a provided structure
fn generate_filter_method(actor Struct, object BaseObject) Function {
object_name := texttools.name_fix_pascal_to_snake(object.structure.name)
object_type := object.structure.name
param_getters := generate_param_getters(
structure: object.structure
prefix: ''
only_mutable: false
)
params_type := 'Filter${object_type}Params'
body := 'return actor.backend.filter[${object_type}, ${object_type}Filter](filter.filter, filter.params)!'
return Function{
name: 'filter_${object_name}'
description: 'lists all of the ${object_name} objects'
receiver: Param{
name: 'actor'
typ: Type{
symbol: actor.name
}
mutable: true
}
params: [
Param{
name: 'filter'
typ: Type{
symbol: params_type
}
},
]
result: Param{
typ: Type{
symbol: '[]${object_type}'
}
is_result: true
}
body: body
}
}
// // generate_object_methods generates CRUD actor methods for a provided structure
// fn generate_object_methods(actor Struct, object BaseObject) []Function {
// fn generate_get_method(actor Struct, object BaseObject) Function {
// object_name := texttools.name_fix_pascal_to_snake(object.structure.name)
// object_type := object.structure.name
// mut funcs := []Function{}
// for method in object.methods {
// mut params := [Param{
// name: 'id'
// get_method := Function{
// name: 'get_${object_name}'
// description: 'gets the ${object_name} with the given object id'
// receiver: Param{
// mutable: true
// name: 'actor'
// typ: type_from_symbol(actor.name)
// }
// }
// params: [generator.id_param]
// result: Param{
// typ: type_from_symbol(object.structure.name)
// is_result: true
// }
// body: 'return actor.backend.get[${object_type}](id)!'
// }
// return get_method
// }
// // generate_object_methods generates CRUD actor methods for a provided structure
// fn generate_set_method(actor Struct, object BaseObject) Function {
// object_name := texttools.name_fix_pascal_to_snake(object.structure.name)
// object_type := object.structure.name
// param_getters := generate_param_getters(
// structure: object.structure
// prefix: ''
// only_mutable: true
// )
// body := 'actor.backend.set[${object_type}](${object_name})!'
// get_method := Function{
// name: 'set_${object_name}'
// description: 'updates the ${object.structure.name} with the given object id'
// receiver: Param{
// mutable: true
// name: 'actor'
// typ: type_from_symbol(actor.name)
// }
// }
// params: [
// Param{
// name: object_name
// typ: Type{
// symbol: object_type
// }
// },
// ]
// result: Param{
// is_result: true
// }
// body: body
// }
// return get_method
// }
// // generate_object_methods generates CRUD actor methods for a provided structure
// fn generate_delete_method(actor Struct, object BaseObject) Function {
// object_name := texttools.name_fix_pascal_to_snake(object.structure.name)
// object_type := object.structure.name
// body := 'actor.backend.delete[${object_type}](id)!'
// get_method := Function{
// name: 'delete_${object_name}'
// description: 'deletes the ${object.structure.name} with the given object id'
// receiver: Param{
// mutable: true
// name: 'actor'
// typ: Type{
// symbol: actor.name
// }
// }
// params: [generator.id_param]
// result: Param{
// is_result: true
// }
// body: body
// }
// return get_method
// }
// // generate_object_methods generates CRUD actor methods for a provided structure
// fn generate_new_method(actor Struct, object BaseObject) Function {
// object_name := texttools.name_fix_pascal_to_snake(object.structure.name)
// object_type := object.structure.name
// param_getters := generate_param_getters(
// structure: object.structure
// prefix: ''
// only_mutable: false
// )
// body := 'return actor.backend.new[${object_type}](${object_name})!'
// new_method := Function{
// name: 'new_${object_name}'
// description: 'news the ${object.structure.name} with the given object id'
// receiver: Param{
// name: 'actor'
// typ: Type{
// symbol: actor.name
// }
// mutable: true
// }
// params: [
// Param{
// name: object_name
// typ: Type{
// symbol: object_type
// }
// },
// ]
// result: Param{
// is_result: true
// typ: Type{
// symbol: 'u32'
// }
// }]
// params << method.params
// funcs << Function{
// name: method.name
// description: method.description
// receiver: Param{
// name: 'actor'
// typ: Type{
// symbol: actor.name
// }
// mutable: true
// }
// params: params
// result: method.result
// body: 'obj := actor.backend.get[${method.receiver.typ.symbol}](id)!
// obj.${method.name}(${method.params.map(it.name).join(',')})
// actor.backend.set[${method.receiver.typ.symbol}](obj)!
// '
// }
// body: body
// }
// return funcs
// return new_method
// }
@[params]
struct GenerateParamGetters {
structure Struct
prefix string
only_mutable bool // if true generates param.get methods for only mutable struct fields. Used for updating.
}
// // generate_object_methods generates CRUD actor methods for a provided structure
// fn generate_list_result_struct(actor Struct, object BaseObject) Struct {
// object_name := texttools.name_fix_pascal_to_snake(object.structure.name)
// object_type := object.structure.name
// return Struct{
// name: '${object_type}List'
// is_pub: true
// fields: [
// StructField{
// name: 'items'
// typ: Type{
// symbol: '[]${object_type}'
// }
// },
// ]
// }
// }
fn generate_param_getters(params GenerateParamGetters) []string {
mut param_getters := []string{}
fields := if params.only_mutable {
params.structure.fields.filter(it.is_mut && it.is_pub)
} else {
params.structure.fields.filter(it.is_pub)
}
for field in fields {
if field.typ.symbol.starts_with_capital() {
subgetters := generate_param_getters(GenerateParamGetters{
...params
structure: field.structure
prefix: '${field.name}_'
})
// name of the tested object, used for param declaration
// ex: fruits []Fruit becomes fruit_name
nested_name := field.structure.name.to_lower()
if field.typ.is_map {
param_getters.insert(0, '${nested_name}_key := params.get(\'${nested_name}_key\')!')
param_getters << '${field.name}: {${nested_name}_key: ${field.structure.name}}{'
} else if field.typ.is_array {
param_getters << '${field.name}: [${field.structure.name}{'
} else {
param_getters << '${field.name}: ${field.structure.name}{'
}
param_getters << subgetters
param_getters << if field.typ.is_array { '}]' } else { '}' }
continue
}
// // generate_object_methods generates CRUD actor methods for a provided structure
// fn generate_list_method(actor Struct, object BaseObject) Function {
// object_name := texttools.name_fix_pascal_to_snake(object.structure.name)
// object_type := object.structure.name
mut get_method := '${field.name}: params.get'
if field.typ.symbol != 'string' {
// TODO: check if params method actually exists
'get_${field.typ.symbol}'
}
// list_struct := Struct{
// name: '${object_type}List'
// fields: [
// StructField{
// name: 'items'
// typ: Type{
// symbol: '[]${object_type}'
// }
// },
// ]
// }
if field.default != '' {
get_method += '_default'
}
// param_getters := generate_param_getters(
// structure: object.structure
// prefix: ''
// only_mutable: false
// )
// body := 'return ${object_type}List{items:actor.backend.list[${object_type}]()!}'
// result_struct := generate_list_result_struct(actor, object)
// mut result := Param{}
// result.typ.symbol = result_struct.name
// result.is_result = true
// new_method := Function{
// name: 'list_${object_name}'
// description: 'lists all of the ${object_name} objects'
// receiver: Param{
// name: 'actor'
// typ: Type{
// symbol: actor.name
// }
// mutable: true
// }
// params: []
// result: result
// body: body
// }
// return new_method
// }
get_method = get_method + "('${params.prefix}${field.name}')!"
param_getters << get_method
}
return param_getters
}
// fn generate_filter_params(actor Struct, object BaseObject) []Struct {
// object_name := texttools.name_fix_pascal_to_snake(object.structure.name)
// object_type := object.structure.name
@[params]
struct GetChildField {
parent Struct @[required]
child Struct @[required]
}
// return [
// Struct{
// name: 'Filter${object_type}Params'
// fields: [
// StructField{
// name: 'filter'
// typ: Type{
// symbol: '${object_type}Filter'
// }
// },
// StructField{
// name: 'params'
// typ: Type{
// symbol: 'FilterParams'
// }
// },
// ]
// },
// Struct{
// name: '${object_type}Filter'
// fields: object.structure.fields.filter(it.attrs.any(it.name == 'index'))
// },
// ]
// }
fn get_child_field(params GetChildField) StructField {
fields := params.parent.fields.filter(it.typ.symbol == 'map[string]&${params.child.name}')
if fields.len != 1 {
panic('this should never happen')
}
return fields[0]
}
// // generate_object_methods generates CRUD actor methods for a provided structure
// fn generate_filter_method(actor Struct, object BaseObject) Function {
// object_name := texttools.name_fix_pascal_to_snake(object.structure.name)
// object_type := object.structure.name
// param_getters := generate_param_getters(
// structure: object.structure
// prefix: ''
// only_mutable: false
// )
// params_type := 'Filter${object_type}Params'
// body := 'return actor.backend.filter[${object_type}, ${object_type}Filter](filter.filter, filter.params)!'
// return Function{
// name: 'filter_${object_name}'
// description: 'lists all of the ${object_name} objects'
// receiver: Param{
// name: 'actor'
// typ: Type{
// symbol: actor.name
// }
// mutable: true
// }
// params: [
// Param{
// name: 'filter'
// typ: Type{
// symbol: params_type
// }
// },
// ]
// result: Param{
// typ: Type{
// symbol: '[]${object_type}'
// }
// is_result: true
// }
// body: body
// }
// }
// // // generate_object_methods generates CRUD actor methods for a provided structure
// // fn generate_object_methods(actor Struct, object BaseObject) []Function {
// // object_name := texttools.name_fix_pascal_to_snake(object.structure.name)
// // object_type := object.structure.name
// // mut funcs := []Function{}
// // for method in object.methods {
// // mut params := [Param{
// // name: 'id'
// // typ: Type{
// // symbol: 'u32'
// // }
// // }]
// // params << method.params
// // funcs << Function{
// // name: method.name
// // description: method.description
// // receiver: Param{
// // name: 'actor'
// // typ: Type{
// // symbol: actor.name
// // }
// // mutable: true
// // }
// // params: params
// // result: method.result
// // body: 'obj := actor.backend.get[${method.receiver.typ.symbol}](id)!
// // obj.${method.name}(${method.params.map(it.name).join(',')})
// // actor.backend.set[${method.receiver.typ.symbol}](obj)!
// // '
// // }
// // }
// // return funcs
// // }
// @[params]
// struct GenerateParamGetters {
// structure Struct
// prefix string
// only_mutable bool // if true generates param.get methods for only mutable struct fields. Used for updating.
// }
// fn generate_param_getters(params GenerateParamGetters) []string {
// mut param_getters := []string{}
// fields := if params.only_mutable {
// params.structure.fields.filter(it.is_mut && it.is_pub)
// } else {
// params.structure.fields.filter(it.is_pub)
// }
// for field in fields {
// if field.typ.symbol.starts_with_capital() {
// subgetters := generate_param_getters(GenerateParamGetters{
// ...params
// structure: field.structure
// prefix: '${field.name}_'
// })
// // name of the tested object, used for param declaration
// // ex: fruits []Fruit becomes fruit_name
// nested_name := field.structure.name.to_lower()
// if field.typ.is_map {
// param_getters.insert(0, '${nested_name}_key := params.get(\'${nested_name}_key\')!')
// param_getters << '${field.name}: {${nested_name}_key: ${field.structure.name}}{'
// } else if field.typ.is_array {
// param_getters << '${field.name}: [${field.structure.name}{'
// } else {
// param_getters << '${field.name}: ${field.structure.name}{'
// }
// param_getters << subgetters
// param_getters << if field.typ.is_array { '}]' } else { '}' }
// continue
// }
// mut get_method := '${field.name}: params.get'
// if field.typ.symbol != 'string' {
// // TODO: check if params method actually exists
// 'get_${field.typ.symbol}'
// }
// if field.default != '' {
// get_method += '_default'
// }
// get_method = get_method + "('${params.prefix}${field.name}')!"
// param_getters << get_method
// }
// return param_getters
// }
// @[params]
// struct GetChildField {
// parent Struct @[required]
// child Struct @[required]
// }
// fn get_child_field(params GetChildField) StructField {
// fields := params.parent.fields.filter(it.typ.symbol == 'map[string]&${params.child.name}')
// if fields.len != 1 {
// panic('this should never happen')
// }
// return fields[0]
// }

View File

@@ -27,7 +27,7 @@ code_md := ''
// describes the struct in markdown format
for struct in structs {
code_md += '# ${struct.name}'
code_md += 'Type: ${struct.typ.symbol}'
code_md += 'Type: ${struct.typ.symbol()}'
code_md += '## Fields:'
for field in struct.fields {
code_md += '- ${field.name}'

View File

@@ -28,6 +28,66 @@ pub mut:
struct_ Struct @[omitempty]
}
pub type Type = Array | Object | Result | Integer | Alias | String
pub struct Integer {
bytes u8
}
pub fn type_from_symbol(symbol_ string) Type {
mut symbol := symbol_.trim_space()
if symbol.starts_with('[]') {
return Array{type_from_symbol(symbol.all_after('[]'))}
} else if symbol == 'int' {
return Integer{}
} else if symbol == 'string' {
return String{}
}
return Object{symbol}
}
pub fn (t Type) symbol() string {
return match t {
Array { '[]${t.typ.symbol()}' }
Object { t.name }
Result { '!${t.typ.symbol()}'}
Integer {'int'}
Alias {t.name}
String {'string'}
}
}
pub struct String {}
pub struct Array {
pub:
typ Type
}
pub struct Object {
pub:
name string
}
pub struct Result {
pub:
typ Type
}
// // todo: maybe make 'is_' fields methods?
// pub struct Type {
// pub mut:
// is_reference bool @[str: skip]
// is_map bool @[str: skip]
// is_array bool
// is_mutable bool @[str: skip]
// is_shared bool @[str: skip]
// is_optional bool @[str: skip]
// is_result bool @[str: skip]
// symbol string
// mod string @[str: skip]
// }
@[params]
pub struct Params{
pub:
@@ -39,16 +99,6 @@ pub fn new_param(params Params) !Param {
return parse_param(params.v)!
}
pub struct Result {
pub mut:
typ Type @[omitempty]
description string @[omitempty]
name string @[omitempty]
result bool @[omitempty] // whether is result type
optional bool @[omitempty] // whether is result type
structure Struct @[omitempty]
}
pub fn new_function(code string) !Function {
// TODO: implement function from file line
return parse_function(code)!

View File

@@ -76,9 +76,7 @@ pub fn parse_param(code_ string) !Param {
if split.len == 1 {
// means anonymous param
return Param{
typ: Type{
symbol: split[0]
}
typ: type_from_symbol(split[0])
mutable: is_mut
}
}
@@ -87,41 +85,11 @@ pub fn parse_param(code_ string) !Param {
}
return Param{
name: split[0]
typ: Type{
symbol: split[1]
}
typ: type_from_symbol(split[1])
mutable: is_mut
}
}
pub fn parse_result(code_ string) !Result {
code := code_.replace(' ', '').trim_space()
return Result{
result: code_.starts_with('!')
optional: code_.starts_with('?')
typ: Type{
symbol: code.trim('!?')
is_optional: code.starts_with('?')
is_result: code.starts_with('!')
}
}
}
// todo: maybe make 'is_' fields methods?
pub struct Type {
pub mut:
is_reference bool @[str: skip]
is_map bool @[str: skip]
is_array bool
is_mutable bool @[str: skip]
is_shared bool @[str: skip]
is_optional bool @[str: skip]
is_result bool @[str: skip]
symbol string
mod string @[str: skip]
}
pub struct Alias {
pub:
name string

View File

@@ -34,10 +34,10 @@ pub fn inflate_types(mut code []CodeItem) {
pub fn inflate_struct_fields(code []CodeItem, mut struct_ CodeItem) {
for mut field in (struct_ as Struct).fields {
// TODO: fix inflation for imported types
if field.typ.symbol.starts_with_capital() {
if field.typ.symbol().starts_with_capital() {
field.structure = get_struct(
code: code
name: field.typ.symbol
name: field.typ.symbol()
) or { continue }
}
}
@@ -51,7 +51,7 @@ pub:
pub fn (func Function) generate_call(params GenerateCallParams) !string {
mut call := ''
if func.result.typ.symbol != '' {
if func.result.typ.symbol() != '' {
call = 'result := '
}
call += if params.receiver != '' {
@@ -79,12 +79,12 @@ pub struct GenerateValueParams {
}
pub fn (param Param) generate_value() !string {
if param.typ.symbol == 'string' {
if param.typ.symbol() == 'string' {
return "'mock_string_${rand.string(3)}'"
} else if param.typ.symbol == 'int' || param.typ.symbol == 'u32' {
} else if param.typ.symbol() == 'int' || param.typ.symbol() == 'u32' {
return '42'
} else if param.typ.symbol[0].is_capital() {
return '${param.typ.symbol}{}'
} else if param.typ.symbol()[0].is_capital() {
return '${param.typ.symbol()}{}'
} else {
log.debug('mock values for types other than strings and ints are not yet supported')
}

View File

@@ -43,21 +43,8 @@ pub fn (import_ Import) vgen() string {
}
// TODO: enfore that cant be both mutable and shared
pub fn (type_ Type) vgen() string {
mut type_str := ''
if type_.is_mutable {
type_str += 'mut '
} else if type_.is_shared {
type_str += 'shared '
}
if type_.is_optional {
type_str += '?'
} else if type_.is_result {
type_str += '!'
}
return '${type_str} ${type_.symbol}'
pub fn (t Type) vgen() string {
return t.symbol()
}
pub fn (field StructField) vgen() string {
@@ -73,7 +60,7 @@ pub fn (field StructField) get_type_symbol() string {
mut field_str := if field.structure.name != '' {
field.structure.get_type_symbol()
} else {
field.typ.symbol
field.typ.symbol()
}
if field.is_ref {
@@ -111,12 +98,10 @@ pub fn vgen_generics(generics map[string]string) string {
pub fn (function Function) vgen(options WriteOptions) string {
mut params_ := function.params.map(Param{
...it
typ: Type{
symbol: if it.struct_.name != '' {
it.struct_.name
} else {
it.typ.symbol
}
typ: if it.struct_.name != '' {
type_from_symbol(it.struct_.name)
} else {
it.typ
}
})
@@ -129,17 +114,13 @@ pub fn (function Function) vgen(options WriteOptions) string {
fields: optionals.map(StructField{
name: it.name
description: it.description
typ: Type{
symbol: it.typ.symbol
}
typ: it.typ
})
}
if optionals.len > 0 {
params_ << Param{
name: 'options'
typ: Type{
symbol: options_struct.name
}
typ: type_from_symbol(options_struct.name)
}
}
@@ -172,22 +153,9 @@ pub fn (function Function) vgen(options WriteOptions) string {
}
pub fn (param Param) vgen() string {
// if param.name == '' {
// return ''
// }
sym := if param.struct_.name != '' {
param.struct_.get_type_symbol()
} else {
param.typ.symbol
}
sym := param.typ.symbol()
param_name := texttools.name_fix_snake(param.name)
mut vstr := '${param_name} ${sym}'
if param.typ.is_reference {
vstr = '&${vstr}'
}
if param.is_result {
vstr = '!${vstr}'
}
if param.mutable {
vstr = 'mut ${vstr}'
}
@@ -196,19 +164,6 @@ pub fn (param Param) vgen() string {
// vgen_function generates a function statement for a function
pub fn (struct_ Struct) vgen() string {
gen := VGenerator{false}
return gen.generate_struct(struct_) or { panic(err) }
// mut struct_str := $tmpl('templates/struct/struct.v.template')
// return struct_str
// result := os.execute_opt('echo "${struct_str.replace('$', '\$')}" | v fmt') or {panic(err)}
// return result.output
}
pub struct VGenerator {
format bool
}
pub fn (gen VGenerator) generate_struct(struct_ Struct) !string {
name := if struct_.generics.len > 0 {
'${struct_.name}${vgen_generics(struct_.generics)}'
} else {
@@ -221,13 +176,13 @@ pub fn (gen VGenerator) generate_struct(struct_ Struct) !string {
''
}
priv_fields := struct_.fields.filter(!it.is_mut && !it.is_pub).map(gen.generate_struct_field(it))
pub_fields := struct_.fields.filter(!it.is_mut && it.is_pub).map(gen.generate_struct_field(it))
mut_fields := struct_.fields.filter(it.is_mut && !it.is_pub).map(gen.generate_struct_field(it))
pub_mut_fields := struct_.fields.filter(it.is_mut && it.is_pub).map(gen.generate_struct_field(it))
priv_fields := struct_.fields.filter(!it.is_mut && !it.is_pub).map(generate_struct_field(it))
pub_fields := struct_.fields.filter(!it.is_mut && it.is_pub).map(generate_struct_field(it))
mut_fields := struct_.fields.filter(it.is_mut && !it.is_pub).map(generate_struct_field(it))
pub_mut_fields := struct_.fields.filter(it.is_mut && it.is_pub).map(generate_struct_field(it))
mut struct_str := $tmpl('templates/struct/struct.v.template')
if gen.format {
if false {
result := os.execute_opt('echo "${struct_str.replace('$', '\$')}" | v fmt') or {
log.debug(struct_str)
panic(err)
@@ -235,9 +190,13 @@ pub fn (gen VGenerator) generate_struct(struct_ Struct) !string {
return result.output
}
return struct_str
// mut struct_str := $tmpl('templates/struct/struct.v.template')
// return struct_str
// result := os.execute_opt('echo "${struct_str.replace('$', '\$')}" | v fmt') or {panic(err)}
// return result.output
}
pub fn (gen VGenerator) generate_struct_field(field StructField) string {
pub fn generate_struct_field(field StructField) string {
symbol := field.get_type_symbol()
mut vstr := '${field.name} ${symbol}'
if field.description != '' {
@@ -250,29 +209,6 @@ pub fn (custom CustomCode) vgen() string {
return custom.text
}
// vgen_function generates a function statement for a function
pub fn (result Result) vgen() string {
result_type := if result.structure.name != '' {
result.structure.get_type_symbol()
} else if result.typ.symbol == 'void' {
''
} else {
if result.typ.is_array {
'[]${result.typ.symbol}'
} else {
result.typ.symbol
}
}
str := if result.result {
'!'
} else if result.typ.is_result {
'!'
} else {
''
}
return '${str}${result_type}'
}
@[params]
pub struct WriteOptions {
pub:

View File

@@ -1,6 +1,6 @@
module codegen
import freeflowuniverse.herolib.core.code { Alias, Attribute, CodeItem, Struct, StructField, Type }
import freeflowuniverse.herolib.core.code { Alias, Attribute, CodeItem, Struct, StructField, Type, type_from_symbol, Object, Array}
import freeflowuniverse.herolib.schemas.jsonschema { Schema, SchemaRef, Reference }
const vtypes = {
@@ -35,7 +35,7 @@ pub fn schema_to_structs(schema Schema) ![]string {
typesymbol = ref_to_symbol(ref)
} else {
property = property_ as Schema
typesymbol = schema_to_type(property)!
typesymbol = schema_to_type(property)!.symbol()
// recursively encode property if object
// todo: handle duplicates
if property.typ == 'object' {
@@ -54,34 +54,36 @@ pub fn schema_to_structs(schema Schema) ![]string {
}
// schema_to_type generates a typesymbol for the schema
pub fn schema_to_type(schema Schema) !string {
mut property_str := ''
pub fn schema_to_type(schema Schema) !Type {
if schema.typ == 'null' {
return ''
Type{}
}
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' {
mut property_str := ''
return match schema.typ {
'object' {
if schema.title == '' {
return error('Object schemas must define a title.')
}
Object{schema.title}
}
'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}'
if schema.items is []SchemaRef {
return error('items of type []SchemaRef not implemented')
}
Array {
typ: schemaref_to_type(schema.items as SchemaRef)!
}
} else {
if schema.typ in vtypes.keys() {
type_from_symbol(vtypes[schema.typ])
} else if schema.title != '' {
type_from_symbol(schema.title)
} else {
return error('unknown type `${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 {
@@ -91,9 +93,7 @@ pub fn schema_to_code(schema Schema) !CodeItem {
if schema.typ in vtypes {
return Alias{
name: schema.title
typ: Type{
symbol: vtypes[schema.typ]
}
typ: type_from_symbol(vtypes[schema.typ])
}
}
if schema.typ == 'array' {
@@ -102,17 +102,13 @@ pub fn schema_to_code(schema Schema) !CodeItem {
items_schema := schema.items as Schema
return Alias{
name: schema.title
typ: Type{
symbol: '[]${items_schema.typ}'
}
typ: type_from_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)}'
}
typ: type_from_symbol('[]${ref_to_symbol(items_ref)}')
}
}
}
@@ -144,9 +140,7 @@ 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)
}
typ: type_from_symbol(ref_to_symbol(schema_ref))
}
} else if schema_ref is Schema {
mut field := StructField{
@@ -158,7 +152,7 @@ pub fn ref_to_field(schema_ref SchemaRef, name string) !StructField {
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]
field.typ = type_from_symbol(vtypes[schema_ref.typ])
return field
}
return error('Schema type ${schema_ref.typ} not supported for code generation')
@@ -170,9 +164,7 @@ 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)!
}
schema_to_type(schema_ref as Schema)!
}
}
@@ -181,7 +173,5 @@ pub fn ref_to_symbol(reference Reference) string {
}
pub fn ref_to_type_from_reference(reference Reference) Type {
return Type{
symbol: ref_to_symbol(reference)
}
return type_from_symbol(ref_to_symbol(reference))
}

View File

@@ -7,7 +7,7 @@ import freeflowuniverse.herolib.schemas.jsonschema { SchemaRef, Schema, Referenc
pub fn sumtype_to_schema(sumtype code.Sumtype) SchemaRef {
mut one_of := []SchemaRef{}
for type_ in sumtype.types {
property_schema := typesymbol_to_schema(type_.symbol)
property_schema := typesymbol_to_schema(type_.symbol())
one_of << property_schema
}
@@ -25,7 +25,7 @@ pub fn struct_to_schema(struct_ Struct) SchemaRef {
mut properties := map[string]SchemaRef{}
for field in struct_.fields {
mut property_schema := SchemaRef(Schema{})
if field.typ.symbol.starts_with('_VAnonStruct') {
if field.typ.symbol().starts_with('_VAnonStruct') {
property_schema = struct_to_schema(field.anon_struct)
} else {
property_schema = type_to_schema(field.typ)
@@ -57,14 +57,7 @@ pub fn param_to_schema(param Param) SchemaRef {
if param.struct_ != Struct{} {
return struct_to_schema(param.struct_)
}
return typesymbol_to_schema(param.typ.symbol)
}
pub fn result_to_schema(result Result) SchemaRef {
if result.structure != Struct{} {
return struct_to_schema(result.structure)
}
return typesymbol_to_schema(result.typ.symbol)
return typesymbol_to_schema(param.typ.symbol())
}
// typesymbol_to_schema receives a typesymbol, if the typesymbol belongs to a user defined struct
@@ -172,12 +165,12 @@ pub fn typesymbol_to_schema(symbol_ string) SchemaRef {
}
pub fn type_to_schema(typ Type) SchemaRef {
mut symbol := typ.symbol.trim_string_left('!').trim_string_left('?')
mut symbol := typ.symbol().trim_string_left('!').trim_string_left('?')
if symbol == '' {
return SchemaRef(Schema{
typ: 'null'
})
} else if symbol.starts_with('[]') || typ.is_array {
} else if symbol.starts_with('[]') {
mut array_type := symbol.trim_string_left('[]')
return SchemaRef(Schema{
typ: 'array'

View File

@@ -55,7 +55,7 @@ pub fn generate_handler_test_file(o OpenRPC, receiver Struct, method_map map[str
if method.params.len == 0 {
continue
}
if method.params[0].typ.symbol[0].is_capital() {
if method.params[0].typ.symbol()[0].is_capital() {
continue
}
method_handle_test := Function{
@@ -64,7 +64,7 @@ pub fn generate_handler_test_file(o OpenRPC, receiver Struct, method_map map[str
is_result: true
}
body: "mut handler := ${receiver.name}Handler {${handler_name}.get(name: actor_name)!}
request := new_jsonrpcrequest[${method.params[0].typ.symbol}]('${method.name}', ${get_mock_value(method.params[0].typ.symbol)!})
request := new_jsonrpcrequest[${method.params[0].typ.symbol()}]('${method.name}', ${get_mock_value(method.params[0].typ.symbol())!})
response_json := handler.handle(request.to_json())!"
}
handle_tests << method_handle_test