fix generated code compilation for example

This commit is contained in:
timurgordon
2025-02-13 02:33:24 +03:00
parent 776942cd8b
commit 715f481364
7 changed files with 96 additions and 36 deletions

View File

@@ -67,7 +67,7 @@ pub fn generate_method_handle(actor_name string, method ActorMethod) !Function {
body += 'params_arr := json.raw_decode(action.params)!.arr()\n'
for i, param in method.parameters {
param_name := texttools.snake_case(param.name)
decode_stmt := generate_decode_stmt('params_arr[${i}]', param)!
decode_stmt := generate_decode_stmt('params_arr[${i}].str()', param)!
body += '${param_name} := ${decode_stmt}'
}
}

View File

@@ -92,7 +92,7 @@ pub fn generate_client_method(method ActorMethod) !Function {
name_fixed := texttools.snake_case(method.name)
call_params := if method.parameters.len > 0 {
method.parameters.map(texttools.snake_case(it.name)).map('Any(${it})').join(', ')
method.parameters.map(texttools.snake_case(it.name)).map('Any(${it}.str())').join(', ')
} else {''}
params_stmt := if method.parameters.len == 0 {

View File

@@ -2,7 +2,10 @@ module generator
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.schemas.openrpc {ContentDescriptor}
import freeflowuniverse.herolib.schemas.openrpc.codegen {content_descriptor_to_parameter, content_descriptor_to_struct}
import freeflowuniverse.herolib.schemas.jsonschema {Schema}
import freeflowuniverse.herolib.schemas.jsonschema.codegen as jsonschema_codegen {schema_to_struct}
import freeflowuniverse.herolib.baobab.specification {ActorMethod, ActorSpecification}
const crud_prefixes = ['new', 'get', 'set', 'delete', 'list']
@@ -14,24 +17,13 @@ pub fn generate_methods_file(spec ActorSpecification) !VFile {
receiver := generate_methods_receiver(spec.name)
receiver_param := Param {
mutable: true
name: name_snake
name: name_snake[0].ascii_str() // receiver is first letter of domain
typ: code.Result{code.Object{receiver.name}}
}
mut items := [CodeItem(receiver), CodeItem(generate_core_factory(receiver_param))]
for method in spec.methods {
method_fn := generate_method_function(receiver_param, method)!
// check if method is a Base Object CRUD Method and
// if so generate the method's body
body := match spec.method_type(method) {
.base_object_new { base_object_new_body(method)! }
.base_object_get { base_object_get_body(method)! }
.base_object_set { base_object_set_body(method)! }
.base_object_delete { base_object_delete_body(method)! }
.base_object_list { base_object_list_body(method)! }
else {"panic('implement')"}
}
items << Function{...method_fn, body: body}
items << generate_method_code(receiver_param, ActorMethod{...method, category: spec.method_type(method)})!
}
return VFile {
@@ -52,14 +44,44 @@ fn generate_methods_receiver(name string) code.Struct {
fn generate_core_factory(receiver code.Param) code.Function {
return code.Function {
is_pub: true
name: 'new_${receiver.name}'
name: 'new_${receiver.typ.symbol()}'
body: "return ${receiver.typ.symbol().trim_left('!?')}{OSIS: osis.new()!}"
result: receiver
}
}
// returns bodyless method prototype
pub fn generate_method_function(receiver code.Param, method ActorMethod) !Function {
pub fn generate_method_code(receiver code.Param, method ActorMethod) ![]CodeItem {
result_param := content_descriptor_to_parameter(method.result)!
mut method_code := []CodeItem{}
// TODO: document assumption
obj_params := method.parameters.filter(if it.schema is Schema {it.schema.typ == 'object'} else {false}).map(content_descriptor_to_struct(it))
if obj_param := obj_params[0] {
method_code << obj_param
}
// check if method is a Base Object CRUD Method and
// if so generate the method's body
body := match method.category {
.base_object_new { base_object_new_body(method)! }
.base_object_get { base_object_get_body(method)! }
.base_object_set { base_object_set_body(method)! }
.base_object_delete { base_object_delete_body(method)! }
.base_object_list { base_object_list_body(method)! }
else {"panic('implement')"}
}
fn_prototype := generate_method_prototype(receiver, method)!
method_code << Function{
...fn_prototype
body: body
}
return method_code
}
// returns bodyless method prototype
pub fn generate_method_prototype(receiver code.Param, method ActorMethod) !Function {
result_param := content_descriptor_to_parameter(method.result)!
return Function{
name: texttools.snake_case(method.name)

View File

@@ -3,6 +3,8 @@ module generator
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 {Example}
import freeflowuniverse.herolib.schemas.jsonschema {Schema}
import freeflowuniverse.herolib.schemas.jsonschema.codegen as jsonschema_codegen {schema_to_struct}
import freeflowuniverse.herolib.schemas.openrpc.codegen {content_descriptor_to_parameter}
import freeflowuniverse.herolib.baobab.specification {ActorMethod, ActorSpecification}
@@ -13,15 +15,12 @@ pub fn generate_methods_example_file(spec ActorSpecification) !VFile {
receiver := generate_example_methods_receiver(spec.name)
receiver_param := Param {
mutable: true
name: name_snake
name: name_snake[0].ascii_str()
typ: code.Result{code.Object{receiver.name}}
}
mut items := [CodeItem(receiver), CodeItem(generate_core_example_factory(receiver_param))]
for method in spec.methods {
method_fn := generate_method_function(receiver_param, method)!
items << Function{...method_fn,
body: generate_method_example_body(method_fn, method)!
}
items << generate_method_example_code(receiver_param, ActorMethod{...method, category: spec.method_type(method)})!
}
return VFile {
@@ -37,7 +36,7 @@ pub fn generate_methods_example_file(spec ActorSpecification) !VFile {
fn generate_core_example_factory(receiver code.Param) code.Function {
return code.Function {
is_pub: true
name: 'new_${receiver.name}_example'
name: 'new_${texttools.snake_case(receiver.typ.symbol())}'
body: "return ${receiver.typ.symbol().trim_left('!?')}{OSIS: osis.new()!}"
result: receiver
}
@@ -51,15 +50,35 @@ fn generate_example_methods_receiver(name string) code.Struct {
}
}
fn generate_method_example_body(func Function, method ActorMethod) !string {
return if !method_is_void(method)! {
// returns bodyless method prototype
pub fn generate_method_example_code(receiver code.Param, method ActorMethod) ![]CodeItem {
result_param := content_descriptor_to_parameter(method.result)!
mut method_code := []CodeItem{}
// TODO: document assumption
// obj_params := method.parameters.filter(if it.schema is Schema {it.schema.typ == 'object'} else {false}).map(schema_to_struct(it.schema as Schema))
// if obj_param := obj_params[0] {
// method_code << Struct{...obj_param, name: method.name}
// }
// check if method is a Base Object CRUD Method and
// if so generate the method's body
body := if !method_is_void(method)! {
if method.example.result is Example {
"data := '${method.example.result.value}'
return ${generate_decode_stmt('data', method.result)!}"
"json_str := '${method.example.result.value}'
return ${generate_decode_stmt('json_str', method.result)!}"
} else {
"return ${func.result.typ.empty_value()}"
"return ${result_param.typ.empty_value()}"
}
} else {
""
}
fn_prototype := generate_method_prototype(receiver, method)!
method_code << Function{
...fn_prototype
body: body
}
return method_code
}

View File

@@ -20,12 +20,12 @@ pub fn generate_methods_interface_declaration(spec ActorSpecification) !code.Int
receiver := generate_methods_receiver(spec.name)
receiver_param := Param {
mutable: true
name: name_snake
name: name_snake[0].ascii_str()
typ: code.Object{receiver.name}
}
return code.Interface {
is_pub: true
name: 'I${name_pascal}'
methods: spec.methods.map(generate_method_function(receiver_param, it)!)
methods: spec.methods.map(generate_method_prototype(receiver_param, it)!)
}
}

View File

@@ -1,8 +1,9 @@
module specification
import freeflowuniverse.herolib.core.texttools
import freeflowuniverse.herolib.core.code { Struct, Function }
import freeflowuniverse.herolib.schemas.jsonschema { Schema, SchemaRef }
import freeflowuniverse.herolib.schemas.openapi { Operation, Parameter, OpenAPI, Components, Info, PathItem, ServerSpec }
import freeflowuniverse.herolib.schemas.openapi { Operation, Parameter, OpenAPI, Components, Info, PathItem, ServerSpec, MediaType }
import freeflowuniverse.herolib.schemas.openrpc { ExamplePairing, Example, ExampleRef, ContentDescriptor, ErrorSpec }
// Helper function: Convert OpenAPI parameter to ContentDescriptor
@@ -42,19 +43,36 @@ fn openapi_operation_to_actor_method(info openapi.OperationInfo) ActorMethod {
}
}
response_200 := info.operation.responses['200'].content['application/json']
if schema_ := info.operation.payload_schema() {
// TODO: document assumption
schema := Schema{...schema_, title: texttools.pascal_case(info.operation.operation_id)}
parameters << ContentDescriptor {name: 'data', schema: SchemaRef(schema)}
}
mut success_responses := map[string]MediaType{}
for code, response in info.operation.responses {
if code.starts_with('2') { // Matches all 2xx responses
success_responses[code] = response.content['application/json']
}
}
if success_responses.len > 1 || success_responses.len == 0 {
panic('Actor specification must specify one successful response.')
}
response_success := success_responses.values()[0]
mut result := ContentDescriptor{
name: "result",
description: "The response of the operation.",
required: true,
schema: response_200.schema
schema: response_success.schema
}
example_result := if response_200.example.str() != '' {
example_result := if response_success.example.str() != '' {
Example{
name: 'Example response',
value: response_200.example
value: response_success.example
}
} else {Example{}}

View File

@@ -35,6 +35,7 @@ pub:
parameters []ContentDescriptor
result ContentDescriptor
errors []ErrorSpec
category MethodCategory
}
pub struct BaseObject {