1use anyhow::Result;
2use camino::Utf8PathBuf;
3use clap::{Parser, Subcommand};
4use std::{
5 collections::HashMap,
6 fs::{self},
7};
8use uniffi_bindgen::{BindingGenerator, Component, ComponentInterface, GenerationSettings};
9
10mod gen_java;
11
12pub struct JavaBindingGenerator;
13impl BindingGenerator for JavaBindingGenerator {
14 type Config = gen_java::Config;
15
16 fn new_config(&self, root_toml: &toml::Value) -> Result<Self::Config> {
17 Ok(
18 match root_toml.get("bindings").and_then(|b| b.get("java")) {
19 Some(v) => v.clone().try_into()?,
20 None => Default::default(),
21 },
22 )
23 }
24
25 fn update_component_configs(
26 &self,
27 settings: &GenerationSettings,
28 components: &mut Vec<Component<Self::Config>>,
29 ) -> Result<()> {
30 for c in &mut *components {
31 c.config
32 .package_name
33 .get_or_insert_with(|| format!("uniffi.{}", c.ci.namespace()));
34 c.config.cdylib_name.get_or_insert_with(|| {
35 settings
36 .cdylib
37 .clone()
38 .unwrap_or_else(|| format!("uniffi_{}", c.ci.namespace()))
39 });
40 }
41 let packages = HashMap::<String, String>::from_iter(
43 components
44 .iter()
45 .map(|c| (c.ci.crate_name().to_string(), c.config.package_name())),
46 );
47 for c in components {
48 for (ext_crate, ext_package) in &packages {
49 if ext_crate != c.ci.crate_name()
50 && !c.config.external_packages.contains_key(ext_crate)
51 {
52 c.config
53 .external_packages
54 .insert(ext_crate.to_string(), ext_package.clone());
55 }
56 }
57 }
58 Ok(())
59 }
60
61 fn write_bindings(
62 &self,
63 settings: &GenerationSettings,
64 components: &[Component<Self::Config>],
65 ) -> anyhow::Result<()> {
66 let filename_capture = regex::Regex::new(
67 r"(?m)^(?:public\s)?(?:final\s)?(?:sealed\s)?(?:abstract\s)?(?:static\s)?(?:class|interface|enum|record)\s(\w+)",
68 )
69 .unwrap();
70 for Component { ci, config, .. } in components {
71 let bindings_str = gen_java::generate_bindings(config, ci)?;
72 let java_package_out_dir = &settings.out_dir.join(
73 config
74 .package_name()
75 .split('.')
76 .collect::<Vec<_>>()
77 .join("/"),
78 );
79 fs::create_dir_all(java_package_out_dir)?;
80 let package_line = format!("package {};", config.package_name());
81 let split_classes = bindings_str.split(&package_line);
82 let writable = split_classes
83 .map(|file| (filename_capture.captures(file), file))
84 .filter(|(x, _)| x.is_some())
85 .map(|(captures, file)| (captures.unwrap().get(1).unwrap().as_str(), file))
86 .collect::<Vec<_>>();
87 for (filename, file) in writable {
88 let java_file_location = java_package_out_dir.join(format!("{}.java", filename));
89 fs::write(&java_file_location, format!("{}\n{}", package_line, file))?;
90 }
91 if settings.try_format_code {
92 }
98 }
99 Ok(())
100 }
101}
102
103#[derive(Parser)]
104#[clap(name = "uniffi-bindgen-java")]
105#[clap(version = clap::crate_version!())]
106#[clap(propagate_version = true, disable_help_subcommand = true)]
107struct Cli {
109 #[clap(subcommand)]
110 command: Commands,
111}
112
113#[derive(Subcommand)]
114enum Commands {
115 Generate {
117 #[clap(long, short)]
119 out_dir: Option<Utf8PathBuf>,
120
121 #[clap(long, short)]
123 no_format: bool,
124
125 #[clap(long, short)]
127 config: Option<Utf8PathBuf>,
128
129 #[clap(long)]
131 lib_file: Option<Utf8PathBuf>,
132
133 #[clap(long = "library")]
135 library_mode: bool,
136
137 #[clap(long = "crate")]
141 crate_name: Option<String>,
142
143 source: Utf8PathBuf,
145
146 #[clap(long)]
152 metadata_no_deps: bool,
153 },
154 Scaffolding {
156 #[clap(long, short)]
158 out_dir: Option<Utf8PathBuf>,
159
160 #[clap(long, short)]
162 no_format: bool,
163
164 udl_file: Utf8PathBuf,
166 },
167 PrintRepr {
169 path: Utf8PathBuf,
171 },
172}
173
174pub fn run_main() -> Result<()> {
175 let cli = Cli::parse();
176 match cli.command {
177 Commands::Generate {
178 out_dir,
179 no_format,
180 config,
181 lib_file,
182 library_mode,
183 crate_name,
184 source,
185 metadata_no_deps,
186 } => {
187 if library_mode {
188 use uniffi_bindgen::library_mode::generate_bindings;
189 if lib_file.is_some() {
190 panic!("--lib-file is not compatible with --library.")
191 }
192 let out_dir = out_dir.expect("--out-dir is required when using --library");
193
194 let config_supplier = {
195 use uniffi_bindgen::cargo_metadata::CrateConfigSupplier;
196 let mut cmd = cargo_metadata::MetadataCommand::new();
197 if metadata_no_deps {
198 cmd.no_deps();
199 }
200 let metadata = cmd.exec()?;
201 CrateConfigSupplier::from(metadata)
202 };
203
204 generate_bindings(
205 &source,
206 crate_name,
207 &JavaBindingGenerator,
208 &config_supplier,
209 config.as_deref(),
210 &out_dir,
211 !no_format,
212 )?;
213 } else {
214 use uniffi_bindgen::generate_bindings;
215 generate_bindings(
216 &source,
217 config.as_deref(),
218 JavaBindingGenerator,
219 out_dir.as_deref(),
220 lib_file.as_deref(),
221 crate_name.as_deref(),
222 !no_format,
223 )?;
224 }
225 }
226 Commands::Scaffolding {
227 out_dir,
228 no_format,
229 udl_file,
230 } => {
231 uniffi_bindgen::generate_component_scaffolding(
232 &udl_file,
233 out_dir.as_deref(),
234 !no_format,
235 )?;
236 }
237 Commands::PrintRepr { path } => {
238 uniffi_bindgen::print_repr(&path)?;
239 }
240 };
241 Ok(())
242}