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.texttools const id_param = Param{ name: 'id' typ: Type{ symbol: 'u32' } } // pub fn generate_object_code(actor Struct, object BaseObject) VFile { // obj_name := texttools.name_fix_pascal_to_snake(object.structure.name) // object_type := object.structure.name // mut items := []CodeItem{} // items = [generate_new_method(actor, object), generate_get_method(actor, object), // generate_set_method(actor, object), generate_delete_method(actor, object), // generate_list_result_struct(actor, object), generate_list_method(actor, object)] // items << generate_object_methods(actor, object) // mut file := code.new_file( // mod: texttools.name_fix(actor.name) // name: obj_name // imports: [ // Import{ // mod: object.structure.mod // types: [object_type] // }, // Import{ // mod: 'freeflowuniverse.herolib.baobab.backend' // types: ['FilterParams'] // }, // ] // items: items // ) // if object.structure.fields.any(it.attrs.any(it.name == 'index')) { // // can't filter without indices // filter_params := generate_filter_params(actor, object) // file.items << filter_params.map(CodeItem(it)) // file.items << generate_filter_method(actor, object) // } // 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 { // 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] }