use alef_codegen::generators::trait_bridge::{TraitBridgeGenerator, TraitBridgeSpec};
use alef_core::ir::MethodDef;
use super::{to_lower_camel, to_pascal_case};
pub struct KotlinJvmBridgeGenerator {
pub java_package: String,
}
impl TraitBridgeGenerator for KotlinJvmBridgeGenerator {
fn foreign_object_type(&self) -> &str {
""
}
fn bridge_imports(&self) -> Vec<String> {
vec![]
}
fn gen_sync_method_body(&self, _method: &MethodDef, _spec: &TraitBridgeSpec) -> String {
String::new()
}
fn gen_async_method_body(&self, _method: &MethodDef, _spec: &TraitBridgeSpec) -> String {
String::new()
}
fn gen_constructor(&self, _spec: &TraitBridgeSpec) -> String {
String::new()
}
fn gen_registration_fn(&self, spec: &TraitBridgeSpec) -> String {
let Some(register_fn) = spec.bridge_config.register_fn.as_deref() else {
return String::new();
};
let trait_pascal = to_pascal_case(&spec.trait_def.name);
let kotlin_fn = to_lower_camel(register_fn);
let bridge_class = format!("{}.{}Bridge", self.java_package, trait_pascal);
let iface = format!("{}.I{}", self.java_package, trait_pascal);
let java_method = format!("register{trait_pascal}");
format!(" fun {kotlin_fn}(impl: {iface}) {{\n {bridge_class}.{java_method}(impl)\n }}\n")
}
fn gen_unregistration_fn(&self, spec: &TraitBridgeSpec) -> String {
let Some(unregister_fn) = spec.bridge_config.unregister_fn.as_deref() else {
return String::new();
};
let trait_pascal = to_pascal_case(&spec.trait_def.name);
let kotlin_fn = to_lower_camel(unregister_fn);
let bridge_class = format!("{}.{}Bridge", self.java_package, trait_pascal);
let java_method = format!("unregister{trait_pascal}");
format!(" fun {kotlin_fn}(name: String) {{\n {bridge_class}.{java_method}(name)\n }}\n")
}
fn gen_clear_fn(&self, spec: &TraitBridgeSpec) -> String {
let Some(clear_fn) = spec.bridge_config.clear_fn.as_deref() else {
return String::new();
};
let trait_pascal = to_pascal_case(&spec.trait_def.name);
let kotlin_fn = to_lower_camel(clear_fn);
let bridge_class = format!("{}.{}Bridge", self.java_package, trait_pascal);
let java_method = format!("clearAll{trait_pascal}");
format!(" fun {kotlin_fn}() {{\n {bridge_class}.{java_method}()\n }}\n")
}
}
#[cfg(test)]
mod tests {
use super::*;
use alef_core::config::TraitBridgeConfig;
use alef_core::ir::TypeDef;
fn make_bridge_config(
trait_name: &str,
register_fn: Option<&str>,
unregister_fn: Option<&str>,
clear_fn: Option<&str>,
) -> TraitBridgeConfig {
TraitBridgeConfig {
trait_name: trait_name.to_string(),
super_trait: None,
registry_getter: Some("demo::get_registry".to_string()),
register_fn: register_fn.map(|s| s.to_string()),
unregister_fn: unregister_fn.map(|s| s.to_string()),
clear_fn: clear_fn.map(|s| s.to_string()),
type_alias: None,
param_name: None,
register_extra_args: None,
exclude_languages: vec![],
ffi_skip_methods: Vec::new(),
bind_via: alef_core::config::BridgeBinding::FunctionParam,
options_type: None,
options_field: None,
context_type: None,
result_type: None,
}
}
fn make_trait_def(name: &str) -> TypeDef {
TypeDef {
name: name.to_string(),
rust_path: format!("demo::{name}"),
original_rust_path: String::new(),
fields: vec![],
methods: vec![],
is_opaque: false,
is_clone: false,
is_copy: false,
is_trait: true,
has_default: false,
has_stripped_cfg_fields: false,
is_return_type: false,
serde_rename_all: None,
has_serde: false,
super_traits: vec![],
doc: String::new(),
cfg: None,
binding_excluded: false,
binding_exclusion_reason: None,
}
}
fn make_spec<'a>(trait_def: &'a TypeDef, bridge_cfg: &'a TraitBridgeConfig) -> TraitBridgeSpec<'a> {
TraitBridgeSpec {
trait_def,
bridge_config: bridge_cfg,
core_import: "demo",
wrapper_prefix: "Kotlin",
type_paths: std::collections::HashMap::new(),
error_type: "DemoError".to_string(),
error_constructor: "DemoError::from({msg})".to_string(),
}
}
fn make_generator() -> KotlinJvmBridgeGenerator {
KotlinJvmBridgeGenerator {
java_package: "dev.kreuzberg".to_string(),
}
}
#[test]
fn registration_fn_emits_kotlin_fun_when_set() {
let cfg = make_bridge_config("OcrBackend", Some("register_ocr_backend"), None, None);
let trait_def = make_trait_def("OcrBackend");
let spec = make_spec(&trait_def, &cfg);
let generator = make_generator();
let out = generator.gen_registration_fn(&spec);
assert!(!out.is_empty(), "should emit non-empty string when register_fn is set");
assert!(
out.contains("fun registerOcrBackend(impl: dev.kreuzberg.IOcrBackend)"),
"must have correct signature: {out}"
);
assert!(
out.contains("dev.kreuzberg.OcrBackendBridge.registerOcrBackend(impl)"),
"must delegate to Java bridge: {out}"
);
}
#[test]
fn registration_fn_returns_empty_when_none() {
let cfg = make_bridge_config("OcrBackend", None, None, None);
let trait_def = make_trait_def("OcrBackend");
let spec = make_spec(&trait_def, &cfg);
let generator = make_generator();
assert!(generator.gen_registration_fn(&spec).is_empty());
}
#[test]
fn unregistration_fn_emits_kotlin_fun_when_set() {
let cfg = make_bridge_config(
"OcrBackend",
Some("register_ocr_backend"),
Some("unregister_ocr_backend"),
None,
);
let trait_def = make_trait_def("OcrBackend");
let spec = make_spec(&trait_def, &cfg);
let generator = make_generator();
let out = generator.gen_unregistration_fn(&spec);
assert!(
!out.is_empty(),
"should emit non-empty string when unregister_fn is set"
);
assert!(
out.contains("fun unregisterOcrBackend(name: String)"),
"must have correct signature: {out}"
);
assert!(
out.contains("dev.kreuzberg.OcrBackendBridge.unregisterOcrBackend(name)"),
"must delegate to Java bridge: {out}"
);
}
#[test]
fn unregistration_fn_returns_empty_when_none() {
let cfg = make_bridge_config("OcrBackend", Some("register_ocr_backend"), None, None);
let trait_def = make_trait_def("OcrBackend");
let spec = make_spec(&trait_def, &cfg);
let generator = make_generator();
assert!(generator.gen_unregistration_fn(&spec).is_empty());
}
#[test]
fn clear_fn_emits_kotlin_fun_when_set() {
let cfg = make_bridge_config(
"OcrBackend",
Some("register_ocr_backend"),
None,
Some("clear_ocr_backends"),
);
let trait_def = make_trait_def("OcrBackend");
let spec = make_spec(&trait_def, &cfg);
let generator = make_generator();
let out = generator.gen_clear_fn(&spec);
assert!(!out.is_empty(), "should emit non-empty string when clear_fn is set");
assert!(
out.contains("fun clearOcrBackends()"),
"must have correct no-arg signature: {out}"
);
assert!(
out.contains("dev.kreuzberg.OcrBackendBridge.clearAllOcrBackend()"),
"must delegate to Java bridge: {out}"
);
}
#[test]
fn clear_fn_returns_empty_when_none() {
let cfg = make_bridge_config("OcrBackend", Some("register_ocr_backend"), None, None);
let trait_def = make_trait_def("OcrBackend");
let spec = make_spec(&trait_def, &cfg);
let generator = make_generator();
assert!(generator.gen_clear_fn(&spec).is_empty());
}
#[test]
fn all_fns_return_empty_when_all_config_fields_none() {
let cfg = make_bridge_config("Plugin", None, None, None);
let trait_def = make_trait_def("Plugin");
let spec = make_spec(&trait_def, &cfg);
let generator = make_generator();
assert!(generator.gen_registration_fn(&spec).is_empty());
assert!(generator.gen_unregistration_fn(&spec).is_empty());
assert!(generator.gen_clear_fn(&spec).is_empty());
}
}