/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use crate::{ bindings::GenerateOptions, interface::rename, BindgenLoader, Component, ComponentInterface, Result, }; use camino::{Utf8Path, Utf8PathBuf}; use fs_err as fs; use std::collections::HashMap; use std::process::Command; mod gen_kotlin; use gen_kotlin::{generate_bindings, Config}; #[cfg(feature = "bindgen-tests")] pub mod test; /// Generate Kotlin bindings pub fn generate(loader: &BindgenLoader, options: GenerateOptions) -> Result<()> { let metadata = loader.load_metadata(&options.source)?; let cis = loader.load_cis(metadata)?; let cdylib = loader.library_name(&options.source).map(|l| l.to_string()); let mut components = loader.load_components(cis, |ci, toml| parse_config(ci, toml, cdylib.clone()))?; apply_renames(&mut components); for c in components.iter_mut() { // Call derive_ffi_functions after `apply_renames` c.ci.derive_ffi_funcs()?; } for Component { ci, config, .. } in components { if let Some(crate_filter) = &options.crate_filter { if ci.crate_name() != crate_filter { continue; } } let mut kt_file = full_bindings_path(&config, &options.out_dir); fs::create_dir_all(&kt_file)?; kt_file.push(format!("{}.kt", ci.namespace())); fs::write(&kt_file, generate_bindings(&config, &ci)?)?; if options.format { println!( "Code generation complete, formatting with ktlint (use --no-format to disable)" ); if let Err(e) = Command::new("ktlint").arg("-F").arg(&kt_file).output() { println!( "Warning: Unable to auto-format {} using ktlint: {e:?}", kt_file.file_name().unwrap(), ); } } } Ok(()) } fn full_bindings_path(config: &Config, out_dir: &Utf8Path) -> Utf8PathBuf { let package_path: Utf8PathBuf = config.package_name().split('.').collect(); Utf8PathBuf::from(out_dir).join(package_path) } fn parse_config( ci: &ComponentInterface, root_toml: toml::Value, cdylib: Option, ) -> Result { let mut config: Config = match root_toml.get("bindings").and_then(|b| b.get("kotlin")) { Some(v) => v.clone().try_into()?, None => Default::default(), }; config .package_name .get_or_insert_with(|| format!("uniffi.{}", ci.namespace())); config.cdylib_name.get_or_insert_with(|| { cdylib .clone() .unwrap_or_else(|| format!("uniffi_{}", ci.namespace())) }); Ok(config) } // A helper for renaming items. fn apply_renames(components: &mut Vec>) { // Collect all rename configurations from all components, keyed by module_path let mut module_renames = HashMap::new(); for c in components.iter() { if !c.config.rename.is_empty() { let module_path = c.ci.crate_name().to_string(); module_renames.insert(module_path, c.config.rename.clone()); } } // Apply rename configurations to all components if !module_renames.is_empty() { for c in &mut *components { rename(&mut c.ci, &module_renames); } } // We need to update package names let packages = HashMap::::from_iter( components .iter() .map(|c| (c.ci.crate_name().to_string(), c.config.package_name())), ); for c in components { for (ext_crate, ext_package) in &packages { if ext_crate != c.ci.crate_name() && !c.config.external_packages.contains_key(ext_crate) { c.config .external_packages .insert(ext_crate.to_string(), ext_package.clone()); } } } }