use crate::backends::jni::template_env;
use crate::codegen::naming::to_class_name;
use crate::core::config::ResolvedCrateConfig;
use crate::core::ir::{ApiSurface, LifecycleHookDef};
use crate::core::jni::{bridge_method_name, jni_package, jni_symbol, service_bridge_class_name};
use minijinja::context;
pub(crate) fn emit_lifecycle_hooks(
hooks: &[LifecycleHookDef],
api: &ApiSurface,
config: &ResolvedCrateConfig,
) -> String {
if hooks.is_empty() {
return String::new();
}
let package = jni_package(config);
let core_import = config.core_import_name();
let mut out = String::new();
for service in &api.services {
let service_bridge_class = service_bridge_class_name(&service.name);
for hook in hooks {
gen_lifecycle_hook_registration(
&mut out,
service,
hook,
api,
&core_import,
&package,
&service_bridge_class,
);
}
}
out
}
#[allow(dead_code)]
pub(crate) fn emit_websocket_routes(_routes: &[crate::core::ir::WebSocketRouteDef]) -> String {
String::new()
}
#[allow(dead_code)]
pub(crate) fn emit_sse_routes(_routes: &[crate::core::ir::SseRouteDef]) -> String {
String::new()
}
pub(crate) fn emit_error_types(types: &[crate::core::ir::ErrorTypeDef], config: &ResolvedCrateConfig) -> String {
if types.is_empty() {
return String::new();
}
let package = jni_package(config);
let mut out = String::new();
for error_type in types {
gen_error_type_constructor(&mut out, error_type, &package);
}
out
}
pub(crate) fn emit_new_ir_sections(api: &ApiSurface, config: &ResolvedCrateConfig) -> String {
let mut out = String::new();
out.push_str(&emit_lifecycle_hooks(&api.lifecycle_hooks, api, config));
out.push_str(&emit_websocket_routes(&api.websocket_routes));
out.push_str(&emit_sse_routes(&api.sse_routes));
out.push_str(&emit_error_types(&api.error_types, config));
out
}
fn gen_lifecycle_hook_registration(
out: &mut String,
service: &crate::core::ir::ServiceDef,
hook: &LifecycleHookDef,
api: &ApiSurface,
core_import: &str,
package: &str,
service_bridge_class: &str,
) {
let hook_pascal = to_class_name(&hook.name);
let service_pascal = to_class_name(&service.name);
let hook_method = bridge_method_name(&service.name, &format!("register_{}", &hook.name));
let symbol = jni_symbol(package, service_bridge_class, &hook_method);
if let Some(_contract) = api
.handler_contracts
.iter()
.find(|c| c.trait_name == hook.callback_contract)
{
let bridge_name = format!("Jni{}Bridge", to_class_name(&hook.callback_contract));
let opaque_name = format!("{}Opaque", service.name);
out.push_str(&template_env::render(
"lifecycle_hook_registration.rs.jinja",
context! {
service_pascal => service_pascal,
hook_pascal => hook_pascal,
hook_name => hook.name,
symbol => symbol,
bridge_name => bridge_name,
core_import => core_import.to_string(),
contract_name => hook.callback_contract,
opaque_name => opaque_name,
is_async => hook.is_async,
},
));
}
}
fn gen_error_type_constructor(out: &mut String, error_type: &crate::core::ir::ErrorTypeDef, package: &str) {
let error_pascal = &error_type.name;
let status_code = error_type.http_status.as_u16();
let problem_details_type = error_type.problem_details_type.as_deref().unwrap_or("");
let method = format!("create{error_pascal}");
let symbol = jni_symbol(package, "Errors", &method);
let error_class_path = format!("{}/errors/{error_pascal}", package.replace('.', "/"));
out.push_str(&template_env::render(
"error_type_constructor.rs.jinja",
context! {
error_pascal => error_pascal,
error_name => error_type.name,
error_class_path => error_class_path,
symbol => symbol,
status_code => status_code,
problem_details_type => problem_details_type,
doc => error_type.doc,
},
));
}