uniffi_bindgen_library_mode/
main.rs
1use std::{
6 env::consts::{DLL_PREFIX, DLL_SUFFIX},
7 fmt, process,
8};
9
10use anyhow::{bail, Result};
11use camino::{Utf8Path, Utf8PathBuf};
12use clap::{Args, Parser, Subcommand};
13use uniffi_bindgen::bindings::{generate_swift_bindings, SwiftBindingsOptions};
14
15#[derive(Parser)]
16#[command(version, about, long_about = None)]
17struct Cli {
18 #[command(flatten)]
19 megazord: MegazordArg,
20 #[command(subcommand)]
21 command: Command,
22}
23
24#[derive(Args)]
25#[group(required = true, multiple = false)]
26struct MegazordArg {
27 #[arg(short, long, value_parser=["megazord", "megazord_ios", "megazord_focus", "cirrus", "nimbus-experimenter"])]
29 megazord: Option<String>,
30
31 #[arg(short, long)]
33 library: Option<Utf8PathBuf>,
34}
35
36#[derive(Subcommand)]
37enum Command {
38 Kotlin {
39 out_dir: Utf8PathBuf,
40 },
41 Swift {
42 out_dir: Utf8PathBuf,
43 #[arg(long)]
45 swift_sources: bool,
46 #[arg(long)]
48 headers: bool,
49 #[arg(long)]
51 modulemap: bool,
52 #[arg(long)]
54 xcframework: bool,
55 #[arg(long)]
57 module_name: Option<String>,
58 #[arg(long)]
60 modulemap_filename: Option<String>,
61 },
62 Python {
63 out_dir: Utf8PathBuf,
64 },
65}
66
67enum Language {
68 Kotlin,
69 Swift,
70 Python,
71}
72
73fn main() {
74 if let Err(e) = run_uniffi_bindgen(Cli::parse()) {
75 eprintln!("{e}");
76 std::process::exit(1);
77 }
78}
79
80fn run_uniffi_bindgen(cli: Cli) -> Result<()> {
81 let metadata = cargo_metadata::MetadataCommand::new()
82 .exec()
83 .expect("error running cargo metadata");
84 let megazord = Megazord::new(
85 &cli.megazord,
86 cli.command.language(),
87 &metadata.workspace_root,
88 )?;
89 let config_supplier = uniffi::CargoMetadataConfigSupplier::from(metadata);
90
91 match cli.command {
92 Command::Kotlin { out_dir } => {
93 uniffi::generate_bindings_library_mode(
94 &megazord.library_path,
95 None,
96 &uniffi::KotlinBindingGenerator,
97 &config_supplier,
98 None,
99 &out_dir,
100 false,
101 )?;
102 }
103 Command::Swift {
104 out_dir,
105 mut swift_sources,
106 mut headers,
107 mut modulemap,
108 xcframework,
109 module_name,
110 modulemap_filename,
111 } => {
112 let module_name = module_name.unwrap_or_else(|| "MozillaRustComponents".to_owned());
113 if !(swift_sources || headers || modulemap) {
115 swift_sources = true;
116 headers = true;
117 modulemap = true;
118 }
119
120 generate_swift_bindings(SwiftBindingsOptions {
121 out_dir,
122 generate_swift_sources: swift_sources,
123 generate_headers: headers,
124 generate_modulemap: modulemap,
125 library_path: megazord.library_path,
126 xcframework,
127 module_name: Some(module_name),
128 modulemap_filename,
129 metadata_no_deps: false,
130 })?;
131 }
132 Command::Python { out_dir } => {
133 uniffi::generate_bindings_library_mode(
134 &megazord.library_path,
135 None,
136 &uniffi::PythonBindingGenerator,
137 &config_supplier,
138 None,
139 &out_dir,
140 false,
141 )?;
142 }
143 };
144 Ok(())
145}
146
147struct Megazord {
148 library_path: Utf8PathBuf,
149}
150
151impl Megazord {
152 fn new(arg: &MegazordArg, language: Language, workspace_root: &Utf8Path) -> Result<Self> {
153 if let Some(crate_name) = &arg.megazord {
154 process::Command::new("cargo")
156 .args(["build", "--release", "-p", crate_name])
157 .spawn()?
158 .wait()?;
159
160 let filename = match language {
161 Language::Swift => format!("lib{}.a", crate_name.replace('-', "_")),
163 _ => format!(
165 "{}{}{}",
166 DLL_PREFIX,
167 crate_name.replace('-', "_"),
168 DLL_SUFFIX
169 ),
170 };
171 let library_path = workspace_root.join("target").join("release").join(filename);
172 Ok(Self { library_path })
173 } else if let Some(library_path) = &arg.library {
174 Ok(Self {
175 library_path: library_path.clone(),
176 })
177 } else {
178 bail!("Neither megazord nor library specified")
179 }
180 }
181}
182
183impl fmt::Display for Language {
184 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
185 let name = match self {
186 Self::Swift => "swift",
187 Self::Kotlin => "kotlin",
188 Self::Python => "python",
189 };
190 write!(f, "{}", name)
191 }
192}
193
194impl Command {
195 fn language(&self) -> Language {
196 match self {
197 Self::Kotlin { .. } => Language::Kotlin,
198 Self::Swift { .. } => Language::Swift,
199 Self::Python { .. } => Language::Python,
200 }
201 }
202}