use boltffi_bindgen::render::jni::{JniEmitter, JniLowerer, JvmBindingStyle};
use boltffi_bindgen::render::kotlin::{
KotlinApiStyle as BindgenKotlinApiStyle, KotlinDesktopLoader as BindgenKotlinDesktopLoader,
KotlinEmitter, KotlinLowerer,
};
use boltffi_bindgen::{FactoryStyle as BindgenFactoryStyle, KotlinOptions};
use crate::cli::{CliError, Result};
use crate::commands::generate::generator::{
GenerateRequest, LanguageGenerator, ScanPointerWidth, bindgen_type_mappings,
};
use crate::config::{
FactoryStyle as ConfigFactoryStyle, KotlinApiStyle as ConfigKotlinApiStyle,
KotlinDesktopLoader as ConfigKotlinDesktopLoader, Target,
};
pub struct KotlinGenerator;
impl KotlinGenerator {
fn kotlin_options(request: &GenerateRequest<'_>, module_name: &str) -> KotlinOptions {
let factory_style = match request.config().android_kotlin_factory_style() {
ConfigFactoryStyle::Constructors => BindgenFactoryStyle::Constructors,
ConfigFactoryStyle::CompanionMethods => BindgenFactoryStyle::CompanionMethods,
};
KotlinOptions {
factory_style,
api_style: match request.config().android_kotlin_api_style() {
ConfigKotlinApiStyle::TopLevel => BindgenKotlinApiStyle::TopLevel,
ConfigKotlinApiStyle::ModuleObject => BindgenKotlinApiStyle::ModuleObject,
},
module_object_name: Some(module_name.to_string()),
library_name: Some(boltffi_bindgen::load_library_name(
&request.config().resolved_android_kotlin_library_name(),
)),
desktop_jni_library_name: None,
desktop_fallback_library_name: None,
desktop_loader: match request.config().android_kotlin_desktop_loader() {
ConfigKotlinDesktopLoader::Bundled => BindgenKotlinDesktopLoader::Bundled,
ConfigKotlinDesktopLoader::System => BindgenKotlinDesktopLoader::System,
ConfigKotlinDesktopLoader::None => BindgenKotlinDesktopLoader::None,
},
}
}
}
impl LanguageGenerator for KotlinGenerator {
const TARGET: Target = Target::Kotlin;
fn generate(request: &GenerateRequest<'_>) -> Result<()> {
if !request.config().is_android_enabled() {
return Err(CliError::CommandFailed {
command: "targets.android.enabled = false".to_string(),
status: None,
});
}
let package_name = request.config().android_kotlin_package();
let package_path = package_name.replace('.', "/");
let output_directory = request
.output_override()
.map(ToOwned::to_owned)
.unwrap_or_else(|| request.config().android_kotlin_output());
let kotlin_directory = output_directory.join(&package_path);
let jni_directory = output_directory.join("jni");
request.ensure_output_directory(&kotlin_directory)?;
request.ensure_output_directory(&jni_directory)?;
let lowered_crate = request.lowered_crate(ScanPointerWidth::Flexible)?;
let module_name = request.config().android_kotlin_module_name();
let kotlin_module = KotlinLowerer::new(
&lowered_crate.ffi_contract,
&lowered_crate.abi_contract,
package_name.clone(),
module_name.clone(),
Self::kotlin_options(request, &module_name),
)
.with_type_mappings(bindgen_type_mappings(
request.config().kotlin_type_mappings(),
))
.lower();
let kotlin_source = KotlinEmitter::emit(&kotlin_module);
let kotlin_path = kotlin_directory.join(format!("{module_name}.kt"));
request.write_output(&kotlin_path, kotlin_source)?;
let jni_module = JniLowerer::new(
&lowered_crate.ffi_contract,
&lowered_crate.abi_contract,
package_name,
module_name,
)
.with_jvm_binding_style(JvmBindingStyle::Kotlin)
.lower();
let jni_source = JniEmitter::emit(&jni_module);
let jni_path = jni_directory.join("jni_glue.c");
request.write_output(&jni_path, jni_source)
}
}