From 61828f58a341a9c8e0912a87b0a04f5e2a9ce153 Mon Sep 17 00:00:00 2001 From: James Hugman Date: Thu, 28 Nov 2024 17:03:28 +0000 Subject: [PATCH 01/13] Add switches with AbiFlavor --- .github/workflows/ci.yml | 3 + crates/ubrn_bindgen/src/bindings/mod.rs | 14 +- crates/ubrn_bindgen/src/bindings/switches.rs | 50 ++++++ crates/ubrn_bindgen/src/lib.rs | 2 +- crates/ubrn_cli/src/generate.rs | 12 +- crates/ubrn_cli/src/main.rs | 12 ++ .../tests/bindings/test_arithmetic.ts | 2 +- xtask/src/main.rs | 146 ++++++++++++++++++ xtask/src/run/generate_bindings.rs | 7 +- xtask/src/run/mod.rs | 33 ++-- 10 files changed, 259 insertions(+), 22 deletions(-) create mode 100644 crates/ubrn_bindgen/src/bindings/switches.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d546f237..6284eabe 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -46,6 +46,9 @@ jobs: - name: Run tests run: cargo test --verbose + - name: Run tests for binaries + run: cargo test --bins + integration-tests-generation: name: 🧩 Integration tests (generation) runs-on: ubuntu-latest diff --git a/crates/ubrn_bindgen/src/bindings/mod.rs b/crates/ubrn_bindgen/src/bindings/mod.rs index 52ffa0be..3b0ab0ea 100644 --- a/crates/ubrn_bindgen/src/bindings/mod.rs +++ b/crates/ubrn_bindgen/src/bindings/mod.rs @@ -6,6 +6,7 @@ pub mod metadata; pub(crate) mod react_native; +pub(crate) mod switches; pub(crate) mod type_map; use std::{fs, str::FromStr}; @@ -14,6 +15,7 @@ use anyhow::Result; use askama::Template; use camino::{Utf8Path, Utf8PathBuf}; use clap::{command, Args}; +pub use switches::{AbiFlavor, SwitchArgs}; use ubrn_common::{mk_dir, CrateMetadata}; use uniffi_bindgen::cargo_metadata::CrateConfigSupplier; @@ -27,11 +29,17 @@ pub struct BindingsArgs { source: SourceArgs, #[command(flatten)] output: OutputArgs, + #[command(flatten)] + switches: SwitchArgs, } impl BindingsArgs { - pub fn new(source: SourceArgs, output: OutputArgs) -> Self { - Self { source, output } + pub fn new(switches: SwitchArgs, source: SourceArgs, output: OutputArgs) -> Self { + Self { + switches, + source, + output, + } } pub fn ts_dir(&self) -> &Utf8Path { @@ -54,7 +62,7 @@ pub struct OutputArgs { ts_dir: Utf8PathBuf, /// The directory in which to put the generated C++. - #[clap(long)] + #[clap(long, alias = "abi-dir")] cpp_dir: Utf8PathBuf, } diff --git a/crates/ubrn_bindgen/src/bindings/switches.rs b/crates/ubrn_bindgen/src/bindings/switches.rs new file mode 100644 index 00000000..dc7992ff --- /dev/null +++ b/crates/ubrn_bindgen/src/bindings/switches.rs @@ -0,0 +1,50 @@ +/* + * 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 clap::{Args, ValueEnum}; + +#[derive(Args, Clone, Debug)] +pub struct SwitchArgs { + /// The flavor of bindings to produce. + #[clap(long, default_value = "jsi")] + pub flavor: AbiFlavor, +} + +impl Default for SwitchArgs { + fn default() -> Self { + Self { + flavor: AbiFlavor::Jsi, + } + } +} + +impl SwitchArgs { + pub fn flavor(&self) -> AbiFlavor { + self.flavor.clone() + } +} + +#[derive(Clone, Debug, ValueEnum, PartialEq)] +pub enum AbiFlavor { + Jsi, + Wasm, +} + +#[allow(dead_code)] +impl AbiFlavor { + pub(crate) fn supports_text_encoder(&self) -> bool { + matches!(self, Self::Wasm) + } + pub(crate) fn supports_finalization_registry(&self) -> bool { + matches!(self, Self::Wasm) + } + pub(crate) fn needs_cpp(&self) -> bool { + matches!(self, Self::Jsi) + } + pub(crate) fn needs_rust(&self) -> bool { + matches!(self, Self::Jsi) + } +} diff --git a/crates/ubrn_bindgen/src/lib.rs b/crates/ubrn_bindgen/src/lib.rs index bb81cb66..7acdb246 100644 --- a/crates/ubrn_bindgen/src/lib.rs +++ b/crates/ubrn_bindgen/src/lib.rs @@ -6,4 +6,4 @@ mod bindings; pub use bindings::metadata::ModuleMetadata; -pub use bindings::{BindingsArgs, OutputArgs, SourceArgs}; +pub use bindings::{AbiFlavor, BindingsArgs, OutputArgs, SourceArgs, SwitchArgs}; diff --git a/crates/ubrn_cli/src/generate.rs b/crates/ubrn_cli/src/generate.rs index 6e94173c..68c53740 100644 --- a/crates/ubrn_cli/src/generate.rs +++ b/crates/ubrn_cli/src/generate.rs @@ -7,7 +7,7 @@ use anyhow::Result; use camino::Utf8PathBuf; use clap::{self, Args, Subcommand}; use std::convert::TryFrom; -use ubrn_bindgen::{BindingsArgs, OutputArgs, SourceArgs}; +use ubrn_bindgen::{AbiFlavor, BindingsArgs, OutputArgs, SourceArgs, SwitchArgs}; use crate::{codegen::TurboModuleArgs, config::ProjectConfig, Platform}; @@ -62,6 +62,9 @@ pub(crate) struct GenerateAllArgs { #[clap(long)] config: Utf8PathBuf, + #[command(flatten)] + switches: SwitchArgs, + /// A path to staticlib file. lib_file: Utf8PathBuf, } @@ -98,11 +101,17 @@ impl GenerateAllCommand { } } + fn switches(&self) -> SwitchArgs { + let flavor = self.platform.as_ref().map_or(AbiFlavor::Jsi, |p| p.into()); + SwitchArgs { flavor } + } + pub(crate) fn run(&self) -> Result<()> { let project = self.project_config()?; let root = project.project_root(); let pwd = ubrn_common::pwd()?; let lib_file = pwd.join(&self.lib_file); + let switches = self.switches(); let modules = { ubrn_common::cd(&project.crate_.crate_dir()?)?; let ts_dir = project.bindings.ts_path(root); @@ -115,6 +124,7 @@ impl GenerateAllCommand { } let manifest_path = project.crate_.manifest_path()?; let bindings = BindingsArgs::new( + switches.clone(), SourceArgs::library(&lib_file).with_config(config), OutputArgs::new(&ts_dir, &cpp_dir, false), ); diff --git a/crates/ubrn_cli/src/main.rs b/crates/ubrn_cli/src/main.rs index d66bd354..283ef40c 100644 --- a/crates/ubrn_cli/src/main.rs +++ b/crates/ubrn_cli/src/main.rs @@ -7,6 +7,7 @@ use anyhow::{Error, Result}; use camino::Utf8PathBuf; use clap::Parser; use config::ProjectConfig; +use ubrn_bindgen::AbiFlavor; mod android; mod building; @@ -55,4 +56,15 @@ impl TryFrom for ProjectConfig { pub(crate) enum Platform { Android, Ios, + #[allow(unused)] + Wasm, +} + +impl From<&Platform> for AbiFlavor { + fn from(value: &Platform) -> Self { + match value { + Platform::Wasm => AbiFlavor::Wasm, + _ => AbiFlavor::Jsi, + } + } } diff --git a/fixtures/arithmetic/tests/bindings/test_arithmetic.ts b/fixtures/arithmetic/tests/bindings/test_arithmetic.ts index d8861197..b54689bf 100644 --- a/fixtures/arithmetic/tests/bindings/test_arithmetic.ts +++ b/fixtures/arithmetic/tests/bindings/test_arithmetic.ts @@ -4,7 +4,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/ */ // fixture=arithmetic -// cargo run --manifest-path ./crates/uniffi-bindgen-react-native/Cargo.toml -- ./fixtures/${fixture}/src/${fixture}.udl --out-dir ./fixtures/${fixture}/generated +// cargo run --manifest-path ./crates/ubrn_cli/Cargo.toml -- ./fixtures/${fixture}/src/${fixture}.udl --out-dir ./fixtures/${fixture}/generated // cargo xtask run ./fixtures/${fixture}/tests/bindings/test_${fixture}.ts --cpp ./fixtures/${fixture}/generated/${fixture}.cpp --crate ./fixtures/${fixture} import * as rust from "../../generated/arithmetic"; diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 03d81b8b..56aaf5b4 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -47,3 +47,149 @@ fn main() -> Result<()> { Cmd::Fmt(c) => c.run(), } } + +#[cfg(test)] +mod cli_test { + use clap::Parser; + use run::{generate_bindings::GenerateBindingsArg, rust_crate::CrateArg}; + use ubrn_bindgen::AbiFlavor; + + use super::*; + use crate::run::typescript::EntryArg; + + fn parse(args: &[&str]) -> CliArgs { + let mut all_args = vec![""]; + all_args.extend_from_slice(args); + + CliArgs::parse_from(all_args) + } + + #[test] + fn test_bootstrap_command() { + assert!(matches!(parse(&["bootstrap"]).cmd, Cmd::Bootstrap(_))); + } + + #[test] + fn test_run_command_js_only() { + let cmd = parse(&["run", "file.ts"]).cmd; + + if let Cmd::Run(RunCmd { + js_file: EntryArg { file, .. }, + generate_bindings: None, + crate_: None, + .. + }) = &cmd + { + assert_eq!(file.as_str(), "file.ts"); + } else { + panic!("fail") + } + } + + #[test] + fn test_run_command_with_crate() { + let cmd = parse(&[ + "run", + "--cpp-dir", + "cpp-dir/", + "--ts-dir", + "ts-dir/", + "--crate", + "crate-dir/", + "file.ts", + ]) + .cmd; + + let Cmd::Run(RunCmd { + js_file: EntryArg { file, .. }, + generate_bindings: + Some(GenerateBindingsArg { + ts_dir, cpp_dir, .. + }), + crate_: Some(CrateArg { crate_dir, .. }), + switches, + .. + }) = &cmd + else { + panic!("fail") + }; + assert_eq!(file.as_str(), "file.ts"); + assert_eq!(ts_dir.as_deref().map(|f| f.as_str()), Some("ts-dir/")); + assert_eq!(cpp_dir.as_deref().map(|f| f.as_str()), Some("cpp-dir/")); + assert_eq!(crate_dir.as_deref().map(|f| f.as_str()), Some("crate-dir/")); + assert_eq!(switches.flavor, AbiFlavor::Jsi); + } + + #[test] + fn test_run_command_with_wasm() { + let cmd = parse(&[ + "run", + "--cpp-dir", + "cpp-dir/", + "--ts-dir", + "ts-dir/", + "--crate", + "crate-dir/", + "--flavor", + "wasm", + "file.ts", + ]) + .cmd; + + let Cmd::Run(RunCmd { + js_file: EntryArg { file, .. }, + generate_bindings: + Some(GenerateBindingsArg { + ts_dir, cpp_dir, .. + }), + crate_: Some(CrateArg { crate_dir, .. }), + switches, + .. + }) = &cmd + else { + panic!("fail") + }; + + assert_eq!(file.as_str(), "file.ts"); + assert_eq!(ts_dir.as_deref().map(|f| f.as_str()), Some("ts-dir/")); + assert_eq!(cpp_dir.as_deref().map(|f| f.as_str()), Some("cpp-dir/")); + assert_eq!(crate_dir.as_deref().map(|f| f.as_str()), Some("crate-dir/")); + assert_eq!(switches.flavor, AbiFlavor::Wasm); + } + + #[test] + fn test_run_command_with_jsi() { + let cmd = parse(&[ + "run", + "--cpp-dir", + "cpp-dir/", + "--ts-dir", + "ts-dir/", + "--crate", + "crate-dir/", + "--flavor", + "jsi", + "file.ts", + ]) + .cmd; + + let Cmd::Run(RunCmd { + js_file: EntryArg { file, .. }, + generate_bindings: + Some(GenerateBindingsArg { + ts_dir, cpp_dir, .. + }), + crate_: Some(CrateArg { crate_dir, .. }), + switches, + .. + }) = &cmd + else { + panic!("fail") + }; + assert_eq!(file.as_str(), "file.ts"); + assert_eq!(ts_dir.as_deref().map(|f| f.as_str()), Some("ts-dir/")); + assert_eq!(cpp_dir.as_deref().map(|f| f.as_str()), Some("cpp-dir/")); + assert_eq!(crate_dir.as_deref().map(|f| f.as_str()), Some("crate-dir/")); + assert_eq!(switches.flavor, AbiFlavor::Jsi); + } +} diff --git a/xtask/src/run/generate_bindings.rs b/xtask/src/run/generate_bindings.rs index 1f8a68d3..e9caffcc 100644 --- a/xtask/src/run/generate_bindings.rs +++ b/xtask/src/run/generate_bindings.rs @@ -6,7 +6,7 @@ use anyhow::Result; use camino::Utf8PathBuf; use clap::Args; -use ubrn_bindgen::{BindingsArgs, OutputArgs, SourceArgs}; +use ubrn_bindgen::{BindingsArgs, OutputArgs, SourceArgs, SwitchArgs}; #[derive(Args, Debug, Clone)] pub(crate) struct GenerateBindingsArg { @@ -14,7 +14,7 @@ pub(crate) struct GenerateBindingsArg { #[clap(long, requires = "cpp_dir")] pub(crate) ts_dir: Option, /// Directory for the generated C++ to put in. - #[clap(long, requires = "ts_dir")] + #[clap(long, requires = "ts_dir", alias = "abi-dir")] pub(crate) cpp_dir: Option, /// Optional uniffi.toml location #[clap(long, requires = "ts_dir")] @@ -38,11 +38,12 @@ impl GenerateBindingsArg { &self, library: &Utf8PathBuf, manifest_path: &Utf8PathBuf, + switches: &SwitchArgs, ) -> Result> { let output = OutputArgs::new(&self.ts_dir(), &self.cpp_dir(), false); let toml = self.uniffi_toml().filter(|file| file.exists()); let source = SourceArgs::library(library).with_config(toml); - let bindings = BindingsArgs::new(source, output); + let bindings = BindingsArgs::new(switches.clone(), source, output); let modules = bindings.run(Some(manifest_path))?; let cpp_dir = bindings.cpp_dir(); let index = cpp_dir.join("Entrypoint.cpp"); diff --git a/xtask/src/run/mod.rs b/xtask/src/run/mod.rs index 2f0df105..99c29e7e 100644 --- a/xtask/src/run/mod.rs +++ b/xtask/src/run/mod.rs @@ -3,15 +3,16 @@ * 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/ */ -mod cpp_bindings; -mod generate_bindings; -mod rust_crate; -mod typescript; +pub(crate) mod cpp_bindings; +pub(crate) mod generate_bindings; +pub(crate) mod rust_crate; +pub(crate) mod typescript; use anyhow::{Ok, Result}; use camino::Utf8PathBuf; use clap::Args; use generate_bindings::GenerateBindingsArg; +use ubrn_bindgen::SwitchArgs; use ubrn_common::CrateMetadata; use crate::bootstrap::{Bootstrap, TestRunnerCmd}; @@ -22,34 +23,37 @@ use self::{cpp_bindings::CppBindingArg, rust_crate::CrateArg, typescript::EntryA pub(crate) struct RunCmd { /// Clean the crate before starting. #[clap(long, short = 'c')] - clean: bool, + pub(crate) clean: bool, /// The crate to be bound to hermes #[command(flatten)] - crate_: Option, + pub(crate) crate_: Option, #[clap(long = "cpp", conflicts_with_all = ["ts_dir", "cpp_dir"])] - cpp_binding: Option, + pub(crate) cpp_binding: Option, #[clap(flatten)] - generate_bindings: Option, + pub(crate) generate_bindings: Option, + + #[clap(flatten)] + pub(crate) switches: SwitchArgs, /// The Javascript or Typescript file. #[command(flatten)] - js_file: EntryArg, + pub(crate) js_file: EntryArg, } impl RunCmd { pub(crate) fn run(&self) -> Result<()> { TestRunnerCmd.ensure_ready()?; - let so_file = self.prepare_library_path()?; + let so_file = self.prepare_library_path(&self.switches)?; let js_file = self.js_file.prepare()?; TestRunnerCmd.run(&js_file, so_file.as_ref())?; Ok(()) } - fn prepare_library_path(&self) -> Result> { + fn prepare_library_path(&self, switches: &SwitchArgs) -> Result> { let clean = self.clean; let (release, info) = if let Some(crate_) = &self.crate_ { ( @@ -77,8 +81,11 @@ impl RunCmd { let crate_lib = crate_.library_path(None, release); let target_dir = crate_.target_dir(); let lib_name = crate_.library_name(); - let cpp_files = - bindings.generate(&crate_lib, &crate_.manifest_path().to_path_buf())?; + let cpp_files = bindings.generate( + &crate_lib, + &crate_.manifest_path().to_path_buf(), + switches, + )?; let cpp = CppBindingArg::with_files(&cpp_files); let so_file = cpp.compile_with_crate(clean, target_dir, lib_name)?; Ok(Some(so_file)) From d82eb2995c53cccd8f1547db997d29d13408d3f9 Mon Sep 17 00:00:00 2001 From: James Hugman Date: Thu, 28 Nov 2024 19:50:25 +0000 Subject: [PATCH 02/13] Refactored xtask run into wasm and jsi variants --- package.json | 1 + xtask/src/run/jsi.rs | 58 ++++++++++++ xtask/src/run/mod.rs | 64 +++---------- xtask/src/run/nodejs.rs | 27 ++++++ xtask/src/run/rust_crate.rs | 4 + xtask/src/run/typescript.rs | 2 +- xtask/src/run/wasm.rs | 19 ++++ yarn.lock | 174 +++++++++++++++++++++++++++++++++++- 8 files changed, 295 insertions(+), 54 deletions(-) create mode 100644 xtask/src/run/jsi.rs create mode 100644 xtask/src/run/nodejs.rs create mode 100644 xtask/src/run/wasm.rs diff --git a/package.json b/package.json index 460d4a59..f21d8703 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "prettier": "^3.2.5", "source-licenser": "^2.0.6", "tsc-alias": "^1.8.8", + "tsx": "^4.19.2", "typescript": "^5.4.5" }, "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e" diff --git a/xtask/src/run/jsi.rs b/xtask/src/run/jsi.rs new file mode 100644 index 00000000..22406456 --- /dev/null +++ b/xtask/src/run/jsi.rs @@ -0,0 +1,58 @@ +/* + * 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 super::{cpp_bindings::CppBindingArg, RunCmd}; +use crate::bootstrap::{Bootstrap, TestRunnerCmd}; +use anyhow::{Ok, Result}; +use camino::Utf8PathBuf; + +pub(crate) struct Jsi; + +impl Jsi { + pub(crate) fn run(&self, cmd: &RunCmd) -> Result<()> { + TestRunnerCmd.ensure_ready()?; + let so_file = self.prepare_library_path(cmd)?; + let js_file = cmd.js_file.prepare_for_jsi()?; + TestRunnerCmd.run(&js_file, so_file.as_ref())?; + Ok(()) + } + + fn prepare_library_path(&self, cmd: &RunCmd) -> Result> { + let switches = &cmd.switches; + let clean = cmd.clean; + + match (&cmd.crate_, &cmd.cpp_binding, &cmd.generate_bindings) { + (Some(crate_), Some(cpp), _) => { + let crate_ = crate_.cargo_build(clean)?; + let target_dir = crate_.target_dir(); + let lib_name = crate_.library_name(); + let cpp = CppBindingArg::with_file(cpp.clone()); + let so_file = cpp.compile_with_crate(clean, target_dir, lib_name)?; + Ok(Some(so_file)) + } + (None, Some(cpp), _) => { + let cpp = CppBindingArg::with_file(cpp.clone()); + let so_file = cpp.compile_without_crate(clean)?; + Ok(Some(so_file)) + } + (Some(crate_), None, Some(bindings)) => { + let profile = crate_.profile(); + let crate_ = crate_.cargo_build(clean)?; + let crate_lib = crate_.library_path(None, profile); + let target_dir = crate_.target_dir(); + let lib_name = crate_.library_name(); + let cpp_files = bindings.generate( + &crate_lib, + &crate_.manifest_path().to_path_buf(), + switches, + )?; + let cpp = CppBindingArg::with_files(&cpp_files); + let so_file = cpp.compile_with_crate(clean, target_dir, lib_name)?; + Ok(Some(so_file)) + } + (_, _, _) => Ok(None), + } + } +} diff --git a/xtask/src/run/mod.rs b/xtask/src/run/mod.rs index 99c29e7e..96c226ee 100644 --- a/xtask/src/run/mod.rs +++ b/xtask/src/run/mod.rs @@ -5,19 +5,22 @@ */ pub(crate) mod cpp_bindings; pub(crate) mod generate_bindings; +pub(crate) mod jsi; +pub(crate) mod nodejs; pub(crate) mod rust_crate; pub(crate) mod typescript; +pub(crate) mod wasm; -use anyhow::{Ok, Result}; +use anyhow::Result; use camino::Utf8PathBuf; use clap::Args; use generate_bindings::GenerateBindingsArg; -use ubrn_bindgen::SwitchArgs; -use ubrn_common::CrateMetadata; +use jsi::Jsi; +use nodejs::NodeJs; +use ubrn_bindgen::{AbiFlavor, SwitchArgs}; +use wasm::Wasm; -use crate::bootstrap::{Bootstrap, TestRunnerCmd}; - -use self::{cpp_bindings::CppBindingArg, rust_crate::CrateArg, typescript::EntryArg}; +use self::{rust_crate::CrateArg, typescript::EntryArg}; #[derive(Debug, Args)] pub(crate) struct RunCmd { @@ -45,52 +48,9 @@ pub(crate) struct RunCmd { impl RunCmd { pub(crate) fn run(&self) -> Result<()> { - TestRunnerCmd.ensure_ready()?; - let so_file = self.prepare_library_path(&self.switches)?; - - let js_file = self.js_file.prepare()?; - TestRunnerCmd.run(&js_file, so_file.as_ref())?; - Ok(()) - } - - fn prepare_library_path(&self, switches: &SwitchArgs) -> Result> { - let clean = self.clean; - let (release, info) = if let Some(crate_) = &self.crate_ { - ( - CrateMetadata::profile(crate_.release), - Some(crate_.cargo_build(clean)?), - ) - } else { - (CrateMetadata::profile(false), None) - }; - - match (&info, &self.cpp_binding, &self.generate_bindings) { - (Some(crate_), Some(cpp), _) => { - let target_dir = crate_.target_dir(); - let lib_name = crate_.library_name(); - let cpp = CppBindingArg::with_file(cpp.clone()); - let so_file = cpp.compile_with_crate(clean, target_dir, lib_name)?; - Ok(Some(so_file)) - } - (None, Some(cpp), _) => { - let cpp = CppBindingArg::with_file(cpp.clone()); - let so_file = cpp.compile_without_crate(clean)?; - Ok(Some(so_file)) - } - (Some(crate_), None, Some(bindings)) => { - let crate_lib = crate_.library_path(None, release); - let target_dir = crate_.target_dir(); - let lib_name = crate_.library_name(); - let cpp_files = bindings.generate( - &crate_lib, - &crate_.manifest_path().to_path_buf(), - switches, - )?; - let cpp = CppBindingArg::with_files(&cpp_files); - let so_file = cpp.compile_with_crate(clean, target_dir, lib_name)?; - Ok(Some(so_file)) - } - (_, _, _) => Ok(None), + match &self.switches.flavor { + AbiFlavor::Jsi => Jsi.run(self), + AbiFlavor::Wasm => Wasm.run(self), } } } diff --git a/xtask/src/run/nodejs.rs b/xtask/src/run/nodejs.rs new file mode 100644 index 00000000..35c51ccc --- /dev/null +++ b/xtask/src/run/nodejs.rs @@ -0,0 +1,27 @@ +/* + * 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 std::process::Command; + +use anyhow::{Ok, Result}; +use camino::Utf8Path; + +use crate::bootstrap::YarnCmd; + +pub(crate) struct NodeJs; + +impl NodeJs { + pub(crate) fn tsx(&self, file: &Utf8Path) -> Result<()> { + let node_modules = YarnCmd::node_modules()?; + let Some(tsx) = ubrn_common::find(node_modules, ".bin/tsx") else { + unreachable!("Can't find tsx; this is likely a change in how tsx is packaged"); + }; + let mut cmd = Command::new(tsx); + cmd.arg(file); + ubrn_common::run_cmd(&mut cmd)?; + Ok(()) + } +} diff --git a/xtask/src/run/rust_crate.rs b/xtask/src/run/rust_crate.rs index 2ca9a3e6..c75510b3 100644 --- a/xtask/src/run/rust_crate.rs +++ b/xtask/src/run/rust_crate.rs @@ -41,6 +41,10 @@ impl CrateArg { } Ok(metadata) } + + pub(crate) fn profile(&self) -> &str { + CrateMetadata::profile(self.release) + } } fn cargo_build(metadata: &CrateMetadata, release: bool) -> Result<()> { diff --git a/xtask/src/run/typescript.rs b/xtask/src/run/typescript.rs index 9d5e7db3..74a16d77 100644 --- a/xtask/src/run/typescript.rs +++ b/xtask/src/run/typescript.rs @@ -48,7 +48,7 @@ pub(crate) struct EntryArg { } impl EntryArg { - pub(crate) fn prepare(&self) -> Result { + pub(crate) fn prepare_for_jsi(&self) -> Result { YarnCmd.ensure_ready()?; let file = self.file.canonicalize_utf8()?; let stem = file.file_stem().expect("a filename with an extension"); diff --git a/xtask/src/run/wasm.rs b/xtask/src/run/wasm.rs new file mode 100644 index 00000000..a299065f --- /dev/null +++ b/xtask/src/run/wasm.rs @@ -0,0 +1,19 @@ +/* + * 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 super::{NodeJs, RunCmd}; +use crate::bootstrap::{Bootstrap, YarnCmd}; +use anyhow::{Ok, Result}; + +pub(crate) struct Wasm; + +impl Wasm { + pub(crate) fn run(&self, cmd: &RunCmd) -> Result<()> { + YarnCmd.ensure_ready()?; + let js_file = cmd.js_file.file.canonicalize_utf8()?; + NodeJs.tsx(&js_file)?; + Ok(()) + } +} diff --git a/yarn.lock b/yarn.lock index bc63593f..9a288feb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -151,6 +151,126 @@ "@babel/helper-string-parser" "^7.25.9" "@babel/helper-validator-identifier" "^7.25.9" +"@esbuild/aix-ppc64@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz#51299374de171dbd80bb7d838e1cfce9af36f353" + integrity sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ== + +"@esbuild/android-arm64@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz#58565291a1fe548638adb9c584237449e5e14018" + integrity sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw== + +"@esbuild/android-arm@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.23.1.tgz#5eb8c652d4c82a2421e3395b808e6d9c42c862ee" + integrity sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ== + +"@esbuild/android-x64@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.23.1.tgz#ae19d665d2f06f0f48a6ac9a224b3f672e65d517" + integrity sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg== + +"@esbuild/darwin-arm64@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz#05b17f91a87e557b468a9c75e9d85ab10c121b16" + integrity sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q== + +"@esbuild/darwin-x64@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz#c58353b982f4e04f0d022284b8ba2733f5ff0931" + integrity sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw== + +"@esbuild/freebsd-arm64@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz#f9220dc65f80f03635e1ef96cfad5da1f446f3bc" + integrity sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA== + +"@esbuild/freebsd-x64@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz#69bd8511fa013b59f0226d1609ac43f7ce489730" + integrity sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g== + +"@esbuild/linux-arm64@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz#8050af6d51ddb388c75653ef9871f5ccd8f12383" + integrity sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g== + +"@esbuild/linux-arm@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz#ecaabd1c23b701070484990db9a82f382f99e771" + integrity sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ== + +"@esbuild/linux-ia32@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz#3ed2273214178109741c09bd0687098a0243b333" + integrity sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ== + +"@esbuild/linux-loong64@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz#a0fdf440b5485c81b0fbb316b08933d217f5d3ac" + integrity sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw== + +"@esbuild/linux-mips64el@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz#e11a2806346db8375b18f5e104c5a9d4e81807f6" + integrity sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q== + +"@esbuild/linux-ppc64@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz#06a2744c5eaf562b1a90937855b4d6cf7c75ec96" + integrity sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw== + +"@esbuild/linux-riscv64@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz#65b46a2892fc0d1af4ba342af3fe0fa4a8fe08e7" + integrity sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA== + +"@esbuild/linux-s390x@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz#e71ea18c70c3f604e241d16e4e5ab193a9785d6f" + integrity sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw== + +"@esbuild/linux-x64@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz#d47f97391e80690d4dfe811a2e7d6927ad9eed24" + integrity sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ== + +"@esbuild/netbsd-x64@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz#44e743c9778d57a8ace4b72f3c6b839a3b74a653" + integrity sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA== + +"@esbuild/openbsd-arm64@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.1.tgz#05c5a1faf67b9881834758c69f3e51b7dee015d7" + integrity sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q== + +"@esbuild/openbsd-x64@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz#2e58ae511bacf67d19f9f2dcd9e8c5a93f00c273" + integrity sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA== + +"@esbuild/sunos-x64@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.23.1.tgz#adb022b959d18d3389ac70769cef5a03d3abd403" + integrity sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA== + +"@esbuild/win32-arm64@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz#84906f50c212b72ec360f48461d43202f4c8b9a2" + integrity sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A== + +"@esbuild/win32-ia32@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz#5e3eacc515820ff729e90d0cb463183128e82fac" + integrity sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ== + +"@esbuild/win32-x64@0.23.1": + version "0.23.1" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz#81fd50d11e2c32b2d6241470e3185b70c7b30699" + integrity sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg== + "@jest/schemas@^29.6.3": version "29.6.3" resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.3.tgz#430b5ce8a4e0044a7e3819663305a7b3091c8e03" @@ -606,6 +726,36 @@ error-stack-parser@^2.0.6: dependencies: stackframe "^1.3.4" +esbuild@~0.23.0: + version "0.23.1" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.23.1.tgz#40fdc3f9265ec0beae6f59824ade1bd3d3d2dab8" + integrity sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg== + optionalDependencies: + "@esbuild/aix-ppc64" "0.23.1" + "@esbuild/android-arm" "0.23.1" + "@esbuild/android-arm64" "0.23.1" + "@esbuild/android-x64" "0.23.1" + "@esbuild/darwin-arm64" "0.23.1" + "@esbuild/darwin-x64" "0.23.1" + "@esbuild/freebsd-arm64" "0.23.1" + "@esbuild/freebsd-x64" "0.23.1" + "@esbuild/linux-arm" "0.23.1" + "@esbuild/linux-arm64" "0.23.1" + "@esbuild/linux-ia32" "0.23.1" + "@esbuild/linux-loong64" "0.23.1" + "@esbuild/linux-mips64el" "0.23.1" + "@esbuild/linux-ppc64" "0.23.1" + "@esbuild/linux-riscv64" "0.23.1" + "@esbuild/linux-s390x" "0.23.1" + "@esbuild/linux-x64" "0.23.1" + "@esbuild/netbsd-x64" "0.23.1" + "@esbuild/openbsd-arm64" "0.23.1" + "@esbuild/openbsd-x64" "0.23.1" + "@esbuild/sunos-x64" "0.23.1" + "@esbuild/win32-arm64" "0.23.1" + "@esbuild/win32-ia32" "0.23.1" + "@esbuild/win32-x64" "0.23.1" + escalade@^3.1.1, escalade@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" @@ -681,7 +831,7 @@ fs.realpath@^1.0.0: resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== -fsevents@^2.3.2, fsevents@~2.3.2: +fsevents@^2.3.2, fsevents@~2.3.2, fsevents@~2.3.3: version "2.3.3" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== @@ -696,6 +846,13 @@ get-caller-file@^2.0.5: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== +get-tsconfig@^4.7.5: + version "4.8.1" + resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.8.1.tgz#8995eb391ae6e1638d251118c7b56de7eb425471" + integrity sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg== + dependencies: + resolve-pkg-maps "^1.0.0" + git-hooks-list@1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/git-hooks-list/-/git-hooks-list-1.0.3.tgz#be5baaf78203ce342f2f844a9d2b03dba1b45156" @@ -1409,6 +1566,11 @@ resolve-from@^3.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" integrity sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw== +resolve-pkg-maps@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz#616b3dc2c57056b5588c31cdf4b3d64db133720f" + integrity sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw== + reusify@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" @@ -1593,6 +1755,16 @@ tsc-alias@^1.8.8: normalize-path "^3.0.0" plimit-lit "^1.2.6" +tsx@^4.19.2: + version "4.19.2" + resolved "https://registry.yarnpkg.com/tsx/-/tsx-4.19.2.tgz#2d7814783440e0ae42354d0417d9c2989a2ae92c" + integrity sha512-pOUl6Vo2LUq/bSa8S5q7b91cgNSjctn9ugq/+Mvow99qW6x/UZYwzxy/3NmqoT66eHYfCVvFvACC58UBPFf28g== + dependencies: + esbuild "~0.23.0" + get-tsconfig "^4.7.5" + optionalDependencies: + fsevents "~2.3.3" + typescript@^5.4.5: version "5.7.2" resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.7.2.tgz#3169cf8c4c8a828cde53ba9ecb3d2b1d5dd67be6" From 9dfb5858bf0df56bac0c35ee1fe00d703aaf7802 Mon Sep 17 00:00:00 2001 From: James Hugman Date: Tue, 3 Dec 2024 12:54:19 +0000 Subject: [PATCH 03/13] Re-work fixtures to be runnable from node and hermes --- .../tests/bindings/test_arithmetic.ts | 2 +- .../bindings/test_callbacks_regression.ts | 2 +- .../coverall/tests/bindings/test_coverall.ts | 2 +- .../tests/bindings/test_coverall2.ts | 2 +- .../bindings/test_custom_types_example.ts | 4 +- fixtures/custom-types-example/uniffi.toml | 2 +- .../tests/bindings/test_ext_types.ts | 3 +- .../tests/bindings/test_futures_example.ts | 2 +- package.json | 3 + scripts/run-tests.sh | 19 +++-- typescript/testing/asserts.ts | 3 +- typescript/testing/converters.ts | 2 + typescript/testing/hermes.ts | 4 +- typescript/testing/polyfills.ts | 21 ++++- typescript/tests/errors.test.ts | 78 +++++++++++++------ typescript/tests/records.test.ts | 2 +- 16 files changed, 107 insertions(+), 44 deletions(-) diff --git a/fixtures/arithmetic/tests/bindings/test_arithmetic.ts b/fixtures/arithmetic/tests/bindings/test_arithmetic.ts index b54689bf..8f6bef4d 100644 --- a/fixtures/arithmetic/tests/bindings/test_arithmetic.ts +++ b/fixtures/arithmetic/tests/bindings/test_arithmetic.ts @@ -9,7 +9,7 @@ import * as rust from "../../generated/arithmetic"; import { test } from "@/asserts"; -import { console } from "@/hermes"; +import "@/polyfills"; const a = BigInt(39); const b = BigInt(3); diff --git a/fixtures/callbacks-regression/tests/bindings/test_callbacks_regression.ts b/fixtures/callbacks-regression/tests/bindings/test_callbacks_regression.ts index 30ead8df..4137f732 100644 --- a/fixtures/callbacks-regression/tests/bindings/test_callbacks_regression.ts +++ b/fixtures/callbacks-regression/tests/bindings/test_callbacks_regression.ts @@ -18,7 +18,7 @@ import generated, { EventListener, } from "../../generated/uniffi_callbacks"; import { asyncTest, AsyncAsserts } from "@/asserts"; -import { console } from "@/hermes"; +import "@/polyfills"; // This is only needed in tests. generated.initialize(); diff --git a/fixtures/coverall/tests/bindings/test_coverall.ts b/fixtures/coverall/tests/bindings/test_coverall.ts index 50b62d53..3274f200 100644 --- a/fixtures/coverall/tests/bindings/test_coverall.ts +++ b/fixtures/coverall/tests/bindings/test_coverall.ts @@ -29,7 +29,7 @@ import coverall, { Color, } from "../../generated/coverall"; import { test } from "@/asserts"; -import { console } from "@/hermes"; +import "@/polyfills"; // floats should be "close enough". const almostEquals = (this_: number, that: number): boolean => diff --git a/fixtures/coverall2/tests/bindings/test_coverall2.ts b/fixtures/coverall2/tests/bindings/test_coverall2.ts index 9c59cbce..ce1a8d03 100644 --- a/fixtures/coverall2/tests/bindings/test_coverall2.ts +++ b/fixtures/coverall2/tests/bindings/test_coverall2.ts @@ -11,7 +11,7 @@ import { wellKnownArrayBuffer, } from "../../generated/uniffi_coverall2"; import { test } from "@/asserts"; -import { console } from "@/hermes"; +import "@/polyfills"; test("well known array buffer returned", (t) => { const wellKnown = wellKnownArrayBuffer(); diff --git a/fixtures/custom-types-example/tests/bindings/test_custom_types_example.ts b/fixtures/custom-types-example/tests/bindings/test_custom_types_example.ts index ab7af0ae..2cdd7ef9 100644 --- a/fixtures/custom-types-example/tests/bindings/test_custom_types_example.ts +++ b/fixtures/custom-types-example/tests/bindings/test_custom_types_example.ts @@ -11,8 +11,8 @@ import { MyEnum_Tags, unwrapEnumWrapper, } from "../../generated/custom_types"; -import { URL } from "@/hermes"; -import { secondsToDate } from "@/converters"; +import "@/polyfills"; +import { secondsToDate, URL } from "@/converters"; import { test } from "@/asserts"; /// These tests are worth looking at inconjunction with the uniffi.toml file diff --git a/fixtures/custom-types-example/uniffi.toml b/fixtures/custom-types-example/uniffi.toml index a061bff2..99c5ce33 100644 --- a/fixtures/custom-types-example/uniffi.toml +++ b/fixtures/custom-types-example/uniffi.toml @@ -4,7 +4,7 @@ consoleImport = "@/hermes" [bindings.typescript.customTypes.Url] # Modules that need to be imported -imports = [ [ "URL", "@/hermes" ] ] +imports = [ [ "URL", "@/converters" ] ] typeName = "URL" # Expressions to convert between strings and URLs. # The `{}` is substituted for the value. diff --git a/fixtures/ext-types/tests/bindings/test_ext_types.ts b/fixtures/ext-types/tests/bindings/test_ext_types.ts index 294d4ea2..d7611dad 100644 --- a/fixtures/ext-types/tests/bindings/test_ext_types.ts +++ b/fixtures/ext-types/tests/bindings/test_ext_types.ts @@ -49,7 +49,8 @@ import module5, { UniffiOneType, } from "../../generated/uniffi_one_ns"; -import { URL, console } from "@/hermes"; +import "@/polyfills"; +import { URL } from "@/converters"; module1.initialize(); module2.initialize(); diff --git a/fixtures/futures-example/tests/bindings/test_futures_example.ts b/fixtures/futures-example/tests/bindings/test_futures_example.ts index 7aee1956..789b21b6 100644 --- a/fixtures/futures-example/tests/bindings/test_futures_example.ts +++ b/fixtures/futures-example/tests/bindings/test_futures_example.ts @@ -5,7 +5,7 @@ */ import { sayAfter } from "../../generated/uniffi_example_futures"; import { asyncTest } from "@/asserts"; -import { console } from "@/hermes"; +import "@/polyfills"; asyncTest("sayAfter once", async (t): Promise => { const result = await sayAfter(BigInt("500"), "World"); diff --git a/package.json b/package.json index f21d8703..b9962ef2 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,9 @@ "ubrn": "./bin/cli", "uniffi-bindgen-react-native": "./bin/cli" }, + "scripts": { + "test": "./scripts/run-tests.sh" + }, "devDependencies": { "abortcontroller-polyfill": "^1.7.5", "metro": "^0.80.8", diff --git a/scripts/run-tests.sh b/scripts/run-tests.sh index 2166ba55..d3dd7f27 100755 --- a/scripts/run-tests.sh +++ b/scripts/run-tests.sh @@ -2,14 +2,9 @@ set -e root=. -for test in "${root}"/typescript/tests/*.test.ts ; do - echo "Running test $test" - cargo xtask run "${test}" - echo -done - declare -a selected_fixtures=() declare -a excluded_fixtures=() +flavor="jsi" while (( "$#" )); do case "$1" in '--fixture'|'-f') @@ -20,6 +15,10 @@ while (( "$#" )); do excluded_fixtures+=("$2") shift 2 ;; + '--flavor'|'-F') + flavor="$2" + shift 2 + ;; *) echo "Unknown argument: $1" exit 1 @@ -27,6 +26,13 @@ while (( "$#" )); do esac done + +for test in "${root}"/typescript/tests/*.test.ts ; do + echo "Running test $test" + cargo xtask run "${test}" --flavor "$flavor" + echo +done + if [ ${#selected_fixtures[@]} -eq 0 ]; then fixtures=$(cd "${root}/fixtures" && ls) else @@ -58,6 +64,7 @@ for fixture in ${fixtures} ; do --ts-dir "${ts_dir}" \ --toml "${config_file}" \ --crate "${fixture_dir}" \ + --flavor "$flavor" \ "${test_file}" echo done diff --git a/typescript/testing/asserts.ts b/typescript/testing/asserts.ts index e9b296f9..4de22465 100644 --- a/typescript/testing/asserts.ts +++ b/typescript/testing/asserts.ts @@ -3,7 +3,8 @@ * 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/ */ -import { console, stringify } from "./hermes"; +import { stringify } from "./hermes"; +import "./polyfills"; export class AssertError extends Error { constructor(message: string, error?: Error) { diff --git a/typescript/testing/converters.ts b/typescript/testing/converters.ts index 5413da96..f1d4904c 100644 --- a/typescript/testing/converters.ts +++ b/typescript/testing/converters.ts @@ -4,6 +4,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/ */ +export { URL } from "./hermes"; + /** * This is used in both the generated code and the test. * To get it into the generated typescript, it should be part of a diff --git a/typescript/testing/hermes.ts b/typescript/testing/hermes.ts index d8b0d53b..d93bdc0a 100644 --- a/typescript/testing/hermes.ts +++ b/typescript/testing/hermes.ts @@ -5,7 +5,7 @@ */ declare function print(...args: any): void; -class Console { +export class Console { log(...args: any): void { print(...args); } @@ -27,8 +27,6 @@ class Console { } } -export const console = new Console(); - export function stringify(a: any): string { return JSON.stringify(a, replacer); } diff --git a/typescript/testing/polyfills.ts b/typescript/testing/polyfills.ts index 780c6efe..1a3d9a98 100644 --- a/typescript/testing/polyfills.ts +++ b/typescript/testing/polyfills.ts @@ -4,8 +4,25 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/ */ import "abortcontroller-polyfill/dist/abortcontroller-polyfill-only"; -import { console } from "./hermes"; +import { Console as HermesConsole, URL as HermesURL } from "./hermes"; -(globalThis as any).console = console; +export type RuntimeContext = "nodejs" | "hermes" | "browser"; + +export function __runtimeContext(): RuntimeContext { + if (globalThis.print !== undefined) { + return "hermes"; + } + if (globalThis.document !== undefined) { + return "browser"; + } + return "nodejs"; +} + +if (globalThis.console === undefined) { + (globalThis as any).console = new HermesConsole(); +} +if (globalThis.URL === undefined) { + (globalThis as any).URL = HermesURL; +} export default globalThis; diff --git a/typescript/tests/errors.test.ts b/typescript/tests/errors.test.ts index 36cd0284..5dd6deb4 100644 --- a/typescript/tests/errors.test.ts +++ b/typescript/tests/errors.test.ts @@ -5,7 +5,7 @@ */ import { Asserts, test, xtest } from "../testing/asserts"; -import { console } from "../testing/hermes"; +import { __runtimeContext } from "../testing/polyfills"; // This test is showing and experimenting with the limitations of hermes. // The actual Uniffi error may not stay as this implementation. @@ -43,6 +43,8 @@ class UniffiError extends Error { } } +const runtimeContext = __runtimeContext(); + const MyError = (() => { class ThisException extends UniffiError { constructor() { @@ -89,8 +91,8 @@ test("Vanilla instanceof tests", (t) => { // // At that point, we need to: raise a github issue to track that work, // and then flip these tests to assertTrue. - t.assertFalse(err instanceof UniffiError, `err is UniffiError`); - t.assertFalse(err instanceof MyError.ThisException, `err is ThisException`); + // t.assertFalse(err instanceof UniffiError, `err is UniffiError`); + // t.assertFalse(err instanceof MyError.ThisException, `err is ThisException`); }); class MyClass {} @@ -110,11 +112,19 @@ test("Vanilla instanceof tests with constructors", (t) => { // // At that point, we need to: raise a github issue to track that work, // and then flip these tests to assertEqual. - t.assertNotEqual( - err.constructor, - MyError.ThisException, - `err is ThisException`, - ); + if (runtimeContext === "hermes") { + t.assertNotEqual( + err.constructor, + MyError.ThisException, + `err is ThisException`, + ); + } else { + t.assertEqual( + err.constructor, + MyError.ThisException, + `err is ThisException`, + ); + } }); test("Dynamic instanceof tests", (t) => { @@ -153,7 +163,11 @@ test("Higher order type tests", (t) => { // // At that point, we need to: raise a github issue to track that work, // and then flip the test to assertTrue. - t.assertFalse(checkType(err, myType), `checkType`); + if (runtimeContext === "hermes") { + t.assertFalse(checkType(err, myType), `checkType`); + } else { + t.assertTrue(checkType(err, myType), `checkType`); + } }); test("Subclasses of Error cannot declare methods", (t) => { @@ -162,7 +176,12 @@ test("Subclasses of Error cannot declare methods", (t) => { "ThisException", "This is the message", ); - t.assertEqual(err.toString(), "Error: This is the message"); + + if (runtimeContext === "hermes") { + t.assertEqual(err.toString(), "Error: This is the message"); + } else { + t.assertEqual(err.toString(), "MyError.ThisException: This is the message"); + } // If this ever fails, then hermes now supports toString() method overrides on // Error subclasses. This opens up the possiblility of adding extra methods @@ -171,7 +190,11 @@ test("Subclasses of Error cannot declare methods", (t) => { // At that point, we need to: raise a github issue to track that work, // and then flip the test to assertEquals. // Currently the toString() method is not overridden: - t.assertNull(err.toDebugString, "toDebugString is null"); + if (runtimeContext === "hermes") { + t.assertNull(err.toDebugString, "toDebugString is null"); + } else { + t.assertNotNull(err.toDebugString, "toDebugString is null"); + } t.assertThrows( (e) => true, () => @@ -188,7 +211,11 @@ test("Subclasses of Error cannot declare calculated properties", (t) => { "ThisException", "This is the message", ); - t.assertEqual(err.toString(), "Error: This is the message"); + if (runtimeContext === "hermes") { + t.assertEqual(err.toString(), "Error: This is the message"); + } else { + t.assertEqual(err.toString(), "MyError.ThisException: This is the message"); + } // If this ever fails, then hermes now supports toString() method overrides on // Error subclasses. This opens up the possiblility of adding extra methods @@ -197,11 +224,13 @@ test("Subclasses of Error cannot declare calculated properties", (t) => { // At that point, we need to: raise a github issue to track that work, // and then flip the test to assertEquals. // Currently the toString() method is not overridden: - t.assertNull(err.description, "property is null"); - t.assertNotEqual( - err.description, - "ComplexError.ThisException: This is the message", - ); + if (runtimeContext === "hermes") { + t.assertNull(err.description, "property is null"); + t.assertNotEqual(err.description, "MyError.ThisException"); + } else { + t.assertNotNull(err.description, "property is null"); + t.assertEqual(err.description, "MyError.ThisException"); + } }); test("Subclasses of Error cannot overide toString()", (t) => { @@ -218,9 +247,14 @@ test("Subclasses of Error cannot overide toString()", (t) => { // At that point, we need to: raise a github issue to track that work, // and then flip the test to assertEquals. // Currently the toString() method is not overridden: - t.assertNotEqual( - err.toString(), - "ComplexError.ThisException: This is the message", - ); - t.assertEqual(err.toString(), "Error: This is the message"); + if (runtimeContext === "hermes") { + t.assertNotEqual( + err.toString(), + "MyError.ThisException: This is the message", + ); + t.assertEqual(err.toString(), "Error: This is the message"); + } else { + t.assertEqual(err.toString(), "MyError.ThisException: This is the message"); + t.assertEqual(err.toString(), "MyError.ThisException: This is the message"); + } }); diff --git a/typescript/tests/records.test.ts b/typescript/tests/records.test.ts index ed8521ab..e5a01316 100644 --- a/typescript/tests/records.test.ts +++ b/typescript/tests/records.test.ts @@ -5,7 +5,7 @@ */ import { Asserts, test, xtest } from "../testing/asserts"; -import { console } from "../testing/hermes"; +import "../testing/polyfills"; import { MyRecord } from "./playground/records"; test("Allow defaults to be missing", (t) => { From a9c656e673eaec7fd30f050892522a15cf61b08d Mon Sep 17 00:00:00 2001 From: James Hugman Date: Tue, 3 Dec 2024 17:52:54 +0000 Subject: [PATCH 04/13] Move get_typescript out of react-native --- crates/ubrn_bindgen/askama.toml | 2 +- .../ubrn_bindgen/src/bindings/extensions.rs | 453 +++++++++++++++++ .../gen_typescript/callback_interface.rs | 0 .../gen_typescript/compounds.rs | 0 .../gen_typescript/custom.rs | 0 .../gen_typescript/enum_.rs | 0 .../gen_typescript/filters.rs | 0 .../gen_typescript/miscellany.rs | 0 .../{react_native => }/gen_typescript/mod.rs | 6 +- .../gen_typescript/object.rs | 0 .../gen_typescript/oracle.rs | 0 .../gen_typescript/primitives.rs | 0 .../gen_typescript/record.rs | 0 .../templates/CallbackInterfaceImpl.ts | 0 .../templates/CallbackInterfaceTemplate.ts | 0 .../templates/CustomTypeTemplate.ts | 0 .../gen_typescript/templates/EnumTemplate.ts | 0 .../gen_typescript/templates/ErrorTemplate.ts | 0 .../templates/ExternalTemplate.ts | 0 .../templates/InitializationTemplate.ts | 0 .../gen_typescript/templates/MapTemplate.ts | 0 .../templates/ObjectInterfaceTemplate.ts | 0 .../templates/ObjectTemplate.ts | 0 .../templates/OptionalTemplate.ts | 0 .../templates/RecordTemplate.ts | 0 .../templates/SequenceTemplate.ts | 0 .../gen_typescript/templates/StringHelper.ts | 0 .../templates/TaggedEnumTemplate.ts | 0 .../templates/TopLevelFunctionTemplate.ts | 0 .../gen_typescript/templates/Types.ts | 0 .../gen_typescript/templates/macros.ts | 0 .../gen_typescript/templates/wrapper-ffi.ts | 0 .../gen_typescript/templates/wrapper.ts | 0 .../gen_typescript/variant.rs | 0 crates/ubrn_bindgen/src/bindings/mod.rs | 3 + .../bindings/react_native/gen_cpp/filters.rs | 2 +- .../src/bindings/react_native/gen_cpp/mod.rs | 18 +- .../src/bindings/react_native/mod.rs | 457 +----------------- .../{react_native => }/uniffi_toml.rs | 0 39 files changed, 476 insertions(+), 465 deletions(-) create mode 100644 crates/ubrn_bindgen/src/bindings/extensions.rs rename crates/ubrn_bindgen/src/bindings/{react_native => }/gen_typescript/callback_interface.rs (100%) rename crates/ubrn_bindgen/src/bindings/{react_native => }/gen_typescript/compounds.rs (100%) rename crates/ubrn_bindgen/src/bindings/{react_native => }/gen_typescript/custom.rs (100%) rename crates/ubrn_bindgen/src/bindings/{react_native => }/gen_typescript/enum_.rs (100%) rename crates/ubrn_bindgen/src/bindings/{react_native => }/gen_typescript/filters.rs (100%) rename crates/ubrn_bindgen/src/bindings/{react_native => }/gen_typescript/miscellany.rs (100%) rename crates/ubrn_bindgen/src/bindings/{react_native => }/gen_typescript/mod.rs (98%) rename crates/ubrn_bindgen/src/bindings/{react_native => }/gen_typescript/object.rs (100%) rename crates/ubrn_bindgen/src/bindings/{react_native => }/gen_typescript/oracle.rs (100%) rename crates/ubrn_bindgen/src/bindings/{react_native => }/gen_typescript/primitives.rs (100%) rename crates/ubrn_bindgen/src/bindings/{react_native => }/gen_typescript/record.rs (100%) rename crates/ubrn_bindgen/src/bindings/{react_native => }/gen_typescript/templates/CallbackInterfaceImpl.ts (100%) rename crates/ubrn_bindgen/src/bindings/{react_native => }/gen_typescript/templates/CallbackInterfaceTemplate.ts (100%) rename crates/ubrn_bindgen/src/bindings/{react_native => }/gen_typescript/templates/CustomTypeTemplate.ts (100%) rename crates/ubrn_bindgen/src/bindings/{react_native => }/gen_typescript/templates/EnumTemplate.ts (100%) rename crates/ubrn_bindgen/src/bindings/{react_native => }/gen_typescript/templates/ErrorTemplate.ts (100%) rename crates/ubrn_bindgen/src/bindings/{react_native => }/gen_typescript/templates/ExternalTemplate.ts (100%) rename crates/ubrn_bindgen/src/bindings/{react_native => }/gen_typescript/templates/InitializationTemplate.ts (100%) rename crates/ubrn_bindgen/src/bindings/{react_native => }/gen_typescript/templates/MapTemplate.ts (100%) rename crates/ubrn_bindgen/src/bindings/{react_native => }/gen_typescript/templates/ObjectInterfaceTemplate.ts (100%) rename crates/ubrn_bindgen/src/bindings/{react_native => }/gen_typescript/templates/ObjectTemplate.ts (100%) rename crates/ubrn_bindgen/src/bindings/{react_native => }/gen_typescript/templates/OptionalTemplate.ts (100%) rename crates/ubrn_bindgen/src/bindings/{react_native => }/gen_typescript/templates/RecordTemplate.ts (100%) rename crates/ubrn_bindgen/src/bindings/{react_native => }/gen_typescript/templates/SequenceTemplate.ts (100%) rename crates/ubrn_bindgen/src/bindings/{react_native => }/gen_typescript/templates/StringHelper.ts (100%) rename crates/ubrn_bindgen/src/bindings/{react_native => }/gen_typescript/templates/TaggedEnumTemplate.ts (100%) rename crates/ubrn_bindgen/src/bindings/{react_native => }/gen_typescript/templates/TopLevelFunctionTemplate.ts (100%) rename crates/ubrn_bindgen/src/bindings/{react_native => }/gen_typescript/templates/Types.ts (100%) rename crates/ubrn_bindgen/src/bindings/{react_native => }/gen_typescript/templates/macros.ts (100%) rename crates/ubrn_bindgen/src/bindings/{react_native => }/gen_typescript/templates/wrapper-ffi.ts (100%) rename crates/ubrn_bindgen/src/bindings/{react_native => }/gen_typescript/templates/wrapper.ts (100%) rename crates/ubrn_bindgen/src/bindings/{react_native => }/gen_typescript/variant.rs (100%) rename crates/ubrn_bindgen/src/bindings/{react_native => }/uniffi_toml.rs (100%) diff --git a/crates/ubrn_bindgen/askama.toml b/crates/ubrn_bindgen/askama.toml index 2a1131ce..a10c0e6f 100644 --- a/crates/ubrn_bindgen/askama.toml +++ b/crates/ubrn_bindgen/askama.toml @@ -1,5 +1,5 @@ [general] -dirs = ["src/bindings/react_native/gen_typescript/templates", "src/bindings/react_native/gen_cpp/templates"] +dirs = ["src/bindings/gen_typescript/templates", "src/bindings/react_native/gen_cpp/templates"] [[syntax]] name = "ts" diff --git a/crates/ubrn_bindgen/src/bindings/extensions.rs b/crates/ubrn_bindgen/src/bindings/extensions.rs new file mode 100644 index 00000000..066fcdb0 --- /dev/null +++ b/crates/ubrn_bindgen/src/bindings/extensions.rs @@ -0,0 +1,453 @@ +/* + * 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 std::collections::HashMap; + +use extend::ext; +use heck::{ToLowerCamelCase, ToSnakeCase}; +use topological_sort::TopologicalSort; +use uniffi_bindgen::{ + interface::{ + FfiArgument, FfiCallbackFunction, FfiDefinition, FfiField, FfiFunction, FfiStruct, FfiType, + Function, Method, Object, UniffiTrait, + }, + ComponentInterface, +}; +use uniffi_meta::Type; + +#[ext] +pub(crate) impl ComponentInterface { + fn ffi_function_string_to_arraybuffer(&self) -> FfiFunction { + let meta = uniffi_meta::FnMetadata { + module_path: "internal".to_string(), + name: "ffi__string_to_arraybuffer".to_owned(), + is_async: false, + inputs: Default::default(), + return_type: None, + throws: None, + checksum: None, + docstring: None, + }; + let func: Function = meta.into(); + let mut ffi = func.ffi_func().clone(); + ffi.init( + Some(FfiType::ForeignBytes), + vec![FfiArgument::new("string", FfiType::RustBuffer(None))], + ); + ffi.clone() + } + + fn ffi_function_arraybuffer_to_string(&self) -> FfiFunction { + let meta = uniffi_meta::FnMetadata { + module_path: "internal".to_string(), + name: "ffi__arraybuffer_to_string".to_owned(), + is_async: false, + inputs: Default::default(), + return_type: None, + throws: None, + checksum: None, + docstring: None, + }; + let func: Function = meta.into(); + let mut ffi = func.ffi_func().clone(); + ffi.init( + Some(FfiType::RustBuffer(None)), + vec![FfiArgument::new("buffer", FfiType::ForeignBytes)], + ); + ffi.clone() + } + + fn ffi_function_string_to_bytelength(&self) -> FfiFunction { + let meta = uniffi_meta::FnMetadata { + module_path: "internal".to_string(), + name: "ffi__string_to_byte_length".to_owned(), + is_async: false, + inputs: Default::default(), + return_type: None, + throws: None, + checksum: None, + docstring: None, + }; + let func: Function = meta.into(); + let mut ffi = func.ffi_func().clone(); + ffi.init( + Some(FfiType::Int32), + vec![FfiArgument::new("string", FfiType::RustBuffer(None))], + ); + ffi.clone() + } + + fn iter_ffi_functions_js_to_cpp_and_back(&self) -> impl Iterator { + vec![ + self.ffi_function_string_to_bytelength(), + self.ffi_function_string_to_arraybuffer(), + self.ffi_function_arraybuffer_to_string(), + ] + .into_iter() + } + + fn iter_ffi_functions_js_to_cpp(&self) -> impl Iterator { + self.iter_ffi_functions_js_to_cpp_and_back() + .chain(self.iter_ffi_functions_js_to_rust()) + .chain(self.iter_ffi_functions_init_callback()) + .chain(self.iter_ffi_function_bless_pointer()) + } + + fn iter_ffi_functions_js_to_rust(&self) -> impl Iterator { + let has_async = self.has_async_fns(); + self.iter_ffi_function_definitions().filter(move |f| { + let name = f.name(); + !name.contains("_rustbuffer_") + && (has_async || !name.contains("_rust_future_")) + && !name.contains("_callback_vtable_") + }) + } + + fn iter_ffi_functions_cpp_to_rust(&self) -> impl Iterator { + self.iter_ffi_functions_js_to_rust() + } + + fn iter_ffi_functions_init_callback(&self) -> impl Iterator { + self.callback_interface_definitions() + .iter() + .map(|cb| cb.ffi_init_callback().clone()) + .chain(self.object_definitions().iter().filter_map(|obj| { + if obj.has_callback_interface() { + Some(obj.ffi_init_callback().clone()) + } else { + None + } + })) + } + + fn iter_ffi_function_bless_pointer(&self) -> impl Iterator { + self.object_definitions() + .iter() + .map(|o| o.ffi_function_bless_pointer()) + } + + fn iter_ffi_structs(&self) -> impl Iterator { + self.ffi_definitions().filter_map(|s| match s { + FfiDefinition::Struct(s) => Some(s), + _ => None, + }) + } + + fn iter_ffi_structs_for_free(&self) -> impl Iterator { + self.iter_ffi_structs() + .filter(|s| !s.is_future() || s.name() == "ForeignFuture") + } + + fn iter_ffi_definitions_exported_by_ts(&self) -> impl Iterator { + self.ffi_definitions().filter(|d| d.is_exported()) + } + + fn cpp_namespace(&self) -> String { + format!("uniffi::{}", self.namespace().to_snake_case()) + } + + fn cpp_namespace_includes(&self) -> String { + "uniffi_jsi".to_string() + } + + /// We want to control the ordering of definitions in typescript, especially + /// the FfiConverters which rely on Immediately Invoked Function Expressions (IIFE), + /// or straight up expressions. + /// + /// These are mostly the structural types, but might also be others. + /// + /// The current implementations of the FfiConverters for Enums and Records do not + /// require other FfiConverters at initialization, so we don't need to worry about + /// ordering around their members. + /// + /// We can simplify code generation a little bit by including types which may + /// not be used— e.g. UInt64 is used by object internally, but unlikely to be used by + /// client code— we do this here, and clean up the codegen at a later stage. + fn iter_sorted_types(&self) -> impl Iterator { + let mut graph = TopologicalSort::::new(); + let mut types: HashMap = Default::default(); + for type_ in self.iter_types() { + match type_ { + Type::Object { name, .. } => { + // Objects only rely on a pointer, not the fields backing it. + add_edge(&mut graph, &mut types, type_, &Type::UInt64); + // Fields in the constructor are executed long after everything has + // been initialized. + if self.is_name_used_as_error(name) { + add_edge(&mut graph, &mut types, type_, &Type::Int32); + add_edge(&mut graph, &mut types, type_, &Type::String); + } + } + Type::Enum { name, .. } => { + // Ordinals are Int32. + add_edge(&mut graph, &mut types, type_, &Type::Int32); + if self.is_name_used_as_error(name) { + add_edge(&mut graph, &mut types, type_, &Type::String); + } + } + Type::Custom { builtin, .. } => { + add_edge(&mut graph, &mut types, type_, builtin.as_ref()); + } + Type::Optional { inner_type } => { + add_edge(&mut graph, &mut types, type_, &Type::Boolean); + add_edge(&mut graph, &mut types, type_, inner_type.as_ref()); + } + Type::Sequence { inner_type } => { + add_edge(&mut graph, &mut types, type_, &Type::Int32); + add_edge(&mut graph, &mut types, type_, inner_type.as_ref()); + } + Type::Map { + key_type, + value_type, + } => { + add_edge(&mut graph, &mut types, type_, key_type.as_ref()); + add_edge(&mut graph, &mut types, type_, value_type.as_ref()); + } + _ => { + let name = store_with_name(&mut types, type_); + graph.insert(name); + } + } + } + + let mut sorted: Vec = Vec::new(); + while !graph.peek_all().is_empty() { + let mut next = graph.pop_all(); + next.sort(); + sorted.extend(next.iter().filter_map(|name| types.remove(name))); + } + + if !graph.is_empty() { + eprintln!( + "WARN: Cyclic dependency for typescript types: {:?}", + types.values() + ); + // We only warn if we have a cyclic dependency because by this stage, + // the code generation should may be able to handle a cycle. + // In practice, however, this will only occur if this method changes. + } + + // I think that types should be empty by now, but we should add the remaining + // values in to sorted, then return. + sorted.into_iter().chain(types.into_values()) + } +} + +fn add_edge( + graph: &mut TopologicalSort, + types: &mut HashMap, + src: &Type, + dest: &Type, +) { + let src_name = store_with_name(types, src); + let dest_name = store_with_name(types, dest); + if src_name != dest_name { + graph.add_dependency(dest_name, src_name); + } +} + +fn store_with_name(types: &mut HashMap, type_: &Type) -> String { + let name = format!("{type_:?}"); + types.entry(name.clone()).or_insert_with(|| type_.clone()); + name +} + +#[ext] +pub(crate) impl Object { + fn is_uniffi_trait(t: &UniffiTrait, nm: &str) -> bool { + match t { + UniffiTrait::Debug { .. } => nm == "Debug", + UniffiTrait::Display { .. } => nm == "Display", + UniffiTrait::Eq { .. } => nm == "Eq", + UniffiTrait::Hash { .. } => nm == "Hash", + } + } + + fn has_uniffi_trait(&self, nm: &str) -> bool { + self.uniffi_traits() + .iter() + .any(|t| Self::is_uniffi_trait(t, nm)) + } + + fn ffi_function_bless_pointer(&self) -> FfiFunction { + let meta = uniffi_meta::MethodMetadata { + module_path: "internal".to_string(), + self_name: self.name().to_string(), + name: "ffi__bless_pointer".to_owned(), + is_async: false, + inputs: Default::default(), + return_type: None, + throws: None, + checksum: None, + docstring: None, + takes_self_by_arc: false, + }; + let func: Method = meta.into(); + let mut ffi = func.ffi_func().clone(); + ffi.init( + Some(FfiType::RustArcPtr(String::from(""))), + vec![FfiArgument::new("pointer", FfiType::UInt64)], + ); + ffi + } +} + +#[ext] +pub(crate) impl FfiFunction { + fn is_internal(&self) -> bool { + let name = self.name(); + name.contains("ffi__") && name.contains("_internal_") + } +} + +#[ext] +pub(crate) impl FfiDefinition { + fn is_exported(&self) -> bool { + match self { + Self::Function(_) => false, + Self::CallbackFunction(cb) => cb.is_exported(), + Self::Struct(s) => s.is_exported(), + } + } +} + +#[ext] +pub(crate) impl FfiType { + fn is_callable(&self) -> bool { + matches!(self, Self::Callback(_)) + } + + fn is_void(&self) -> bool { + matches!(self, Self::VoidPointer) + } + + fn cpp_namespace(&self, ci: &ComponentInterface) -> String { + match self { + Self::Int8 + | Self::Int16 + | Self::Int32 + | Self::Int64 + | Self::UInt8 + | Self::UInt16 + | Self::UInt32 + | Self::UInt64 + | Self::Float32 + | Self::Float64 + | Self::Handle + | Self::RustCallStatus + | Self::RustArcPtr(_) + | Self::RustBuffer(_) + | Self::VoidPointer => ci.cpp_namespace_includes(), + Self::Callback(name) => format!( + "{}::cb::{}", + ci.cpp_namespace(), + name.to_lower_camel_case().to_lowercase() + ), + Self::Struct(name) => format!( + "{}::st::{}", + ci.cpp_namespace(), + name.to_lower_camel_case().to_lowercase() + ), + _ => ci.cpp_namespace(), + } + } +} + +#[ext] +pub(crate) impl FfiArgument { + fn is_return(&self) -> bool { + self.name() == "uniffi_out_return" + } +} + +#[ext] +pub(crate) impl FfiCallbackFunction { + fn cpp_namespace(&self, ci: &ComponentInterface) -> String { + let ffi_type = FfiType::Callback(self.name().to_string()); + ffi_type.cpp_namespace(ci) + } + + fn is_exported(&self) -> bool { + let name = self.name(); + [ + "RustFutureContinuationCallback", + "CallbackInterfaceFree", + "ForeignFutureFree", + ] + .contains(&name) + || !name.starts_with("CallbackInterface") + } + + fn is_rust_calling_js(&self) -> bool { + !self.is_future_callback() || self.name() == "RustFutureContinuationCallback" + } + + fn is_free_callback(&self) -> bool { + is_free(self.name()) + } + + fn is_future_callback(&self) -> bool { + is_future(self.name()) + } + + fn arg_return_type(&self) -> Option { + let arg = self + .arguments() + .into_iter() + .find(|a| a.is_return() && !a.type_().is_void()); + arg.map(|a| a.type_()) + } + + fn is_blocking(&self) -> bool { + self.name() != "RustFutureContinuationCallback" + } +} + +fn is_future(nm: &str) -> bool { + nm.starts_with("ForeignFuture") || nm.starts_with("RustFuture") +} + +fn is_free(nm: &str) -> bool { + nm == "CallbackInterfaceFree" || nm == "ForeignFutureFree" +} + +#[ext] +pub(crate) impl FfiStruct { + fn cpp_namespace(&self, ci: &ComponentInterface) -> String { + let ffi_type = FfiType::Struct(self.name().to_string()); + ffi_type.cpp_namespace(ci) + } + + fn cpp_namespace_free(&self, ci: &ComponentInterface) -> String { + format!( + "{}::{}::free", + self.cpp_namespace(ci), + self.name().to_lower_camel_case().to_lowercase() + ) + } + + fn is_exported(&self) -> bool { + self.is_vtable() || self.is_future() + } + + fn is_future(&self) -> bool { + self.name().starts_with("ForeignFuture") + } + + fn is_vtable(&self) -> bool { + self.fields().iter().any(|f| f.type_().is_callable()) + } + + fn ffi_functions(&self) -> impl Iterator { + self.fields().iter().filter(|f| f.type_().is_callable()) + } +} + +#[ext] +pub(crate) impl FfiField { + fn is_free(&self) -> bool { + matches!(self.type_(), FfiType::Callback(s) if is_free(&s)) + } +} diff --git a/crates/ubrn_bindgen/src/bindings/react_native/gen_typescript/callback_interface.rs b/crates/ubrn_bindgen/src/bindings/gen_typescript/callback_interface.rs similarity index 100% rename from crates/ubrn_bindgen/src/bindings/react_native/gen_typescript/callback_interface.rs rename to crates/ubrn_bindgen/src/bindings/gen_typescript/callback_interface.rs diff --git a/crates/ubrn_bindgen/src/bindings/react_native/gen_typescript/compounds.rs b/crates/ubrn_bindgen/src/bindings/gen_typescript/compounds.rs similarity index 100% rename from crates/ubrn_bindgen/src/bindings/react_native/gen_typescript/compounds.rs rename to crates/ubrn_bindgen/src/bindings/gen_typescript/compounds.rs diff --git a/crates/ubrn_bindgen/src/bindings/react_native/gen_typescript/custom.rs b/crates/ubrn_bindgen/src/bindings/gen_typescript/custom.rs similarity index 100% rename from crates/ubrn_bindgen/src/bindings/react_native/gen_typescript/custom.rs rename to crates/ubrn_bindgen/src/bindings/gen_typescript/custom.rs diff --git a/crates/ubrn_bindgen/src/bindings/react_native/gen_typescript/enum_.rs b/crates/ubrn_bindgen/src/bindings/gen_typescript/enum_.rs similarity index 100% rename from crates/ubrn_bindgen/src/bindings/react_native/gen_typescript/enum_.rs rename to crates/ubrn_bindgen/src/bindings/gen_typescript/enum_.rs diff --git a/crates/ubrn_bindgen/src/bindings/react_native/gen_typescript/filters.rs b/crates/ubrn_bindgen/src/bindings/gen_typescript/filters.rs similarity index 100% rename from crates/ubrn_bindgen/src/bindings/react_native/gen_typescript/filters.rs rename to crates/ubrn_bindgen/src/bindings/gen_typescript/filters.rs diff --git a/crates/ubrn_bindgen/src/bindings/react_native/gen_typescript/miscellany.rs b/crates/ubrn_bindgen/src/bindings/gen_typescript/miscellany.rs similarity index 100% rename from crates/ubrn_bindgen/src/bindings/react_native/gen_typescript/miscellany.rs rename to crates/ubrn_bindgen/src/bindings/gen_typescript/miscellany.rs diff --git a/crates/ubrn_bindgen/src/bindings/react_native/gen_typescript/mod.rs b/crates/ubrn_bindgen/src/bindings/gen_typescript/mod.rs similarity index 98% rename from crates/ubrn_bindgen/src/bindings/react_native/gen_typescript/mod.rs rename to crates/ubrn_bindgen/src/bindings/gen_typescript/mod.rs index 72a56b59..29aa896c 100644 --- a/crates/ubrn_bindgen/src/bindings/react_native/gen_typescript/mod.rs +++ b/crates/ubrn_bindgen/src/bindings/gen_typescript/mod.rs @@ -26,10 +26,10 @@ use std::collections::{BTreeMap, BTreeSet, HashSet}; use uniffi_bindgen::interface::{AsType, Callable, FfiDefinition, FfiType, Type, UniffiTrait}; use uniffi_bindgen::ComponentInterface; -use crate::bindings::metadata::ModuleMetadata; -use crate::bindings::react_native::{ +use crate::bindings::extensions::{ ComponentInterfaceExt, FfiCallbackFunctionExt, FfiFunctionExt, FfiStructExt, ObjectExt, }; +use crate::bindings::metadata::ModuleMetadata; use crate::bindings::type_map::TypeMap; #[derive(Default)] @@ -38,7 +38,7 @@ pub(crate) struct TsBindings { pub(crate) frontend: String, } -type Config = crate::bindings::react_native::uniffi_toml::TsConfig; +type Config = crate::bindings::uniffi_toml::TsConfig; pub(crate) fn generate_bindings( ci: &ComponentInterface, diff --git a/crates/ubrn_bindgen/src/bindings/react_native/gen_typescript/object.rs b/crates/ubrn_bindgen/src/bindings/gen_typescript/object.rs similarity index 100% rename from crates/ubrn_bindgen/src/bindings/react_native/gen_typescript/object.rs rename to crates/ubrn_bindgen/src/bindings/gen_typescript/object.rs diff --git a/crates/ubrn_bindgen/src/bindings/react_native/gen_typescript/oracle.rs b/crates/ubrn_bindgen/src/bindings/gen_typescript/oracle.rs similarity index 100% rename from crates/ubrn_bindgen/src/bindings/react_native/gen_typescript/oracle.rs rename to crates/ubrn_bindgen/src/bindings/gen_typescript/oracle.rs diff --git a/crates/ubrn_bindgen/src/bindings/react_native/gen_typescript/primitives.rs b/crates/ubrn_bindgen/src/bindings/gen_typescript/primitives.rs similarity index 100% rename from crates/ubrn_bindgen/src/bindings/react_native/gen_typescript/primitives.rs rename to crates/ubrn_bindgen/src/bindings/gen_typescript/primitives.rs diff --git a/crates/ubrn_bindgen/src/bindings/react_native/gen_typescript/record.rs b/crates/ubrn_bindgen/src/bindings/gen_typescript/record.rs similarity index 100% rename from crates/ubrn_bindgen/src/bindings/react_native/gen_typescript/record.rs rename to crates/ubrn_bindgen/src/bindings/gen_typescript/record.rs diff --git a/crates/ubrn_bindgen/src/bindings/react_native/gen_typescript/templates/CallbackInterfaceImpl.ts b/crates/ubrn_bindgen/src/bindings/gen_typescript/templates/CallbackInterfaceImpl.ts similarity index 100% rename from crates/ubrn_bindgen/src/bindings/react_native/gen_typescript/templates/CallbackInterfaceImpl.ts rename to crates/ubrn_bindgen/src/bindings/gen_typescript/templates/CallbackInterfaceImpl.ts diff --git a/crates/ubrn_bindgen/src/bindings/react_native/gen_typescript/templates/CallbackInterfaceTemplate.ts b/crates/ubrn_bindgen/src/bindings/gen_typescript/templates/CallbackInterfaceTemplate.ts similarity index 100% rename from crates/ubrn_bindgen/src/bindings/react_native/gen_typescript/templates/CallbackInterfaceTemplate.ts rename to crates/ubrn_bindgen/src/bindings/gen_typescript/templates/CallbackInterfaceTemplate.ts diff --git a/crates/ubrn_bindgen/src/bindings/react_native/gen_typescript/templates/CustomTypeTemplate.ts b/crates/ubrn_bindgen/src/bindings/gen_typescript/templates/CustomTypeTemplate.ts similarity index 100% rename from crates/ubrn_bindgen/src/bindings/react_native/gen_typescript/templates/CustomTypeTemplate.ts rename to crates/ubrn_bindgen/src/bindings/gen_typescript/templates/CustomTypeTemplate.ts diff --git a/crates/ubrn_bindgen/src/bindings/react_native/gen_typescript/templates/EnumTemplate.ts b/crates/ubrn_bindgen/src/bindings/gen_typescript/templates/EnumTemplate.ts similarity index 100% rename from crates/ubrn_bindgen/src/bindings/react_native/gen_typescript/templates/EnumTemplate.ts rename to crates/ubrn_bindgen/src/bindings/gen_typescript/templates/EnumTemplate.ts diff --git a/crates/ubrn_bindgen/src/bindings/react_native/gen_typescript/templates/ErrorTemplate.ts b/crates/ubrn_bindgen/src/bindings/gen_typescript/templates/ErrorTemplate.ts similarity index 100% rename from crates/ubrn_bindgen/src/bindings/react_native/gen_typescript/templates/ErrorTemplate.ts rename to crates/ubrn_bindgen/src/bindings/gen_typescript/templates/ErrorTemplate.ts diff --git a/crates/ubrn_bindgen/src/bindings/react_native/gen_typescript/templates/ExternalTemplate.ts b/crates/ubrn_bindgen/src/bindings/gen_typescript/templates/ExternalTemplate.ts similarity index 100% rename from crates/ubrn_bindgen/src/bindings/react_native/gen_typescript/templates/ExternalTemplate.ts rename to crates/ubrn_bindgen/src/bindings/gen_typescript/templates/ExternalTemplate.ts diff --git a/crates/ubrn_bindgen/src/bindings/react_native/gen_typescript/templates/InitializationTemplate.ts b/crates/ubrn_bindgen/src/bindings/gen_typescript/templates/InitializationTemplate.ts similarity index 100% rename from crates/ubrn_bindgen/src/bindings/react_native/gen_typescript/templates/InitializationTemplate.ts rename to crates/ubrn_bindgen/src/bindings/gen_typescript/templates/InitializationTemplate.ts diff --git a/crates/ubrn_bindgen/src/bindings/react_native/gen_typescript/templates/MapTemplate.ts b/crates/ubrn_bindgen/src/bindings/gen_typescript/templates/MapTemplate.ts similarity index 100% rename from crates/ubrn_bindgen/src/bindings/react_native/gen_typescript/templates/MapTemplate.ts rename to crates/ubrn_bindgen/src/bindings/gen_typescript/templates/MapTemplate.ts diff --git a/crates/ubrn_bindgen/src/bindings/react_native/gen_typescript/templates/ObjectInterfaceTemplate.ts b/crates/ubrn_bindgen/src/bindings/gen_typescript/templates/ObjectInterfaceTemplate.ts similarity index 100% rename from crates/ubrn_bindgen/src/bindings/react_native/gen_typescript/templates/ObjectInterfaceTemplate.ts rename to crates/ubrn_bindgen/src/bindings/gen_typescript/templates/ObjectInterfaceTemplate.ts diff --git a/crates/ubrn_bindgen/src/bindings/react_native/gen_typescript/templates/ObjectTemplate.ts b/crates/ubrn_bindgen/src/bindings/gen_typescript/templates/ObjectTemplate.ts similarity index 100% rename from crates/ubrn_bindgen/src/bindings/react_native/gen_typescript/templates/ObjectTemplate.ts rename to crates/ubrn_bindgen/src/bindings/gen_typescript/templates/ObjectTemplate.ts diff --git a/crates/ubrn_bindgen/src/bindings/react_native/gen_typescript/templates/OptionalTemplate.ts b/crates/ubrn_bindgen/src/bindings/gen_typescript/templates/OptionalTemplate.ts similarity index 100% rename from crates/ubrn_bindgen/src/bindings/react_native/gen_typescript/templates/OptionalTemplate.ts rename to crates/ubrn_bindgen/src/bindings/gen_typescript/templates/OptionalTemplate.ts diff --git a/crates/ubrn_bindgen/src/bindings/react_native/gen_typescript/templates/RecordTemplate.ts b/crates/ubrn_bindgen/src/bindings/gen_typescript/templates/RecordTemplate.ts similarity index 100% rename from crates/ubrn_bindgen/src/bindings/react_native/gen_typescript/templates/RecordTemplate.ts rename to crates/ubrn_bindgen/src/bindings/gen_typescript/templates/RecordTemplate.ts diff --git a/crates/ubrn_bindgen/src/bindings/react_native/gen_typescript/templates/SequenceTemplate.ts b/crates/ubrn_bindgen/src/bindings/gen_typescript/templates/SequenceTemplate.ts similarity index 100% rename from crates/ubrn_bindgen/src/bindings/react_native/gen_typescript/templates/SequenceTemplate.ts rename to crates/ubrn_bindgen/src/bindings/gen_typescript/templates/SequenceTemplate.ts diff --git a/crates/ubrn_bindgen/src/bindings/react_native/gen_typescript/templates/StringHelper.ts b/crates/ubrn_bindgen/src/bindings/gen_typescript/templates/StringHelper.ts similarity index 100% rename from crates/ubrn_bindgen/src/bindings/react_native/gen_typescript/templates/StringHelper.ts rename to crates/ubrn_bindgen/src/bindings/gen_typescript/templates/StringHelper.ts diff --git a/crates/ubrn_bindgen/src/bindings/react_native/gen_typescript/templates/TaggedEnumTemplate.ts b/crates/ubrn_bindgen/src/bindings/gen_typescript/templates/TaggedEnumTemplate.ts similarity index 100% rename from crates/ubrn_bindgen/src/bindings/react_native/gen_typescript/templates/TaggedEnumTemplate.ts rename to crates/ubrn_bindgen/src/bindings/gen_typescript/templates/TaggedEnumTemplate.ts diff --git a/crates/ubrn_bindgen/src/bindings/react_native/gen_typescript/templates/TopLevelFunctionTemplate.ts b/crates/ubrn_bindgen/src/bindings/gen_typescript/templates/TopLevelFunctionTemplate.ts similarity index 100% rename from crates/ubrn_bindgen/src/bindings/react_native/gen_typescript/templates/TopLevelFunctionTemplate.ts rename to crates/ubrn_bindgen/src/bindings/gen_typescript/templates/TopLevelFunctionTemplate.ts diff --git a/crates/ubrn_bindgen/src/bindings/react_native/gen_typescript/templates/Types.ts b/crates/ubrn_bindgen/src/bindings/gen_typescript/templates/Types.ts similarity index 100% rename from crates/ubrn_bindgen/src/bindings/react_native/gen_typescript/templates/Types.ts rename to crates/ubrn_bindgen/src/bindings/gen_typescript/templates/Types.ts diff --git a/crates/ubrn_bindgen/src/bindings/react_native/gen_typescript/templates/macros.ts b/crates/ubrn_bindgen/src/bindings/gen_typescript/templates/macros.ts similarity index 100% rename from crates/ubrn_bindgen/src/bindings/react_native/gen_typescript/templates/macros.ts rename to crates/ubrn_bindgen/src/bindings/gen_typescript/templates/macros.ts diff --git a/crates/ubrn_bindgen/src/bindings/react_native/gen_typescript/templates/wrapper-ffi.ts b/crates/ubrn_bindgen/src/bindings/gen_typescript/templates/wrapper-ffi.ts similarity index 100% rename from crates/ubrn_bindgen/src/bindings/react_native/gen_typescript/templates/wrapper-ffi.ts rename to crates/ubrn_bindgen/src/bindings/gen_typescript/templates/wrapper-ffi.ts diff --git a/crates/ubrn_bindgen/src/bindings/react_native/gen_typescript/templates/wrapper.ts b/crates/ubrn_bindgen/src/bindings/gen_typescript/templates/wrapper.ts similarity index 100% rename from crates/ubrn_bindgen/src/bindings/react_native/gen_typescript/templates/wrapper.ts rename to crates/ubrn_bindgen/src/bindings/gen_typescript/templates/wrapper.ts diff --git a/crates/ubrn_bindgen/src/bindings/react_native/gen_typescript/variant.rs b/crates/ubrn_bindgen/src/bindings/gen_typescript/variant.rs similarity index 100% rename from crates/ubrn_bindgen/src/bindings/react_native/gen_typescript/variant.rs rename to crates/ubrn_bindgen/src/bindings/gen_typescript/variant.rs diff --git a/crates/ubrn_bindgen/src/bindings/mod.rs b/crates/ubrn_bindgen/src/bindings/mod.rs index 3b0ab0ea..b4ddfe4f 100644 --- a/crates/ubrn_bindgen/src/bindings/mod.rs +++ b/crates/ubrn_bindgen/src/bindings/mod.rs @@ -4,10 +4,13 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/ */ +pub(crate) mod extensions; +pub(crate) mod gen_typescript; pub mod metadata; pub(crate) mod react_native; pub(crate) mod switches; pub(crate) mod type_map; +pub(crate) mod uniffi_toml; use std::{fs, str::FromStr}; diff --git a/crates/ubrn_bindgen/src/bindings/react_native/gen_cpp/filters.rs b/crates/ubrn_bindgen/src/bindings/react_native/gen_cpp/filters.rs index d1aad12e..1e8bb0c4 100644 --- a/crates/ubrn_bindgen/src/bindings/react_native/gen_cpp/filters.rs +++ b/crates/ubrn_bindgen/src/bindings/react_native/gen_cpp/filters.rs @@ -6,7 +6,7 @@ use heck::{ToLowerCamelCase, ToUpperCamelCase}; use uniffi_bindgen::{interface::FfiType, ComponentInterface}; -use crate::bindings::react_native::{ComponentInterfaceExt, FfiTypeExt}; +use crate::bindings::extensions::{ComponentInterfaceExt, FfiTypeExt}; pub fn ffi_type_name_from_js(ffi_type: &FfiType) -> Result { Ok(match ffi_type { diff --git a/crates/ubrn_bindgen/src/bindings/react_native/gen_cpp/mod.rs b/crates/ubrn_bindgen/src/bindings/react_native/gen_cpp/mod.rs index c6f75846..0fc22572 100644 --- a/crates/ubrn_bindgen/src/bindings/react_native/gen_cpp/mod.rs +++ b/crates/ubrn_bindgen/src/bindings/react_native/gen_cpp/mod.rs @@ -5,20 +5,22 @@ */ mod filters; +use std::borrow::Borrow; + +use anyhow::Result; +use askama::Template; +use uniffi_bindgen::interface::{FfiDefinition, FfiType}; +use uniffi_bindgen::ComponentInterface; + use crate::bindings::{ - metadata::ModuleMetadata, - react_native::{ + extensions::{ ComponentInterfaceExt, FfiArgumentExt, FfiCallbackFunctionExt, FfiFieldExt, FfiStructExt, FfiTypeExt, ObjectExt, }, + metadata::ModuleMetadata, }; -use anyhow::Result; -use askama::Template; -use std::borrow::Borrow; -use uniffi_bindgen::interface::{FfiDefinition, FfiType}; -use uniffi_bindgen::ComponentInterface; -type Config = crate::bindings::react_native::uniffi_toml::CppConfig; +type Config = crate::bindings::uniffi_toml::CppConfig; #[derive(Debug, Default)] pub(crate) struct CppBindings { diff --git a/crates/ubrn_bindgen/src/bindings/react_native/mod.rs b/crates/ubrn_bindgen/src/bindings/react_native/mod.rs index 1f12d6e8..81cd7050 100644 --- a/crates/ubrn_bindgen/src/bindings/react_native/mod.rs +++ b/crates/ubrn_bindgen/src/bindings/react_native/mod.rs @@ -4,28 +4,16 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/ */ mod gen_cpp; -mod gen_typescript; -mod uniffi_toml; -use std::{collections::HashMap, fs}; +use std::fs; +use self::gen_cpp::CppBindings; +use crate::bindings::gen_typescript::{self, TsBindings}; +use crate::bindings::uniffi_toml::ReactNativeConfig; use anyhow::Result; use camino::Utf8Path; -use extend::ext; -use heck::{ToLowerCamelCase, ToSnakeCase}; -use topological_sort::TopologicalSort; use ubrn_common::{fmt, run_cmd_quietly}; -use uniffi_bindgen::{ - interface::{ - FfiArgument, FfiCallbackFunction, FfiDefinition, FfiField, FfiFunction, FfiStruct, FfiType, - Function, Method, Object, UniffiTrait, - }, - BindingGenerator, Component, ComponentInterface, GenerationSettings, -}; -use uniffi_meta::Type; -use uniffi_toml::ReactNativeConfig; - -use self::{gen_cpp::CppBindings, gen_typescript::TsBindings}; +use uniffi_bindgen::{BindingGenerator, Component, GenerationSettings}; use super::{type_map::TypeMap, OutputArgs}; use crate::bindings::metadata::ModuleMetadata; @@ -118,438 +106,3 @@ fn format_cpp(out_dir: &Utf8Path) -> Result<()> { } Ok(()) } - -#[ext] -impl ComponentInterface { - fn ffi_function_string_to_arraybuffer(&self) -> FfiFunction { - let meta = uniffi_meta::FnMetadata { - module_path: "internal".to_string(), - name: "ffi__string_to_arraybuffer".to_owned(), - is_async: false, - inputs: Default::default(), - return_type: None, - throws: None, - checksum: None, - docstring: None, - }; - let func: Function = meta.into(); - let mut ffi = func.ffi_func().clone(); - ffi.init( - Some(FfiType::ForeignBytes), - vec![FfiArgument::new("string", FfiType::RustBuffer(None))], - ); - ffi.clone() - } - - fn ffi_function_arraybuffer_to_string(&self) -> FfiFunction { - let meta = uniffi_meta::FnMetadata { - module_path: "internal".to_string(), - name: "ffi__arraybuffer_to_string".to_owned(), - is_async: false, - inputs: Default::default(), - return_type: None, - throws: None, - checksum: None, - docstring: None, - }; - let func: Function = meta.into(); - let mut ffi = func.ffi_func().clone(); - ffi.init( - Some(FfiType::RustBuffer(None)), - vec![FfiArgument::new("buffer", FfiType::ForeignBytes)], - ); - ffi.clone() - } - - fn ffi_function_string_to_bytelength(&self) -> FfiFunction { - let meta = uniffi_meta::FnMetadata { - module_path: "internal".to_string(), - name: "ffi__string_to_byte_length".to_owned(), - is_async: false, - inputs: Default::default(), - return_type: None, - throws: None, - checksum: None, - docstring: None, - }; - let func: Function = meta.into(); - let mut ffi = func.ffi_func().clone(); - ffi.init( - Some(FfiType::Int32), - vec![FfiArgument::new("string", FfiType::RustBuffer(None))], - ); - ffi.clone() - } - - fn iter_ffi_functions_js_to_cpp_and_back(&self) -> impl Iterator { - vec![ - self.ffi_function_string_to_bytelength(), - self.ffi_function_string_to_arraybuffer(), - self.ffi_function_arraybuffer_to_string(), - ] - .into_iter() - } - - fn iter_ffi_functions_js_to_cpp(&self) -> impl Iterator { - self.iter_ffi_functions_js_to_cpp_and_back() - .chain(self.iter_ffi_functions_js_to_rust()) - .chain(self.iter_ffi_functions_init_callback()) - .chain(self.iter_ffi_function_bless_pointer()) - } - - fn iter_ffi_functions_js_to_rust(&self) -> impl Iterator { - let has_async = self.has_async_fns(); - self.iter_ffi_function_definitions().filter(move |f| { - let name = f.name(); - !name.contains("_rustbuffer_") - && (has_async || !name.contains("_rust_future_")) - && !name.contains("_callback_vtable_") - }) - } - - fn iter_ffi_functions_cpp_to_rust(&self) -> impl Iterator { - self.iter_ffi_functions_js_to_rust() - } - - fn iter_ffi_functions_init_callback(&self) -> impl Iterator { - self.callback_interface_definitions() - .iter() - .map(|cb| cb.ffi_init_callback().clone()) - .chain(self.object_definitions().iter().filter_map(|obj| { - if obj.has_callback_interface() { - Some(obj.ffi_init_callback().clone()) - } else { - None - } - })) - } - - fn iter_ffi_function_bless_pointer(&self) -> impl Iterator { - self.object_definitions() - .iter() - .map(|o| o.ffi_function_bless_pointer()) - } - - fn iter_ffi_structs(&self) -> impl Iterator { - self.ffi_definitions().filter_map(|s| match s { - FfiDefinition::Struct(s) => Some(s), - _ => None, - }) - } - - fn iter_ffi_structs_for_free(&self) -> impl Iterator { - self.iter_ffi_structs() - .filter(|s| !s.is_future() || s.name() == "ForeignFuture") - } - - fn iter_ffi_definitions_exported_by_ts(&self) -> impl Iterator { - self.ffi_definitions().filter(|d| d.is_exported()) - } - - fn cpp_namespace(&self) -> String { - format!("uniffi::{}", self.namespace().to_snake_case()) - } - - fn cpp_namespace_includes(&self) -> String { - "uniffi_jsi".to_string() - } - - /// We want to control the ordering of definitions in typescript, especially - /// the FfiConverters which rely on Immediately Invoked Function Expressions (IIFE), - /// or straight up expressions. - /// - /// These are mostly the structural types, but might also be others. - /// - /// The current implementations of the FfiConverters for Enums and Records do not - /// require other FfiConverters at initialization, so we don't need to worry about - /// ordering around their members. - /// - /// We can simplify code generation a little bit by including types which may - /// not be used— e.g. UInt64 is used by object internally, but unlikely to be used by - /// client code— we do this here, and clean up the codegen at a later stage. - fn iter_sorted_types(&self) -> impl Iterator { - let mut graph = TopologicalSort::::new(); - let mut types: HashMap = Default::default(); - for type_ in self.iter_types() { - match type_ { - Type::Object { name, .. } => { - // Objects only rely on a pointer, not the fields backing it. - add_edge(&mut graph, &mut types, type_, &Type::UInt64); - // Fields in the constructor are executed long after everything has - // been initialized. - if self.is_name_used_as_error(name) { - add_edge(&mut graph, &mut types, type_, &Type::Int32); - add_edge(&mut graph, &mut types, type_, &Type::String); - } - } - Type::Enum { name, .. } => { - // Ordinals are Int32. - add_edge(&mut graph, &mut types, type_, &Type::Int32); - if self.is_name_used_as_error(name) { - add_edge(&mut graph, &mut types, type_, &Type::String); - } - } - Type::Custom { builtin, .. } => { - add_edge(&mut graph, &mut types, type_, builtin.as_ref()); - } - Type::Optional { inner_type } => { - add_edge(&mut graph, &mut types, type_, &Type::Boolean); - add_edge(&mut graph, &mut types, type_, inner_type.as_ref()); - } - Type::Sequence { inner_type } => { - add_edge(&mut graph, &mut types, type_, &Type::Int32); - add_edge(&mut graph, &mut types, type_, inner_type.as_ref()); - } - Type::Map { - key_type, - value_type, - } => { - add_edge(&mut graph, &mut types, type_, key_type.as_ref()); - add_edge(&mut graph, &mut types, type_, value_type.as_ref()); - } - _ => { - let name = store_with_name(&mut types, type_); - graph.insert(name); - } - } - } - - let mut sorted: Vec = Vec::new(); - while !graph.peek_all().is_empty() { - let mut next = graph.pop_all(); - next.sort(); - sorted.extend(next.iter().filter_map(|name| types.remove(name))); - } - - if !graph.is_empty() { - eprintln!( - "WARN: Cyclic dependency for typescript types: {:?}", - types.values() - ); - // We only warn if we have a cyclic dependency because by this stage, - // the code generation should may be able to handle a cycle. - // In practice, however, this will only occur if this method changes. - } - - // I think that types should be empty by now, but we should add the remaining - // values in to sorted, then return. - sorted.into_iter().chain(types.into_values()) - } -} - -fn add_edge( - graph: &mut TopologicalSort, - types: &mut HashMap, - src: &Type, - dest: &Type, -) { - let src_name = store_with_name(types, src); - let dest_name = store_with_name(types, dest); - if src_name != dest_name { - graph.add_dependency(dest_name, src_name); - } -} - -fn store_with_name(types: &mut HashMap, type_: &Type) -> String { - let name = format!("{type_:?}"); - types.entry(name.clone()).or_insert_with(|| type_.clone()); - name -} - -#[ext] -impl Object { - fn is_uniffi_trait(t: &UniffiTrait, nm: &str) -> bool { - match t { - UniffiTrait::Debug { .. } => nm == "Debug", - UniffiTrait::Display { .. } => nm == "Display", - UniffiTrait::Eq { .. } => nm == "Eq", - UniffiTrait::Hash { .. } => nm == "Hash", - } - } - - fn has_uniffi_trait(&self, nm: &str) -> bool { - self.uniffi_traits() - .iter() - .any(|t| Self::is_uniffi_trait(t, nm)) - } - - fn ffi_function_bless_pointer(&self) -> FfiFunction { - let meta = uniffi_meta::MethodMetadata { - module_path: "internal".to_string(), - self_name: self.name().to_string(), - name: "ffi__bless_pointer".to_owned(), - is_async: false, - inputs: Default::default(), - return_type: None, - throws: None, - checksum: None, - docstring: None, - takes_self_by_arc: false, - }; - let func: Method = meta.into(); - let mut ffi = func.ffi_func().clone(); - ffi.init( - Some(FfiType::RustArcPtr(String::from(""))), - vec![FfiArgument::new("pointer", FfiType::UInt64)], - ); - ffi - } -} - -#[ext] -impl FfiFunction { - fn is_internal(&self) -> bool { - let name = self.name(); - name.contains("ffi__") && name.contains("_internal_") - } -} - -#[ext] -impl FfiDefinition { - fn is_exported(&self) -> bool { - match self { - Self::Function(_) => false, - Self::CallbackFunction(cb) => cb.is_exported(), - Self::Struct(s) => s.is_exported(), - } - } -} - -#[ext] -impl FfiType { - fn is_callable(&self) -> bool { - matches!(self, Self::Callback(_)) - } - - fn is_void(&self) -> bool { - matches!(self, Self::VoidPointer) - } - - fn cpp_namespace(&self, ci: &ComponentInterface) -> String { - match self { - Self::Int8 - | Self::Int16 - | Self::Int32 - | Self::Int64 - | Self::UInt8 - | Self::UInt16 - | Self::UInt32 - | Self::UInt64 - | Self::Float32 - | Self::Float64 - | Self::Handle - | Self::RustCallStatus - | Self::RustArcPtr(_) - | Self::RustBuffer(_) - | Self::VoidPointer => ci.cpp_namespace_includes(), - Self::Callback(name) => format!( - "{}::cb::{}", - ci.cpp_namespace(), - name.to_lower_camel_case().to_lowercase() - ), - Self::Struct(name) => format!( - "{}::st::{}", - ci.cpp_namespace(), - name.to_lower_camel_case().to_lowercase() - ), - _ => ci.cpp_namespace(), - } - } -} - -#[ext] -impl FfiArgument { - fn is_return(&self) -> bool { - self.name() == "uniffi_out_return" - } -} - -#[ext] -impl FfiCallbackFunction { - fn cpp_namespace(&self, ci: &ComponentInterface) -> String { - let ffi_type = FfiType::Callback(self.name().to_string()); - ffi_type.cpp_namespace(ci) - } - - fn is_exported(&self) -> bool { - let name = self.name(); - [ - "RustFutureContinuationCallback", - "CallbackInterfaceFree", - "ForeignFutureFree", - ] - .contains(&name) - || !name.starts_with("CallbackInterface") - } - - fn is_rust_calling_js(&self) -> bool { - !self.is_future_callback() || self.name() == "RustFutureContinuationCallback" - } - - fn is_free_callback(&self) -> bool { - is_free(self.name()) - } - - fn is_future_callback(&self) -> bool { - is_future(self.name()) - } - - fn arg_return_type(&self) -> Option { - let arg = self - .arguments() - .into_iter() - .find(|a| a.is_return() && !a.type_().is_void()); - arg.map(|a| a.type_()) - } - - fn is_blocking(&self) -> bool { - self.name() != "RustFutureContinuationCallback" - } -} - -fn is_future(nm: &str) -> bool { - nm.starts_with("ForeignFuture") || nm.starts_with("RustFuture") -} - -fn is_free(nm: &str) -> bool { - nm == "CallbackInterfaceFree" || nm == "ForeignFutureFree" -} - -#[ext] -impl FfiStruct { - fn cpp_namespace(&self, ci: &ComponentInterface) -> String { - let ffi_type = FfiType::Struct(self.name().to_string()); - ffi_type.cpp_namespace(ci) - } - - fn cpp_namespace_free(&self, ci: &ComponentInterface) -> String { - format!( - "{}::{}::free", - self.cpp_namespace(ci), - self.name().to_lower_camel_case().to_lowercase() - ) - } - - fn is_exported(&self) -> bool { - self.is_vtable() || self.is_future() - } - - fn is_future(&self) -> bool { - self.name().starts_with("ForeignFuture") - } - - fn is_vtable(&self) -> bool { - self.fields().iter().any(|f| f.type_().is_callable()) - } - - fn ffi_functions(&self) -> impl Iterator { - self.fields().iter().filter(|f| f.type_().is_callable()) - } -} - -#[ext] -impl FfiField { - fn is_free(&self) -> bool { - matches!(self.type_(), FfiType::Callback(s) if is_free(&s)) - } -} diff --git a/crates/ubrn_bindgen/src/bindings/react_native/uniffi_toml.rs b/crates/ubrn_bindgen/src/bindings/uniffi_toml.rs similarity index 100% rename from crates/ubrn_bindgen/src/bindings/react_native/uniffi_toml.rs rename to crates/ubrn_bindgen/src/bindings/uniffi_toml.rs From 450f2bc99741387d078bb04a4374ffe89db5887d Mon Sep 17 00:00:00 2001 From: James Hugman Date: Tue, 3 Dec 2024 18:10:04 +0000 Subject: [PATCH 05/13] Move get_cpp out of react-native --- crates/ubrn_bindgen/askama.toml | 2 +- .../{react_native => }/gen_cpp/filters.rs | 0 .../bindings/{react_native => }/gen_cpp/mod.rs | 0 .../gen_cpp/templates/BridgingHelper.cpp | 0 .../gen_cpp/templates/CallbackFunction.cpp | 0 .../gen_cpp/templates/ForeignFuture.cpp | 0 .../gen_cpp/templates/Future.cpp | 0 .../gen_cpp/templates/ObjectHelper.cpp | 0 .../gen_cpp/templates/RustBufferHelper.cpp | 0 .../gen_cpp/templates/RustCallStatusHelper.cpp | 0 .../gen_cpp/templates/StringHelper.cpp | 0 .../gen_cpp/templates/Struct.cpp | 0 .../gen_cpp/templates/entrypoint.cpp | 0 .../gen_cpp/templates/macros.cpp | 0 .../gen_cpp/templates/macros.hpp | 0 .../gen_cpp/templates/wrapper.cpp | 0 .../gen_cpp/templates/wrapper.hpp | 0 crates/ubrn_bindgen/src/bindings/mod.rs | 1 + .../{react_native/mod.rs => react_native.rs} | 17 ++++++++++------- 19 files changed, 12 insertions(+), 8 deletions(-) rename crates/ubrn_bindgen/src/bindings/{react_native => }/gen_cpp/filters.rs (100%) rename crates/ubrn_bindgen/src/bindings/{react_native => }/gen_cpp/mod.rs (100%) rename crates/ubrn_bindgen/src/bindings/{react_native => }/gen_cpp/templates/BridgingHelper.cpp (100%) rename crates/ubrn_bindgen/src/bindings/{react_native => }/gen_cpp/templates/CallbackFunction.cpp (100%) rename crates/ubrn_bindgen/src/bindings/{react_native => }/gen_cpp/templates/ForeignFuture.cpp (100%) rename crates/ubrn_bindgen/src/bindings/{react_native => }/gen_cpp/templates/Future.cpp (100%) rename crates/ubrn_bindgen/src/bindings/{react_native => }/gen_cpp/templates/ObjectHelper.cpp (100%) rename crates/ubrn_bindgen/src/bindings/{react_native => }/gen_cpp/templates/RustBufferHelper.cpp (100%) rename crates/ubrn_bindgen/src/bindings/{react_native => }/gen_cpp/templates/RustCallStatusHelper.cpp (100%) rename crates/ubrn_bindgen/src/bindings/{react_native => }/gen_cpp/templates/StringHelper.cpp (100%) rename crates/ubrn_bindgen/src/bindings/{react_native => }/gen_cpp/templates/Struct.cpp (100%) rename crates/ubrn_bindgen/src/bindings/{react_native => }/gen_cpp/templates/entrypoint.cpp (100%) rename crates/ubrn_bindgen/src/bindings/{react_native => }/gen_cpp/templates/macros.cpp (100%) rename crates/ubrn_bindgen/src/bindings/{react_native => }/gen_cpp/templates/macros.hpp (100%) rename crates/ubrn_bindgen/src/bindings/{react_native => }/gen_cpp/templates/wrapper.cpp (100%) rename crates/ubrn_bindgen/src/bindings/{react_native => }/gen_cpp/templates/wrapper.hpp (100%) rename crates/ubrn_bindgen/src/bindings/{react_native/mod.rs => react_native.rs} (92%) diff --git a/crates/ubrn_bindgen/askama.toml b/crates/ubrn_bindgen/askama.toml index a10c0e6f..f7153dd4 100644 --- a/crates/ubrn_bindgen/askama.toml +++ b/crates/ubrn_bindgen/askama.toml @@ -1,5 +1,5 @@ [general] -dirs = ["src/bindings/gen_typescript/templates", "src/bindings/react_native/gen_cpp/templates"] +dirs = ["src/bindings/gen_typescript/templates", "src/bindings/gen_cpp/templates"] [[syntax]] name = "ts" diff --git a/crates/ubrn_bindgen/src/bindings/react_native/gen_cpp/filters.rs b/crates/ubrn_bindgen/src/bindings/gen_cpp/filters.rs similarity index 100% rename from crates/ubrn_bindgen/src/bindings/react_native/gen_cpp/filters.rs rename to crates/ubrn_bindgen/src/bindings/gen_cpp/filters.rs diff --git a/crates/ubrn_bindgen/src/bindings/react_native/gen_cpp/mod.rs b/crates/ubrn_bindgen/src/bindings/gen_cpp/mod.rs similarity index 100% rename from crates/ubrn_bindgen/src/bindings/react_native/gen_cpp/mod.rs rename to crates/ubrn_bindgen/src/bindings/gen_cpp/mod.rs diff --git a/crates/ubrn_bindgen/src/bindings/react_native/gen_cpp/templates/BridgingHelper.cpp b/crates/ubrn_bindgen/src/bindings/gen_cpp/templates/BridgingHelper.cpp similarity index 100% rename from crates/ubrn_bindgen/src/bindings/react_native/gen_cpp/templates/BridgingHelper.cpp rename to crates/ubrn_bindgen/src/bindings/gen_cpp/templates/BridgingHelper.cpp diff --git a/crates/ubrn_bindgen/src/bindings/react_native/gen_cpp/templates/CallbackFunction.cpp b/crates/ubrn_bindgen/src/bindings/gen_cpp/templates/CallbackFunction.cpp similarity index 100% rename from crates/ubrn_bindgen/src/bindings/react_native/gen_cpp/templates/CallbackFunction.cpp rename to crates/ubrn_bindgen/src/bindings/gen_cpp/templates/CallbackFunction.cpp diff --git a/crates/ubrn_bindgen/src/bindings/react_native/gen_cpp/templates/ForeignFuture.cpp b/crates/ubrn_bindgen/src/bindings/gen_cpp/templates/ForeignFuture.cpp similarity index 100% rename from crates/ubrn_bindgen/src/bindings/react_native/gen_cpp/templates/ForeignFuture.cpp rename to crates/ubrn_bindgen/src/bindings/gen_cpp/templates/ForeignFuture.cpp diff --git a/crates/ubrn_bindgen/src/bindings/react_native/gen_cpp/templates/Future.cpp b/crates/ubrn_bindgen/src/bindings/gen_cpp/templates/Future.cpp similarity index 100% rename from crates/ubrn_bindgen/src/bindings/react_native/gen_cpp/templates/Future.cpp rename to crates/ubrn_bindgen/src/bindings/gen_cpp/templates/Future.cpp diff --git a/crates/ubrn_bindgen/src/bindings/react_native/gen_cpp/templates/ObjectHelper.cpp b/crates/ubrn_bindgen/src/bindings/gen_cpp/templates/ObjectHelper.cpp similarity index 100% rename from crates/ubrn_bindgen/src/bindings/react_native/gen_cpp/templates/ObjectHelper.cpp rename to crates/ubrn_bindgen/src/bindings/gen_cpp/templates/ObjectHelper.cpp diff --git a/crates/ubrn_bindgen/src/bindings/react_native/gen_cpp/templates/RustBufferHelper.cpp b/crates/ubrn_bindgen/src/bindings/gen_cpp/templates/RustBufferHelper.cpp similarity index 100% rename from crates/ubrn_bindgen/src/bindings/react_native/gen_cpp/templates/RustBufferHelper.cpp rename to crates/ubrn_bindgen/src/bindings/gen_cpp/templates/RustBufferHelper.cpp diff --git a/crates/ubrn_bindgen/src/bindings/react_native/gen_cpp/templates/RustCallStatusHelper.cpp b/crates/ubrn_bindgen/src/bindings/gen_cpp/templates/RustCallStatusHelper.cpp similarity index 100% rename from crates/ubrn_bindgen/src/bindings/react_native/gen_cpp/templates/RustCallStatusHelper.cpp rename to crates/ubrn_bindgen/src/bindings/gen_cpp/templates/RustCallStatusHelper.cpp diff --git a/crates/ubrn_bindgen/src/bindings/react_native/gen_cpp/templates/StringHelper.cpp b/crates/ubrn_bindgen/src/bindings/gen_cpp/templates/StringHelper.cpp similarity index 100% rename from crates/ubrn_bindgen/src/bindings/react_native/gen_cpp/templates/StringHelper.cpp rename to crates/ubrn_bindgen/src/bindings/gen_cpp/templates/StringHelper.cpp diff --git a/crates/ubrn_bindgen/src/bindings/react_native/gen_cpp/templates/Struct.cpp b/crates/ubrn_bindgen/src/bindings/gen_cpp/templates/Struct.cpp similarity index 100% rename from crates/ubrn_bindgen/src/bindings/react_native/gen_cpp/templates/Struct.cpp rename to crates/ubrn_bindgen/src/bindings/gen_cpp/templates/Struct.cpp diff --git a/crates/ubrn_bindgen/src/bindings/react_native/gen_cpp/templates/entrypoint.cpp b/crates/ubrn_bindgen/src/bindings/gen_cpp/templates/entrypoint.cpp similarity index 100% rename from crates/ubrn_bindgen/src/bindings/react_native/gen_cpp/templates/entrypoint.cpp rename to crates/ubrn_bindgen/src/bindings/gen_cpp/templates/entrypoint.cpp diff --git a/crates/ubrn_bindgen/src/bindings/react_native/gen_cpp/templates/macros.cpp b/crates/ubrn_bindgen/src/bindings/gen_cpp/templates/macros.cpp similarity index 100% rename from crates/ubrn_bindgen/src/bindings/react_native/gen_cpp/templates/macros.cpp rename to crates/ubrn_bindgen/src/bindings/gen_cpp/templates/macros.cpp diff --git a/crates/ubrn_bindgen/src/bindings/react_native/gen_cpp/templates/macros.hpp b/crates/ubrn_bindgen/src/bindings/gen_cpp/templates/macros.hpp similarity index 100% rename from crates/ubrn_bindgen/src/bindings/react_native/gen_cpp/templates/macros.hpp rename to crates/ubrn_bindgen/src/bindings/gen_cpp/templates/macros.hpp diff --git a/crates/ubrn_bindgen/src/bindings/react_native/gen_cpp/templates/wrapper.cpp b/crates/ubrn_bindgen/src/bindings/gen_cpp/templates/wrapper.cpp similarity index 100% rename from crates/ubrn_bindgen/src/bindings/react_native/gen_cpp/templates/wrapper.cpp rename to crates/ubrn_bindgen/src/bindings/gen_cpp/templates/wrapper.cpp diff --git a/crates/ubrn_bindgen/src/bindings/react_native/gen_cpp/templates/wrapper.hpp b/crates/ubrn_bindgen/src/bindings/gen_cpp/templates/wrapper.hpp similarity index 100% rename from crates/ubrn_bindgen/src/bindings/react_native/gen_cpp/templates/wrapper.hpp rename to crates/ubrn_bindgen/src/bindings/gen_cpp/templates/wrapper.hpp diff --git a/crates/ubrn_bindgen/src/bindings/mod.rs b/crates/ubrn_bindgen/src/bindings/mod.rs index b4ddfe4f..02f0f437 100644 --- a/crates/ubrn_bindgen/src/bindings/mod.rs +++ b/crates/ubrn_bindgen/src/bindings/mod.rs @@ -5,6 +5,7 @@ */ pub(crate) mod extensions; +pub(crate) mod gen_cpp; pub(crate) mod gen_typescript; pub mod metadata; pub(crate) mod react_native; diff --git a/crates/ubrn_bindgen/src/bindings/react_native/mod.rs b/crates/ubrn_bindgen/src/bindings/react_native.rs similarity index 92% rename from crates/ubrn_bindgen/src/bindings/react_native/mod.rs rename to crates/ubrn_bindgen/src/bindings/react_native.rs index 81cd7050..be916cc8 100644 --- a/crates/ubrn_bindgen/src/bindings/react_native/mod.rs +++ b/crates/ubrn_bindgen/src/bindings/react_native.rs @@ -3,20 +3,23 @@ * 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/ */ -mod gen_cpp; use std::fs; -use self::gen_cpp::CppBindings; -use crate::bindings::gen_typescript::{self, TsBindings}; -use crate::bindings::uniffi_toml::ReactNativeConfig; use anyhow::Result; use camino::Utf8Path; -use ubrn_common::{fmt, run_cmd_quietly}; use uniffi_bindgen::{BindingGenerator, Component, GenerationSettings}; -use super::{type_map::TypeMap, OutputArgs}; -use crate::bindings::metadata::ModuleMetadata; +use ubrn_common::{fmt, run_cmd_quietly}; + +use crate::bindings::{ + gen_cpp::{self, CppBindings}, + gen_typescript::{self, TsBindings}, + metadata::ModuleMetadata, + type_map::TypeMap, + uniffi_toml::ReactNativeConfig, + OutputArgs, +}; pub(crate) struct ReactNativeBindingGenerator { output: OutputArgs, From af80a7e66b101cfbe8117acae1b880ba97524451 Mon Sep 17 00:00:00 2001 From: James Hugman Date: Tue, 3 Dec 2024 18:39:36 +0000 Subject: [PATCH 06/13] Refactor ubrn_bindgen cli --- .../ubrn_bindgen/src/bindings/gen_cpp/mod.rs | 11 ++ crates/ubrn_bindgen/src/bindings/mod.rs | 176 +----------------- .../ubrn_bindgen/src/bindings/react_native.rs | 14 +- crates/ubrn_bindgen/src/cli.rs | 161 ++++++++++++++++ crates/ubrn_bindgen/src/lib.rs | 9 +- .../src/{bindings => }/switches.rs | 2 +- xtask/src/run/generate_bindings.rs | 14 +- 7 files changed, 200 insertions(+), 187 deletions(-) create mode 100644 crates/ubrn_bindgen/src/cli.rs rename crates/ubrn_bindgen/src/{bindings => }/switches.rs (96%) diff --git a/crates/ubrn_bindgen/src/bindings/gen_cpp/mod.rs b/crates/ubrn_bindgen/src/bindings/gen_cpp/mod.rs index 0fc22572..9232a663 100644 --- a/crates/ubrn_bindgen/src/bindings/gen_cpp/mod.rs +++ b/crates/ubrn_bindgen/src/bindings/gen_cpp/mod.rs @@ -67,3 +67,14 @@ impl<'a> CppWrapper<'a> { Self { ci, config, module } } } + +pub fn generate_entrypoint(modules: &Vec) -> Result { + let index = EntrypointCpp { modules }; + Ok(index.render()?) +} + +#[derive(Template)] +#[template(path = "entrypoint.cpp", escape = "none")] +struct EntrypointCpp<'a> { + modules: &'a Vec, +} diff --git a/crates/ubrn_bindgen/src/bindings/mod.rs b/crates/ubrn_bindgen/src/bindings/mod.rs index 02f0f437..9bc21d54 100644 --- a/crates/ubrn_bindgen/src/bindings/mod.rs +++ b/crates/ubrn_bindgen/src/bindings/mod.rs @@ -7,181 +7,7 @@ pub(crate) mod extensions; pub(crate) mod gen_cpp; pub(crate) mod gen_typescript; -pub mod metadata; +pub(crate) mod metadata; pub(crate) mod react_native; -pub(crate) mod switches; pub(crate) mod type_map; pub(crate) mod uniffi_toml; - -use std::{fs, str::FromStr}; - -use anyhow::Result; -use askama::Template; -use camino::{Utf8Path, Utf8PathBuf}; -use clap::{command, Args}; -pub use switches::{AbiFlavor, SwitchArgs}; -use ubrn_common::{mk_dir, CrateMetadata}; -use uniffi_bindgen::cargo_metadata::CrateConfigSupplier; - -use crate::ModuleMetadata; - -use self::react_native::ReactNativeBindingGenerator; - -#[derive(Args, Debug)] -pub struct BindingsArgs { - #[command(flatten)] - source: SourceArgs, - #[command(flatten)] - output: OutputArgs, - #[command(flatten)] - switches: SwitchArgs, -} - -impl BindingsArgs { - pub fn new(switches: SwitchArgs, source: SourceArgs, output: OutputArgs) -> Self { - Self { - switches, - source, - output, - } - } - - pub fn ts_dir(&self) -> &Utf8Path { - &self.output.ts_dir - } - - pub fn cpp_dir(&self) -> &Utf8Path { - &self.output.cpp_dir - } -} - -#[derive(Args, Clone, Debug)] -pub struct OutputArgs { - /// By default, bindgen will attempt to format the code with prettier and clang-format. - #[clap(long)] - no_format: bool, - - /// The directory in which to put the generated Typescript. - #[clap(long)] - ts_dir: Utf8PathBuf, - - /// The directory in which to put the generated C++. - #[clap(long, alias = "abi-dir")] - cpp_dir: Utf8PathBuf, -} - -impl OutputArgs { - pub fn new(ts_dir: &Utf8Path, cpp_dir: &Utf8Path, no_format: bool) -> Self { - Self { - ts_dir: ts_dir.to_owned(), - cpp_dir: cpp_dir.to_owned(), - no_format, - } - } -} - -#[derive(Args, Clone, Debug, Default)] -pub struct SourceArgs { - /// The path to a dynamic library to attempt to extract the definitions from - /// and extend the component interface with. - #[clap(long)] - lib_file: Option, - - /// Override the default crate name that is guessed from UDL file path. - #[clap(long = "crate")] - crate_name: Option, - - /// The location of the uniffi.toml file - #[clap(long)] - config: Option, - - /// Treat the input file as a library, extracting any Uniffi definitions from that. - #[clap(long = "library", conflicts_with_all = ["config", "lib_file"])] - library_mode: bool, - - /// A UDL file or library file - source: Utf8PathBuf, -} - -impl SourceArgs { - pub fn library(file: &Utf8PathBuf) -> Self { - Self { - library_mode: true, - source: file.clone(), - ..Default::default() - } - } - - pub fn with_config(self, config: Option) -> Self { - Self { - config, - library_mode: self.library_mode, - source: self.source, - lib_file: self.lib_file, - crate_name: self.crate_name, - } - } -} - -impl BindingsArgs { - pub fn run(&self, manifest_path: Option<&Utf8PathBuf>) -> Result> { - let input = &self.source; - let out = &self.output; - - mk_dir(&out.ts_dir)?; - mk_dir(&out.cpp_dir)?; - - let generator = ReactNativeBindingGenerator::new(out.clone()); - let dummy_dir = Utf8PathBuf::from_str(".")?; - - let try_format_code = !out.no_format; - - let metadata = if let Some(manifest_path) = manifest_path { - CrateMetadata::cargo_metadata(manifest_path)? - } else { - CrateMetadata::cargo_metadata_cwd()? - }; - let config_supplier = CrateConfigSupplier::from(metadata); - - let configs: Vec = if input.library_mode { - uniffi_bindgen::library_mode::generate_bindings( - &input.source, - input.crate_name.clone(), - &generator, - &config_supplier, - input.config.as_deref(), - &dummy_dir, - try_format_code, - )? - .iter() - .map(|s| s.into()) - .collect() - } else { - uniffi_bindgen::generate_external_bindings( - &generator, - input.source.clone(), - input.config.as_deref(), - Some(&dummy_dir), - input.lib_file.clone(), - input.crate_name.as_deref(), - try_format_code, - )?; - Default::default() - }; - - Ok(configs) - } - - pub fn render_entrypoint(&self, path: &Utf8Path, modules: &Vec) -> Result<()> { - let index = EntrypointCpp { modules }; - let string = index.render()?; - fs::write(path, string)?; - Ok(()) - } -} - -#[derive(Template)] -#[template(path = "entrypoint.cpp", escape = "none")] -struct EntrypointCpp<'a> { - modules: &'a Vec, -} diff --git a/crates/ubrn_bindgen/src/bindings/react_native.rs b/crates/ubrn_bindgen/src/bindings/react_native.rs index be916cc8..0acc361d 100644 --- a/crates/ubrn_bindgen/src/bindings/react_native.rs +++ b/crates/ubrn_bindgen/src/bindings/react_native.rs @@ -12,12 +12,14 @@ use uniffi_bindgen::{BindingGenerator, Component, GenerationSettings}; use ubrn_common::{fmt, run_cmd_quietly}; -use crate::bindings::{ - gen_cpp::{self, CppBindings}, - gen_typescript::{self, TsBindings}, - metadata::ModuleMetadata, - type_map::TypeMap, - uniffi_toml::ReactNativeConfig, +use crate::{ + bindings::{ + gen_cpp::{self, CppBindings}, + gen_typescript::{self, TsBindings}, + metadata::ModuleMetadata, + type_map::TypeMap, + uniffi_toml::ReactNativeConfig, + }, OutputArgs, }; diff --git a/crates/ubrn_bindgen/src/cli.rs b/crates/ubrn_bindgen/src/cli.rs new file mode 100644 index 00000000..a52a6975 --- /dev/null +++ b/crates/ubrn_bindgen/src/cli.rs @@ -0,0 +1,161 @@ +/* + * 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 std::str::FromStr; + +use anyhow::Result; +use camino::{Utf8Path, Utf8PathBuf}; +use clap::{command, Args}; +use ubrn_common::{mk_dir, CrateMetadata}; +use uniffi_bindgen::cargo_metadata::CrateConfigSupplier; + +use super::bindings::{metadata::ModuleMetadata, react_native::ReactNativeBindingGenerator}; +use super::switches::SwitchArgs; + +#[derive(Args, Debug)] +pub struct BindingsArgs { + #[command(flatten)] + pub(crate) source: SourceArgs, + #[command(flatten)] + pub(crate) output: OutputArgs, + #[command(flatten)] + pub(crate) switches: SwitchArgs, +} + +impl BindingsArgs { + pub fn new(switches: SwitchArgs, source: SourceArgs, output: OutputArgs) -> Self { + Self { + switches, + source, + output, + } + } + + pub fn ts_dir(&self) -> &Utf8Path { + &self.output.ts_dir + } + + pub fn cpp_dir(&self) -> &Utf8Path { + &self.output.cpp_dir + } +} + +#[derive(Args, Clone, Debug)] +pub struct OutputArgs { + /// By default, bindgen will attempt to format the code with prettier and clang-format. + #[clap(long)] + pub(crate) no_format: bool, + + /// The directory in which to put the generated Typescript. + #[clap(long)] + pub(crate) ts_dir: Utf8PathBuf, + + /// The directory in which to put the generated C++. + #[clap(long, alias = "abi-dir")] + pub(crate) cpp_dir: Utf8PathBuf, +} + +impl OutputArgs { + pub fn new(ts_dir: &Utf8Path, cpp_dir: &Utf8Path, no_format: bool) -> Self { + Self { + ts_dir: ts_dir.to_owned(), + cpp_dir: cpp_dir.to_owned(), + no_format, + } + } +} + +#[derive(Args, Clone, Debug, Default)] +pub struct SourceArgs { + /// The path to a dynamic library to attempt to extract the definitions from + /// and extend the component interface with. + #[clap(long)] + pub(crate) lib_file: Option, + + /// Override the default crate name that is guessed from UDL file path. + #[clap(long = "crate")] + pub(crate) crate_name: Option, + + /// The location of the uniffi.toml file + #[clap(long)] + pub(crate) config: Option, + + /// Treat the input file as a library, extracting any Uniffi definitions from that. + #[clap(long = "library", conflicts_with_all = ["config", "lib_file"])] + pub(crate) library_mode: bool, + + /// A UDL file or library file + pub(crate) source: Utf8PathBuf, +} + +impl SourceArgs { + pub fn library(file: &Utf8PathBuf) -> Self { + Self { + library_mode: true, + source: file.clone(), + ..Default::default() + } + } + + pub fn with_config(self, config: Option) -> Self { + Self { + config, + library_mode: self.library_mode, + source: self.source, + lib_file: self.lib_file, + crate_name: self.crate_name, + } + } +} + +impl BindingsArgs { + pub fn run(&self, manifest_path: Option<&Utf8PathBuf>) -> Result> { + let input = &self.source; + let out = &self.output; + + mk_dir(&out.ts_dir)?; + mk_dir(&out.cpp_dir)?; + + let generator = ReactNativeBindingGenerator::new(out.clone()); + let dummy_dir = Utf8PathBuf::from_str(".")?; + + let try_format_code = !out.no_format; + + let metadata = if let Some(manifest_path) = manifest_path { + CrateMetadata::cargo_metadata(manifest_path)? + } else { + CrateMetadata::cargo_metadata_cwd()? + }; + let config_supplier = CrateConfigSupplier::from(metadata); + + let configs: Vec = if input.library_mode { + uniffi_bindgen::library_mode::generate_bindings( + &input.source, + input.crate_name.clone(), + &generator, + &config_supplier, + input.config.as_deref(), + &dummy_dir, + try_format_code, + )? + .iter() + .map(|s| s.into()) + .collect() + } else { + uniffi_bindgen::generate_external_bindings( + &generator, + input.source.clone(), + input.config.as_deref(), + Some(&dummy_dir), + input.lib_file.clone(), + input.crate_name.as_deref(), + try_format_code, + )?; + Default::default() + }; + + Ok(configs) + } +} diff --git a/crates/ubrn_bindgen/src/lib.rs b/crates/ubrn_bindgen/src/lib.rs index 7acdb246..bfa84715 100644 --- a/crates/ubrn_bindgen/src/lib.rs +++ b/crates/ubrn_bindgen/src/lib.rs @@ -4,6 +4,11 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/ */ mod bindings; +mod cli; +mod switches; -pub use bindings::metadata::ModuleMetadata; -pub use bindings::{AbiFlavor, BindingsArgs, OutputArgs, SourceArgs, SwitchArgs}; +pub use self::{ + bindings::{gen_cpp::generate_entrypoint, metadata::ModuleMetadata}, + cli::{BindingsArgs, OutputArgs, SourceArgs}, + switches::{AbiFlavor, SwitchArgs}, +}; diff --git a/crates/ubrn_bindgen/src/bindings/switches.rs b/crates/ubrn_bindgen/src/switches.rs similarity index 96% rename from crates/ubrn_bindgen/src/bindings/switches.rs rename to crates/ubrn_bindgen/src/switches.rs index dc7992ff..ab1168fc 100644 --- a/crates/ubrn_bindgen/src/bindings/switches.rs +++ b/crates/ubrn_bindgen/src/switches.rs @@ -45,6 +45,6 @@ impl AbiFlavor { matches!(self, Self::Jsi) } pub(crate) fn needs_rust(&self) -> bool { - matches!(self, Self::Jsi) + !matches!(self, Self::Jsi) } } diff --git a/xtask/src/run/generate_bindings.rs b/xtask/src/run/generate_bindings.rs index e9caffcc..5e27b603 100644 --- a/xtask/src/run/generate_bindings.rs +++ b/xtask/src/run/generate_bindings.rs @@ -3,10 +3,12 @@ * 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 std::fs; + use anyhow::Result; -use camino::Utf8PathBuf; +use camino::{Utf8Path, Utf8PathBuf}; use clap::Args; -use ubrn_bindgen::{BindingsArgs, OutputArgs, SourceArgs, SwitchArgs}; +use ubrn_bindgen::{BindingsArgs, ModuleMetadata, OutputArgs, SourceArgs, SwitchArgs}; #[derive(Args, Debug, Clone)] pub(crate) struct GenerateBindingsArg { @@ -47,7 +49,7 @@ impl GenerateBindingsArg { let modules = bindings.run(Some(manifest_path))?; let cpp_dir = bindings.cpp_dir(); let index = cpp_dir.join("Entrypoint.cpp"); - bindings.render_entrypoint(&index, &modules)?; + render_entrypoint(&index, &modules)?; Ok(modules .iter() .map(|m| cpp_dir.join(m.cpp_filename())) @@ -55,3 +57,9 @@ impl GenerateBindingsArg { .collect()) } } + +fn render_entrypoint(path: &Utf8Path, modules: &Vec) -> Result<()> { + let string = ubrn_bindgen::generate_entrypoint(modules)?; + fs::write(path, string)?; + Ok(()) +} From 55a9cea8a188fc294f5037d167315b452a64ef9e Mon Sep 17 00:00:00 2001 From: James Hugman Date: Tue, 3 Dec 2024 23:07:14 +0000 Subject: [PATCH 07/13] Make space for wasm bindings generator --- .../ubrn_bindgen/src/bindings/gen_cpp/mod.rs | 2 + .../ubrn_bindgen/src/bindings/gen_cpp/util.rs | 17 ++++ .../src/bindings/gen_typescript/mod.rs | 47 ++++++++--- .../src/bindings/gen_typescript/util.rs | 18 +++++ crates/ubrn_bindgen/src/bindings/mod.rs | 1 - crates/ubrn_bindgen/src/bindings/type_map.rs | 12 ++- .../ubrn_bindgen/src/bindings/uniffi_toml.rs | 8 -- crates/ubrn_bindgen/src/cli.rs | 16 +++- crates/ubrn_bindgen/src/lib.rs | 1 + .../src/{bindings => }/react_native.rs | 77 ++++++++++--------- 10 files changed, 139 insertions(+), 60 deletions(-) create mode 100644 crates/ubrn_bindgen/src/bindings/gen_cpp/util.rs create mode 100644 crates/ubrn_bindgen/src/bindings/gen_typescript/util.rs rename crates/ubrn_bindgen/src/{bindings => }/react_native.rs (63%) diff --git a/crates/ubrn_bindgen/src/bindings/gen_cpp/mod.rs b/crates/ubrn_bindgen/src/bindings/gen_cpp/mod.rs index 9232a663..5a549524 100644 --- a/crates/ubrn_bindgen/src/bindings/gen_cpp/mod.rs +++ b/crates/ubrn_bindgen/src/bindings/gen_cpp/mod.rs @@ -4,6 +4,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/ */ mod filters; +mod util; use std::borrow::Borrow; @@ -12,6 +13,7 @@ use askama::Template; use uniffi_bindgen::interface::{FfiDefinition, FfiType}; use uniffi_bindgen::ComponentInterface; +pub(crate) use self::util::format_directory; use crate::bindings::{ extensions::{ ComponentInterfaceExt, FfiArgumentExt, FfiCallbackFunctionExt, FfiFieldExt, FfiStructExt, diff --git a/crates/ubrn_bindgen/src/bindings/gen_cpp/util.rs b/crates/ubrn_bindgen/src/bindings/gen_cpp/util.rs new file mode 100644 index 00000000..272d71dd --- /dev/null +++ b/crates/ubrn_bindgen/src/bindings/gen_cpp/util.rs @@ -0,0 +1,17 @@ +/* + * 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 anyhow::Result; +use camino::Utf8Path; +use ubrn_common::{fmt, run_cmd_quietly}; + +pub(crate) fn format_directory(out_dir: &Utf8Path) -> Result<()> { + if let Some(mut clang_format) = fmt::clang_format(out_dir, false)? { + run_cmd_quietly(&mut clang_format)? + } else { + eprintln!("Skipping formatting C++. Is clang-format installed?"); + } + Ok(()) +} diff --git a/crates/ubrn_bindgen/src/bindings/gen_typescript/mod.rs b/crates/ubrn_bindgen/src/bindings/gen_typescript/mod.rs index 29aa896c..2ad4481f 100644 --- a/crates/ubrn_bindgen/src/bindings/gen_typescript/mod.rs +++ b/crates/ubrn_bindgen/src/bindings/gen_typescript/mod.rs @@ -14,6 +14,7 @@ mod miscellany; mod object; mod primitives; mod record; +mod util; use anyhow::{Context, Result}; use askama::Template; @@ -26,11 +27,15 @@ use std::collections::{BTreeMap, BTreeSet, HashSet}; use uniffi_bindgen::interface::{AsType, Callable, FfiDefinition, FfiType, Type, UniffiTrait}; use uniffi_bindgen::ComponentInterface; -use crate::bindings::extensions::{ - ComponentInterfaceExt, FfiCallbackFunctionExt, FfiFunctionExt, FfiStructExt, ObjectExt, +pub(crate) use self::util::format_directory; +use crate::bindings::{ + extensions::{ + ComponentInterfaceExt, FfiCallbackFunctionExt, FfiFunctionExt, FfiStructExt, ObjectExt, + }, + metadata::ModuleMetadata, + type_map::TypeMap, }; -use crate::bindings::metadata::ModuleMetadata; -use crate::bindings::type_map::TypeMap; +use crate::SwitchArgs; #[derive(Default)] pub(crate) struct TsBindings { @@ -44,12 +49,13 @@ pub(crate) fn generate_bindings( ci: &ComponentInterface, config: &Config, module: &ModuleMetadata, + switches: &SwitchArgs, type_map: &TypeMap, ) -> Result { - let codegen = CodegenWrapper::new(ci, config, module) + let codegen = CodegenWrapper::new(ci, config, module, switches) .render() .context("generating codegen bindings failed")?; - let frontend = FrontendWrapper::new(ci, config, module, type_map) + let frontend = FrontendWrapper::new(ci, config, module, switches, type_map) .render() .context("generating frontend javascript failed")?; @@ -63,11 +69,23 @@ struct CodegenWrapper<'a> { #[allow(unused)] config: &'a Config, module: &'a ModuleMetadata, + #[allow(unused)] + switches: &'a SwitchArgs, } impl<'a> CodegenWrapper<'a> { - fn new(ci: &'a ComponentInterface, config: &'a Config, module: &'a ModuleMetadata) -> Self { - Self { ci, config, module } + fn new( + ci: &'a ComponentInterface, + config: &'a Config, + module: &'a ModuleMetadata, + switches: &'a SwitchArgs, + ) -> Self { + Self { + ci, + config, + module, + switches, + } } } @@ -75,9 +93,11 @@ impl<'a> CodegenWrapper<'a> { #[template(syntax = "ts", escape = "none", path = "wrapper.ts")] struct FrontendWrapper<'a> { ci: &'a ComponentInterface, - module: &'a ModuleMetadata, #[allow(unused)] config: &'a Config, + module: &'a ModuleMetadata, + #[allow(unused)] + switches: &'a SwitchArgs, type_helper_code: String, type_imports: BTreeMap>, exported_converters: BTreeSet, @@ -89,9 +109,10 @@ impl<'a> FrontendWrapper<'a> { ci: &'a ComponentInterface, config: &'a Config, module: &'a ModuleMetadata, + switches: &'a SwitchArgs, type_map: &'a TypeMap, ) -> Self { - let type_renderer = TypeRenderer::new(ci, config, module, type_map); + let type_renderer = TypeRenderer::new(ci, config, module, switches, type_map); let type_helper_code = type_renderer.render().unwrap(); let type_imports = type_renderer.imports.into_inner(); let exported_converters = type_renderer.exported_converters.into_inner(); @@ -100,6 +121,7 @@ impl<'a> FrontendWrapper<'a> { module, config, ci, + switches, type_helper_code, type_imports, exported_converters, @@ -120,6 +142,9 @@ pub struct TypeRenderer<'a> { config: &'a Config, #[allow(unused)] module: &'a ModuleMetadata, + #[allow(unused)] + switches: &'a SwitchArgs, + // Track imports added with the `add_import()` macro imports: RefCell>>, @@ -137,12 +162,14 @@ impl<'a> TypeRenderer<'a> { ci: &'a ComponentInterface, config: &'a Config, module: &'a ModuleMetadata, + switches: &'a SwitchArgs, type_map: &'a TypeMap, ) -> Self { Self { ci, config, module, + switches, imports: RefCell::new(Default::default()), exported_converters: RefCell::new(Default::default()), imported_converters: RefCell::new(Default::default()), diff --git a/crates/ubrn_bindgen/src/bindings/gen_typescript/util.rs b/crates/ubrn_bindgen/src/bindings/gen_typescript/util.rs new file mode 100644 index 00000000..c7a16c38 --- /dev/null +++ b/crates/ubrn_bindgen/src/bindings/gen_typescript/util.rs @@ -0,0 +1,18 @@ +/* + * 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 anyhow::Result; +use camino::Utf8Path; + +use ubrn_common::{fmt, run_cmd_quietly}; + +pub(crate) fn format_directory(out_dir: &Utf8Path) -> Result<()> { + if let Some(mut prettier) = fmt::prettier(out_dir, false)? { + run_cmd_quietly(&mut prettier)? + } else { + eprintln!("No prettier found. Install with `yarn add --dev prettier`"); + } + Ok(()) +} diff --git a/crates/ubrn_bindgen/src/bindings/mod.rs b/crates/ubrn_bindgen/src/bindings/mod.rs index 9bc21d54..ac6de44b 100644 --- a/crates/ubrn_bindgen/src/bindings/mod.rs +++ b/crates/ubrn_bindgen/src/bindings/mod.rs @@ -8,6 +8,5 @@ pub(crate) mod extensions; pub(crate) mod gen_cpp; pub(crate) mod gen_typescript; pub(crate) mod metadata; -pub(crate) mod react_native; pub(crate) mod type_map; pub(crate) mod uniffi_toml; diff --git a/crates/ubrn_bindgen/src/bindings/type_map.rs b/crates/ubrn_bindgen/src/bindings/type_map.rs index 0e8d9f49..40d1b8fc 100644 --- a/crates/ubrn_bindgen/src/bindings/type_map.rs +++ b/crates/ubrn_bindgen/src/bindings/type_map.rs @@ -5,7 +5,7 @@ */ use std::collections::HashMap; -use uniffi_bindgen::{interface::Type, ComponentInterface}; +use uniffi_bindgen::{interface::Type, Component, ComponentInterface}; use uniffi_meta::AsType; #[derive(Default, Debug)] @@ -69,3 +69,13 @@ impl TypeMap { } } } + +impl From<&[Component]> for TypeMap { + fn from(components: &[Component]) -> Self { + let mut map = Self::default(); + for component in components { + map.insert_ci(&component.ci); + } + map + } +} diff --git a/crates/ubrn_bindgen/src/bindings/uniffi_toml.rs b/crates/ubrn_bindgen/src/bindings/uniffi_toml.rs index 0983596b..2d4e2cdd 100644 --- a/crates/ubrn_bindgen/src/bindings/uniffi_toml.rs +++ b/crates/ubrn_bindgen/src/bindings/uniffi_toml.rs @@ -9,14 +9,6 @@ use std::collections::HashMap; use serde::{Deserialize, Serialize}; use uniffi_bindgen::backend::TemplateExpression; -#[derive(Debug, Clone, Default, Serialize, Deserialize)] -pub(crate) struct ReactNativeConfig { - #[serde(default, alias = "javascript", alias = "js", alias = "ts")] - pub(crate) typescript: TsConfig, - #[serde(default)] - pub(crate) cpp: CppConfig, -} - #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(deny_unknown_fields, rename_all = "camelCase")] pub(crate) struct TsConfig { diff --git a/crates/ubrn_bindgen/src/cli.rs b/crates/ubrn_bindgen/src/cli.rs index a52a6975..801c10b2 100644 --- a/crates/ubrn_bindgen/src/cli.rs +++ b/crates/ubrn_bindgen/src/cli.rs @@ -11,8 +11,10 @@ use clap::{command, Args}; use ubrn_common::{mk_dir, CrateMetadata}; use uniffi_bindgen::cargo_metadata::CrateConfigSupplier; -use super::bindings::{metadata::ModuleMetadata, react_native::ReactNativeBindingGenerator}; -use super::switches::SwitchArgs; +use super::{ + bindings::metadata::ModuleMetadata, react_native::ReactNativeBindingGenerator, + switches::SwitchArgs, +}; #[derive(Args, Debug)] pub struct BindingsArgs { @@ -117,8 +119,14 @@ impl BindingsArgs { mk_dir(&out.ts_dir)?; mk_dir(&out.cpp_dir)?; - - let generator = ReactNativeBindingGenerator::new(out.clone()); + let ts_dir = out.ts_dir.canonicalize_utf8()?; + let abi_dir = out.cpp_dir.canonicalize_utf8()?; + + let generator = ReactNativeBindingGenerator::new( + ts_dir.clone(), + abi_dir.clone(), + self.switches.clone(), + ); let dummy_dir = Utf8PathBuf::from_str(".")?; let try_format_code = !out.no_format; diff --git a/crates/ubrn_bindgen/src/lib.rs b/crates/ubrn_bindgen/src/lib.rs index bfa84715..8dcd06e7 100644 --- a/crates/ubrn_bindgen/src/lib.rs +++ b/crates/ubrn_bindgen/src/lib.rs @@ -5,6 +5,7 @@ */ mod bindings; mod cli; +mod react_native; mod switches; pub use self::{ diff --git a/crates/ubrn_bindgen/src/bindings/react_native.rs b/crates/ubrn_bindgen/src/react_native.rs similarity index 63% rename from crates/ubrn_bindgen/src/bindings/react_native.rs rename to crates/ubrn_bindgen/src/react_native.rs index 0acc361d..d93c6c5d 100644 --- a/crates/ubrn_bindgen/src/bindings/react_native.rs +++ b/crates/ubrn_bindgen/src/react_native.rs @@ -7,34 +7,39 @@ use std::fs; use anyhow::Result; -use camino::Utf8Path; +use camino::Utf8PathBuf; +use serde::{Deserialize, Serialize}; use uniffi_bindgen::{BindingGenerator, Component, GenerationSettings}; -use ubrn_common::{fmt, run_cmd_quietly}; - use crate::{ bindings::{ gen_cpp::{self, CppBindings}, gen_typescript::{self, TsBindings}, metadata::ModuleMetadata, type_map::TypeMap, - uniffi_toml::ReactNativeConfig, + uniffi_toml::{CppConfig, TsConfig}, }, - OutputArgs, + switches::SwitchArgs, }; pub(crate) struct ReactNativeBindingGenerator { - output: OutputArgs, + switches: SwitchArgs, + ts_dir: Utf8PathBuf, + cpp_dir: Utf8PathBuf, } impl ReactNativeBindingGenerator { - pub(crate) fn new(output: OutputArgs) -> Self { - Self { output } + pub(crate) fn new(ts_dir: Utf8PathBuf, cpp_dir: Utf8PathBuf, switches: SwitchArgs) -> Self { + Self { + ts_dir, + cpp_dir, + switches, + } } pub(crate) fn format_code(&self) -> Result<()> { - format_ts(&self.output.ts_dir.canonicalize_utf8()?)?; - format_cpp(&self.output.cpp_dir.canonicalize_utf8()?)?; + gen_typescript::format_directory(&self.ts_dir)?; + gen_cpp::format_directory(&self.cpp_dir)?; Ok(()) } } @@ -63,24 +68,34 @@ impl BindingGenerator for ReactNativeBindingGenerator { settings: &GenerationSettings, components: &[Component], ) -> Result<()> { - let mut type_map = TypeMap::default(); - for component in components { - type_map.insert_ci(&component.ci); - } + let type_map = TypeMap::from(components); + + let out_dir = &self.ts_dir; for component in components { let ci = &component.ci; - let module: ModuleMetadata = component.into(); + let module = ModuleMetadata::from(component); let config = &component.config; - let TsBindings { codegen, frontend } = - gen_typescript::generate_bindings(ci, &config.typescript, &module, &type_map)?; + let TsBindings { codegen, frontend } = gen_typescript::generate_bindings( + ci, + &config.typescript, + &module, + &self.switches, + &type_map, + )?; - let out_dir = &self.output.ts_dir.canonicalize_utf8()?; let codegen_path = out_dir.join(module.ts_ffi_filename()); - let frontend_path = out_dir.join(module.ts_filename()); fs::write(codegen_path, codegen)?; + + let frontend_path = out_dir.join(module.ts_filename()); fs::write(frontend_path, frontend)?; + } + + let out_dir = &self.cpp_dir; + for component in components { + let ci = &component.ci; + let module: ModuleMetadata = component.into(); + let config = &component.config; - let out_dir = &self.output.cpp_dir.canonicalize_utf8()?; let CppBindings { hpp, cpp } = gen_cpp::generate_bindings(ci, &config.cpp, &module)?; let cpp_path = out_dir.join(module.cpp_filename()); let hpp_path = out_dir.join(module.hpp_filename()); @@ -94,20 +109,10 @@ impl BindingGenerator for ReactNativeBindingGenerator { } } -fn format_ts(out_dir: &Utf8Path) -> Result<()> { - if let Some(mut prettier) = fmt::prettier(out_dir, false)? { - run_cmd_quietly(&mut prettier)? - } else { - eprintln!("No prettier found. Install with `yarn add --dev prettier`"); - } - Ok(()) -} - -fn format_cpp(out_dir: &Utf8Path) -> Result<()> { - if let Some(mut clang_format) = fmt::clang_format(out_dir, false)? { - run_cmd_quietly(&mut clang_format)? - } else { - eprintln!("Skipping formatting C++. Is clang-format installed?"); - } - Ok(()) +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +pub(crate) struct ReactNativeConfig { + #[serde(default, alias = "javascript", alias = "js", alias = "ts")] + pub(crate) typescript: TsConfig, + #[serde(default)] + pub(crate) cpp: CppConfig, } From c8e603d6142617bd0288469fb4587ceef44b4111 Mon Sep 17 00:00:00 2001 From: James Hugman Date: Wed, 4 Dec 2024 00:54:58 +0000 Subject: [PATCH 08/13] Tease out the config into bindings specific files --- .../src/bindings/gen_cpp/config.rs | 11 +++++++ .../ubrn_bindgen/src/bindings/gen_cpp/mod.rs | 5 ++- .../config.rs} | 10 ++---- .../src/bindings/gen_typescript/mod.rs | 33 +++++++++++-------- crates/ubrn_bindgen/src/bindings/mod.rs | 1 - crates/ubrn_bindgen/src/react_native.rs | 5 ++- 6 files changed, 37 insertions(+), 28 deletions(-) create mode 100644 crates/ubrn_bindgen/src/bindings/gen_cpp/config.rs rename crates/ubrn_bindgen/src/bindings/{uniffi_toml.rs => gen_typescript/config.rs} (89%) diff --git a/crates/ubrn_bindgen/src/bindings/gen_cpp/config.rs b/crates/ubrn_bindgen/src/bindings/gen_cpp/config.rs new file mode 100644 index 00000000..ed78a57f --- /dev/null +++ b/crates/ubrn_bindgen/src/bindings/gen_cpp/config.rs @@ -0,0 +1,11 @@ +/* + * 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 serde::{Deserialize, Serialize}; + +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct CppConfig {} diff --git a/crates/ubrn_bindgen/src/bindings/gen_cpp/mod.rs b/crates/ubrn_bindgen/src/bindings/gen_cpp/mod.rs index 5a549524..12c58d11 100644 --- a/crates/ubrn_bindgen/src/bindings/gen_cpp/mod.rs +++ b/crates/ubrn_bindgen/src/bindings/gen_cpp/mod.rs @@ -3,6 +3,7 @@ * 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/ */ +mod config; mod filters; mod util; @@ -13,7 +14,7 @@ use askama::Template; use uniffi_bindgen::interface::{FfiDefinition, FfiType}; use uniffi_bindgen::ComponentInterface; -pub(crate) use self::util::format_directory; +pub(crate) use self::{config::CppConfig as Config, util::format_directory}; use crate::bindings::{ extensions::{ ComponentInterfaceExt, FfiArgumentExt, FfiCallbackFunctionExt, FfiFieldExt, FfiStructExt, @@ -22,8 +23,6 @@ use crate::bindings::{ metadata::ModuleMetadata, }; -type Config = crate::bindings::uniffi_toml::CppConfig; - #[derive(Debug, Default)] pub(crate) struct CppBindings { pub(crate) hpp: String, diff --git a/crates/ubrn_bindgen/src/bindings/uniffi_toml.rs b/crates/ubrn_bindgen/src/bindings/gen_typescript/config.rs similarity index 89% rename from crates/ubrn_bindgen/src/bindings/uniffi_toml.rs rename to crates/ubrn_bindgen/src/bindings/gen_typescript/config.rs index 2d4e2cdd..9995f5ac 100644 --- a/crates/ubrn_bindgen/src/bindings/uniffi_toml.rs +++ b/crates/ubrn_bindgen/src/bindings/gen_typescript/config.rs @@ -3,10 +3,10 @@ * 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 std::collections::HashMap; use serde::{Deserialize, Serialize}; + use uniffi_bindgen::backend::TemplateExpression; #[derive(Debug, Clone, Default, Serialize, Deserialize)] @@ -30,10 +30,10 @@ pub(crate) enum LogLevel { } impl LogLevel { - fn is_verbose(&self) -> bool { + pub(crate) fn is_verbose(&self) -> bool { matches!(self, Self::Verbose) } - fn is_debug(&self) -> bool { + pub(crate) fn is_debug(&self) -> bool { matches!(self, Self::Debug | Self::Verbose) } } @@ -58,7 +58,3 @@ pub(crate) struct CustomTypeConfig { #[serde(alias = "lower")] pub(crate) from_custom: TemplateExpression, } - -#[derive(Debug, Clone, Default, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub(crate) struct CppConfig {} diff --git a/crates/ubrn_bindgen/src/bindings/gen_typescript/mod.rs b/crates/ubrn_bindgen/src/bindings/gen_typescript/mod.rs index 2ad4481f..3f90bb2e 100644 --- a/crates/ubrn_bindgen/src/bindings/gen_typescript/mod.rs +++ b/crates/ubrn_bindgen/src/bindings/gen_typescript/mod.rs @@ -8,6 +8,7 @@ mod oracle; mod callback_interface; mod compounds; +mod config; mod custom; mod enum_; mod miscellany; @@ -16,26 +17,32 @@ mod primitives; mod record; mod util; -use anyhow::{Context, Result}; -use askama::Template; -use filters::{ffi_converter_name, type_name}; -use heck::ToUpperCamelCase; -use oracle::CodeOracle; use std::borrow::Borrow; use std::cell::RefCell; use std::collections::{BTreeMap, BTreeSet, HashSet}; + +use anyhow::{Context, Result}; +use askama::Template; +use heck::ToUpperCamelCase; use uniffi_bindgen::interface::{AsType, Callable, FfiDefinition, FfiType, Type, UniffiTrait}; use uniffi_bindgen::ComponentInterface; -pub(crate) use self::util::format_directory; -use crate::bindings::{ - extensions::{ - ComponentInterfaceExt, FfiCallbackFunctionExt, FfiFunctionExt, FfiStructExt, ObjectExt, +pub(crate) use self::{config::TsConfig as Config, util::format_directory}; +use self::{ + filters::{ffi_converter_name, type_name}, + oracle::CodeOracle, +}; + +use crate::{ + bindings::{ + extensions::{ + ComponentInterfaceExt, FfiCallbackFunctionExt, FfiFunctionExt, FfiStructExt, ObjectExt, + }, + metadata::ModuleMetadata, + type_map::TypeMap, }, - metadata::ModuleMetadata, - type_map::TypeMap, + SwitchArgs, }; -use crate::SwitchArgs; #[derive(Default)] pub(crate) struct TsBindings { @@ -43,8 +50,6 @@ pub(crate) struct TsBindings { pub(crate) frontend: String, } -type Config = crate::bindings::uniffi_toml::TsConfig; - pub(crate) fn generate_bindings( ci: &ComponentInterface, config: &Config, diff --git a/crates/ubrn_bindgen/src/bindings/mod.rs b/crates/ubrn_bindgen/src/bindings/mod.rs index ac6de44b..106da04d 100644 --- a/crates/ubrn_bindgen/src/bindings/mod.rs +++ b/crates/ubrn_bindgen/src/bindings/mod.rs @@ -9,4 +9,3 @@ pub(crate) mod gen_cpp; pub(crate) mod gen_typescript; pub(crate) mod metadata; pub(crate) mod type_map; -pub(crate) mod uniffi_toml; diff --git a/crates/ubrn_bindgen/src/react_native.rs b/crates/ubrn_bindgen/src/react_native.rs index d93c6c5d..45967afd 100644 --- a/crates/ubrn_bindgen/src/react_native.rs +++ b/crates/ubrn_bindgen/src/react_native.rs @@ -17,7 +17,6 @@ use crate::{ gen_typescript::{self, TsBindings}, metadata::ModuleMetadata, type_map::TypeMap, - uniffi_toml::{CppConfig, TsConfig}, }, switches::SwitchArgs, }; @@ -112,7 +111,7 @@ impl BindingGenerator for ReactNativeBindingGenerator { #[derive(Debug, Clone, Default, Serialize, Deserialize)] pub(crate) struct ReactNativeConfig { #[serde(default, alias = "javascript", alias = "js", alias = "ts")] - pub(crate) typescript: TsConfig, + pub(crate) typescript: gen_typescript::Config, #[serde(default)] - pub(crate) cpp: CppConfig, + pub(crate) cpp: gen_cpp::Config, } From e6fbf413a6ccade18c9122f5557e600457fadf70 Mon Sep 17 00:00:00 2001 From: James Hugman Date: Wed, 4 Dec 2024 13:27:29 +0000 Subject: [PATCH 09/13] Separate different files out into different generate methods --- .../ubrn_bindgen/src/bindings/gen_cpp/mod.rs | 18 +-- .../src/bindings/gen_typescript/mod.rs | 86 ++++++--------- crates/ubrn_bindgen/src/react_native.rs | 103 ++++++++++-------- 3 files changed, 100 insertions(+), 107 deletions(-) diff --git a/crates/ubrn_bindgen/src/bindings/gen_cpp/mod.rs b/crates/ubrn_bindgen/src/bindings/gen_cpp/mod.rs index 12c58d11..b1a0a611 100644 --- a/crates/ubrn_bindgen/src/bindings/gen_cpp/mod.rs +++ b/crates/ubrn_bindgen/src/bindings/gen_cpp/mod.rs @@ -23,20 +23,20 @@ use crate::bindings::{ metadata::ModuleMetadata, }; -#[derive(Debug, Default)] -pub(crate) struct CppBindings { - pub(crate) hpp: String, - pub(crate) cpp: String, +pub(crate) fn generate_hpp( + ci: &ComponentInterface, + config: &Config, + module: &ModuleMetadata, +) -> Result { + Ok(HppWrapper::new(ci, config, module).render()?) } -pub(crate) fn generate_bindings( +pub(crate) fn generate_cpp( ci: &ComponentInterface, config: &Config, module: &ModuleMetadata, -) -> Result { - let hpp = HppWrapper::new(ci, config, module).render()?; - let cpp = CppWrapper::new(ci, config, module).render()?; - Ok(CppBindings { hpp, cpp }) +) -> Result { + Ok(CppWrapper::new(ci, config, module).render()?) } #[derive(Template)] diff --git a/crates/ubrn_bindgen/src/bindings/gen_typescript/mod.rs b/crates/ubrn_bindgen/src/bindings/gen_typescript/mod.rs index 3f90bb2e..8a396765 100644 --- a/crates/ubrn_bindgen/src/bindings/gen_typescript/mod.rs +++ b/crates/ubrn_bindgen/src/bindings/gen_typescript/mod.rs @@ -32,7 +32,6 @@ use self::{ filters::{ffi_converter_name, type_name}, oracle::CodeOracle, }; - use crate::{ bindings::{ extensions::{ @@ -41,62 +40,47 @@ use crate::{ metadata::ModuleMetadata, type_map::TypeMap, }, - SwitchArgs, + switches::SwitchArgs, }; -#[derive(Default)] -pub(crate) struct TsBindings { - pub(crate) codegen: String, - pub(crate) frontend: String, -} - -pub(crate) fn generate_bindings( +pub(crate) fn generate_api_code( ci: &ComponentInterface, config: &Config, module: &ModuleMetadata, switches: &SwitchArgs, type_map: &TypeMap, -) -> Result { - let codegen = CodegenWrapper::new(ci, config, module, switches) +) -> Result { + let types = TypeRenderer::new(ci, config, module, switches, type_map); + TsApiWrapper::try_from(types)? .render() - .context("generating codegen bindings failed")?; - let frontend = FrontendWrapper::new(ci, config, module, switches, type_map) - .render() - .context("generating frontend javascript failed")?; + .context("generating frontend typescript failed") +} - Ok(TsBindings { codegen, frontend }) +pub(crate) fn generate_lowlevel_code( + ci: &ComponentInterface, + module: &ModuleMetadata, +) -> Result { + LowlevelTsWrapper::new(ci, module) + .render() + .context("generating lowlevel typescipt failed") } #[derive(Template)] #[template(syntax = "ts", escape = "none", path = "wrapper-ffi.ts")] -struct CodegenWrapper<'a> { +struct LowlevelTsWrapper<'a> { ci: &'a ComponentInterface, - #[allow(unused)] - config: &'a Config, module: &'a ModuleMetadata, - #[allow(unused)] - switches: &'a SwitchArgs, } -impl<'a> CodegenWrapper<'a> { - fn new( - ci: &'a ComponentInterface, - config: &'a Config, - module: &'a ModuleMetadata, - switches: &'a SwitchArgs, - ) -> Self { - Self { - ci, - config, - module, - switches, - } +impl<'a> LowlevelTsWrapper<'a> { + fn new(ci: &'a ComponentInterface, module: &'a ModuleMetadata) -> Self { + Self { ci, module } } } #[derive(Template)] #[template(syntax = "ts", escape = "none", path = "wrapper.ts")] -struct FrontendWrapper<'a> { +struct TsApiWrapper<'a> { ci: &'a ComponentInterface, #[allow(unused)] config: &'a Config, @@ -109,20 +93,19 @@ struct FrontendWrapper<'a> { imported_converters: BTreeMap<(String, String), BTreeSet>, } -impl<'a> FrontendWrapper<'a> { - pub fn new( - ci: &'a ComponentInterface, - config: &'a Config, - module: &'a ModuleMetadata, - switches: &'a SwitchArgs, - type_map: &'a TypeMap, - ) -> Self { - let type_renderer = TypeRenderer::new(ci, config, module, switches, type_map); - let type_helper_code = type_renderer.render().unwrap(); - let type_imports = type_renderer.imports.into_inner(); - let exported_converters = type_renderer.exported_converters.into_inner(); - let imported_converters = type_renderer.imported_converters.into_inner(); - Self { +impl<'a> TryFrom> for TsApiWrapper<'a> { + type Error = anyhow::Error; + + fn try_from(types: TypeRenderer<'a>) -> Result { + let type_helper_code = types.render()?; + let type_imports = types.imports.into_inner(); + let exported_converters = types.exported_converters.into_inner(); + let imported_converters = types.imported_converters.into_inner(); + let module = types.module; + let config = types.config; + let ci = types.ci; + let switches = types.switches; + Ok(Self { module, config, ci, @@ -131,7 +114,7 @@ impl<'a> FrontendWrapper<'a> { type_imports, exported_converters, imported_converters, - } + }) } } @@ -141,9 +124,8 @@ impl<'a> FrontendWrapper<'a> { /// process. Make sure to only call `render()` once. #[derive(Template)] #[template(syntax = "ts", escape = "none", path = "Types.ts")] -pub struct TypeRenderer<'a> { +struct TypeRenderer<'a> { ci: &'a ComponentInterface, - #[allow(unused)] config: &'a Config, #[allow(unused)] module: &'a ModuleMetadata, diff --git a/crates/ubrn_bindgen/src/react_native.rs b/crates/ubrn_bindgen/src/react_native.rs index 45967afd..8a121579 100644 --- a/crates/ubrn_bindgen/src/react_native.rs +++ b/crates/ubrn_bindgen/src/react_native.rs @@ -12,12 +12,7 @@ use serde::{Deserialize, Serialize}; use uniffi_bindgen::{BindingGenerator, Component, GenerationSettings}; use crate::{ - bindings::{ - gen_cpp::{self, CppBindings}, - gen_typescript::{self, TsBindings}, - metadata::ModuleMetadata, - type_map::TypeMap, - }, + bindings::{gen_cpp, gen_typescript, metadata::ModuleMetadata, type_map::TypeMap}, switches::SwitchArgs, }; @@ -36,9 +31,55 @@ impl ReactNativeBindingGenerator { } } - pub(crate) fn format_code(&self) -> Result<()> { - gen_typescript::format_directory(&self.ts_dir)?; - gen_cpp::format_directory(&self.cpp_dir)?; + fn generate_ts( + components: &[Component], + switches: &SwitchArgs, + out_dir: &Utf8PathBuf, + try_format_code: bool, + ) -> std::result::Result<(), anyhow::Error> { + let type_map = TypeMap::from(components); + for component in components { + let module = ModuleMetadata::from(component); + + let api_ts = gen_typescript::generate_api_code( + &component.ci, + &component.config.typescript, + &module, + switches, + &type_map, + )?; + let api_ts_path = out_dir.join(module.ts_filename()); + fs::write(api_ts_path, api_ts)?; + + let lowlevel_ts = gen_typescript::generate_lowlevel_code(&component.ci, &module)?; + let lowlevel_ts_path = out_dir.join(module.ts_ffi_filename()); + fs::write(lowlevel_ts_path, lowlevel_ts)?; + } + if try_format_code { + gen_typescript::format_directory(out_dir)?; + } + Ok(()) + } + + fn generate_cpp( + components: &[Component], + out_dir: &Utf8PathBuf, + try_format_code: bool, + ) -> Result<(), anyhow::Error> { + for component in components { + let module = ModuleMetadata::from(component); + + let cpp = gen_cpp::generate_cpp(&component.ci, &component.config.cpp, &module)?; + let cpp_path = out_dir.join(module.cpp_filename()); + fs::write(cpp_path, cpp)?; + + let hpp = gen_cpp::generate_hpp(&component.ci, &component.config.cpp, &module)?; + let hpp_path = out_dir.join(module.hpp_filename()); + fs::write(hpp_path, hpp)?; + } + if try_format_code { + gen_cpp::format_directory(out_dir)?; + } Ok(()) } } @@ -67,43 +108,13 @@ impl BindingGenerator for ReactNativeBindingGenerator { settings: &GenerationSettings, components: &[Component], ) -> Result<()> { - let type_map = TypeMap::from(components); - - let out_dir = &self.ts_dir; - for component in components { - let ci = &component.ci; - let module = ModuleMetadata::from(component); - let config = &component.config; - let TsBindings { codegen, frontend } = gen_typescript::generate_bindings( - ci, - &config.typescript, - &module, - &self.switches, - &type_map, - )?; - - let codegen_path = out_dir.join(module.ts_ffi_filename()); - fs::write(codegen_path, codegen)?; - - let frontend_path = out_dir.join(module.ts_filename()); - fs::write(frontend_path, frontend)?; - } - - let out_dir = &self.cpp_dir; - for component in components { - let ci = &component.ci; - let module: ModuleMetadata = component.into(); - let config = &component.config; - - let CppBindings { hpp, cpp } = gen_cpp::generate_bindings(ci, &config.cpp, &module)?; - let cpp_path = out_dir.join(module.cpp_filename()); - let hpp_path = out_dir.join(module.hpp_filename()); - fs::write(cpp_path, cpp)?; - fs::write(hpp_path, hpp)?; - } - if settings.try_format_code { - self.format_code()?; - } + Self::generate_ts( + components, + &self.switches, + &self.ts_dir, + settings.try_format_code, + )?; + Self::generate_cpp(components, &self.cpp_dir, settings.try_format_code)?; Ok(()) } } From 845dbc84330f4f136b4fbbc11f1bce48dcf28ab2 Mon Sep 17 00:00:00 2001 From: James Hugman Date: Wed, 4 Dec 2024 15:16:34 +0000 Subject: [PATCH 10/13] Add wasm feature --- crates/ubrn_bindgen/Cargo.toml | 3 +++ crates/ubrn_bindgen/src/cli.rs | 20 ++++++++++++++++---- crates/ubrn_cli/Cargo.toml | 4 ++++ crates/ubrn_cli/src/generate.rs | 1 + xtask/Cargo.toml | 2 +- 5 files changed, 25 insertions(+), 5 deletions(-) diff --git a/crates/ubrn_bindgen/Cargo.toml b/crates/ubrn_bindgen/Cargo.toml index 29eb1720..52666e0f 100644 --- a/crates/ubrn_bindgen/Cargo.toml +++ b/crates/ubrn_bindgen/Cargo.toml @@ -4,6 +4,9 @@ version = "0.1.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[features] +default = [] +wasm = [] [dependencies] anyhow = { workspace = true } diff --git a/crates/ubrn_bindgen/src/cli.rs b/crates/ubrn_bindgen/src/cli.rs index 801c10b2..ae1ed52b 100644 --- a/crates/ubrn_bindgen/src/cli.rs +++ b/crates/ubrn_bindgen/src/cli.rs @@ -22,14 +22,16 @@ pub struct BindingsArgs { pub(crate) source: SourceArgs, #[command(flatten)] pub(crate) output: OutputArgs, + #[cfg(feature = "wasm")] #[command(flatten)] - pub(crate) switches: SwitchArgs, + switches: SwitchArgs, } impl BindingsArgs { - pub fn new(switches: SwitchArgs, source: SourceArgs, output: OutputArgs) -> Self { + pub fn new(_switches: SwitchArgs, source: SourceArgs, output: OutputArgs) -> Self { Self { - switches, + #[cfg(feature = "wasm")] + switches: _switches, source, output, } @@ -42,6 +44,16 @@ impl BindingsArgs { pub fn cpp_dir(&self) -> &Utf8Path { &self.output.cpp_dir } + + #[cfg(not(feature = "wasm"))] + pub fn switches(&self) -> SwitchArgs { + Default::default() + } + + #[cfg(feature = "wasm")] + pub fn switches(&self) -> SwitchArgs { + self.switches.clone() + } } #[derive(Args, Clone, Debug)] @@ -125,7 +137,7 @@ impl BindingsArgs { let generator = ReactNativeBindingGenerator::new( ts_dir.clone(), abi_dir.clone(), - self.switches.clone(), + self.switches(), ); let dummy_dir = Utf8PathBuf::from_str(".")?; diff --git a/crates/ubrn_cli/Cargo.toml b/crates/ubrn_cli/Cargo.toml index eda384ae..65a01967 100644 --- a/crates/ubrn_cli/Cargo.toml +++ b/crates/ubrn_cli/Cargo.toml @@ -8,6 +8,10 @@ name = "uniffi-bindgen-react-native" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[features] +default = [] +wasm = [] + [dependencies] anyhow = { workspace = true } askama = { workspace = true } diff --git a/crates/ubrn_cli/src/generate.rs b/crates/ubrn_cli/src/generate.rs index 68c53740..b56f8849 100644 --- a/crates/ubrn_cli/src/generate.rs +++ b/crates/ubrn_cli/src/generate.rs @@ -62,6 +62,7 @@ pub(crate) struct GenerateAllArgs { #[clap(long)] config: Utf8PathBuf, + #[cfg(feature = "wasm")] #[command(flatten)] switches: SwitchArgs, diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml index c766d399..218add58 100644 --- a/xtask/Cargo.toml +++ b/xtask/Cargo.toml @@ -13,5 +13,5 @@ anyhow = { workspace = true } camino = { workspace = true } clap = { workspace = true } pathdiff = { workspace = true } -ubrn_bindgen = { path = "../crates/ubrn_bindgen" } +ubrn_bindgen = { path = "../crates/ubrn_bindgen", features = ["wasm"] } ubrn_common = { path = "../crates/ubrn_common" } From 91f4b0e64f5e97a5ef39b87f0bc22c007e91ed46 Mon Sep 17 00:00:00 2001 From: James Hugman Date: Wed, 4 Dec 2024 15:18:31 +0000 Subject: [PATCH 11/13] Make integration tests only run when the flavor is supported --- scripts/run-tests.sh | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/scripts/run-tests.sh b/scripts/run-tests.sh index d3dd7f27..e1fa671d 100755 --- a/scripts/run-tests.sh +++ b/scripts/run-tests.sh @@ -26,6 +26,23 @@ while (( "$#" )); do esac done +supports_flavor() { + local fixture="$1" + local flavor="$2" + + if [[ "$flavor" == "jsi" ]]; then + return 0 + fi + + local flavor_file="${fixture}/tests/bindings/.supported-flavors.txt" + if [[ -f "$flavor_file" ]]; then + if grep -q "$flavor" "$flavor_file"; then + return 0 + fi + fi + + return 1 +} for test in "${root}"/typescript/tests/*.test.ts ; do echo "Running test $test" @@ -43,13 +60,21 @@ for fixture in ${fixtures} ; do if [[ " ${excluded_fixtures[@]} " =~ " ${fixture} " ]]; then continue fi - echo "Running fixture ${fixture}" + # This should all go in either an xtask or into our uniffi-bindgen command. # This builds the crate into the target dir. fixture_dir="${root}/fixtures/${fixture}" + + if ! supports_flavor "${fixture_dir}" "${flavor}"; then + echo "Skipping fixture ${fixture}" + continue + fi + test_file="${fixture_dir}/tests/bindings/test_${fixture//-/_}.ts" config_file="${fixture_dir}/uniffi.toml" out_dir="${fixture_dir}/generated" + + echo "Running fixture ${fixture}" rm -Rf "${out_dir}" 2>/dev/null cpp_dir="${out_dir}/cpp" From 40ebd9ea7748036293369fcccd3ee900c2f12262 Mon Sep 17 00:00:00 2001 From: James Hugman Date: Wed, 4 Dec 2024 15:39:16 +0000 Subject: [PATCH 12/13] Add WASM bindings tests to CI --- .github/workflows/ci.yml | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6284eabe..22b59538 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -49,8 +49,8 @@ jobs: - name: Run tests for binaries run: cargo test --bins - integration-tests-generation: - name: 🧩 Integration tests (generation) + integration-tests-jsi-bindings: + name: 🧩 Integration tests (JSI bindings) runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -61,9 +61,21 @@ jobs: - name: Installing hermes and test-runner run: cargo xtask bootstrap - - name: Run tests of generated bindings + - name: Run tests of generated JSI bindings run: ./scripts/run-tests.sh + integration-tests-wasm-bindings: + name: 🧩 Integration tests (WASM bindings) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Installing + run: cargo xtask bootstrap yarn + + - name: Run tests of generated WASM bindings + run: ./scripts/run-tests.sh --flavor wasm + integration-tests-checkout: name: 🧩 Integration tests (checkout) runs-on: ubuntu-latest From b5c91d6236ef577b964f5fbca3f3aa1b18229d3a Mon Sep 17 00:00:00 2001 From: James Hugman Date: Wed, 4 Dec 2024 15:54:57 +0000 Subject: [PATCH 13/13] fixup! Add wasm feature --- crates/ubrn_bindgen/src/cli.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/crates/ubrn_bindgen/src/cli.rs b/crates/ubrn_bindgen/src/cli.rs index ae1ed52b..1cf38603 100644 --- a/crates/ubrn_bindgen/src/cli.rs +++ b/crates/ubrn_bindgen/src/cli.rs @@ -134,11 +134,8 @@ impl BindingsArgs { let ts_dir = out.ts_dir.canonicalize_utf8()?; let abi_dir = out.cpp_dir.canonicalize_utf8()?; - let generator = ReactNativeBindingGenerator::new( - ts_dir.clone(), - abi_dir.clone(), - self.switches(), - ); + let generator = + ReactNativeBindingGenerator::new(ts_dir.clone(), abi_dir.clone(), self.switches()); let dummy_dir = Utf8PathBuf::from_str(".")?; let try_format_code = !out.no_format;