fn is_enum_param(ty: &str, enum_names: &std::collections::HashSet<&str>) -> bool {
enum_names.contains(ty)
}
fn extract_named_type(ty: &str) -> Option<&str> {
if !ty.is_empty() && ty.chars().next().unwrap().is_uppercase() {
Some(ty)
} else {
None
}
}
fn emit_constructor_jni_external_funs(
out: &mut String,
api: &ApiSurface,
config: &ResolvedCrateConfig,
exception_class: &str,
) {
let opaque_names: std::collections::HashSet<&str> = api
.types
.iter()
.filter(|t| t.is_opaque && !t.is_trait)
.map(|t| t.name.as_str())
.collect();
let enum_names: std::collections::HashSet<&str> = api.enums.iter().map(|e| e.name.as_str()).collect();
let mut sorted: Vec<(&str, &ClientConstructorConfig)> = config
.client_constructors
.iter()
.filter(|(name, _)| opaque_names.contains(name.as_str()))
.map(|(name, ctor)| (name.as_str(), ctor))
.collect();
sorted.sort_by_key(|(name, _)| *name);
if sorted.is_empty() {
return;
}
out.push_str("\n // JNI constructor external funs — implementations are Rust JNI shims.\n");
for (type_name, ctor) in sorted {
let native_name = format!("nativeNew{}", to_pascal_case(type_name));
let params: Vec<String> = ctor
.params
.iter()
.map(|p| {
let kt_ty = if p.ty.contains("c_char") {
"String".to_string()
} else if is_enum_param(&p.ty, &enum_names) {
"Int".to_string()
} else {
"Long".to_string()
};
let param_name = to_lower_camel(&p.name);
format!("{param_name}: {kt_ty}")
})
.collect();
let params_str = params.join(", ");
push_jni_external_fun(
out,
&native_name,
¶ms_str,
Some("Long".to_string()),
Some(exception_class),
);
}
}
fn emit_jni_client_factory(
class_name: &str,
bridge_name: &str,
ctor: &ClientConstructorConfig,
api: &ApiSurface,
out: &mut String,
) {
let native_name = format!("nativeNew{}", to_pascal_case(class_name));
let enum_names: std::collections::HashSet<&str> = api.enums.iter().map(|e| e.name.as_str()).collect();
let params: Vec<String> = ctor
.params
.iter()
.map(|p| {
let kt_ty = if p.ty.contains("c_char") {
"String".to_string()
} else if is_enum_param(&p.ty, &enum_names) {
let enum_name = extract_named_type(&p.ty).unwrap_or("Any");
enum_name.to_string()
} else {
"Long".to_string()
};
let param_name = to_lower_camel(&p.name);
format!("{param_name}: {kt_ty}")
})
.collect();
let call_args: Vec<String> = ctor
.params
.iter()
.map(|p| {
let param_name = to_lower_camel(&p.name);
if is_enum_param(&p.ty, &enum_names) {
format!("{param_name}.ordinal")
} else {
param_name
}
})
.collect();
let params_str = params.join(", ");
let call_args_str = call_args.join(", ");
out.push_str(&template_env::render(
"jni_client_constructor.jinja",
minijinja::context! {
params => params_str,
class_name => class_name,
bridge_name => bridge_name,
native_name => native_name,
call_args => call_args_str,
},
));
}