diff --git a/CHANGELOG.md b/CHANGELOG.md index 3fd775a..57da3a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,17 @@ +#### v0.10.0+v0.31.2 + +---- +- Core: Update bindgen to UniFFI v0.31.2 +- Core: Objects are passed across the FFI as `uint64_t` handles instead of raw pointers +- Core: Trait interfaces distinguish Rust- and C++-implemented objects when lifting and lowering, and implement the new `uniffi_clone` vtable entry +- Core: Support methods and exported `Display`/`Debug`/`Eq`/`Hash`/`Ord` traits on records, enums and errors (flat enums get free functions) +- Core: Support the `Ord` trait (`cmp`) on objects +- Core: Support tuple-style enum variants with unnamed fields +- Core: Support `#[uniffi(default)]` without a literal using C++ value initialization (`{}`) +- Core: Fix optional enum default values rendering with the wrong type +- Core: Fix duplicate type definitions when a record or enum references the same type more than once +- Core: Async functions, constructors and methods are omitted with a warning instead of aborting generation; async callback interfaces are still unsupported + #### v0.9.0+v0.29.4 ---- diff --git a/Cargo.lock b/Cargo.lock index 565c65f..d7cc9de 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -62,7 +62,20 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d4744ed2eef2645831b441d8f5459689ade2ab27c854488fbab1fbe94fce1a7" dependencies = [ - "askama_derive", + "askama_derive 0.13.1", + "itoa", + "percent-encoding", + "serde", + "serde_json", +] + +[[package]] +name = "askama" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f75363874b771be265f4ffe307ca705ef6f3baa19011c149da8674a87f1b75c4" +dependencies = [ + "askama_derive 0.14.0", "itoa", "percent-encoding", "serde", @@ -75,7 +88,24 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d661e0f57be36a5c14c48f78d09011e67e0cb618f269cca9f2fd8d15b68c46ac" dependencies = [ - "askama_parser", + "askama_parser 0.13.0", + "basic-toml", + "memchr", + "proc-macro2", + "quote", + "rustc-hash", + "serde", + "serde_derive", + "syn", +] + +[[package]] +name = "askama_derive" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "129397200fe83088e8a68407a8e2b1f826cf0086b21ccdb866a722c8bcd3a94f" +dependencies = [ + "askama_parser 0.14.0", "basic-toml", "memchr", "proc-macro2", @@ -95,7 +125,19 @@ dependencies = [ "memchr", "serde", "serde_derive", - "winnow", + "winnow 0.7.13", +] + +[[package]] +name = "askama_parser" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6ab5630b3d5eaf232620167977f95eb51f3432fc76852328774afbd242d4358" +dependencies = [ + "memchr", + "serde", + "serde_derive", + "winnow 0.7.13", ] [[package]] @@ -311,6 +353,8 @@ checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5" dependencies = [ "equivalent", "hashbrown", + "serde", + "serde_core", ] [[package]] @@ -507,11 +551,20 @@ dependencies = [ "serde_core", ] +[[package]] +name = "serde_spanned" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "876ac351060d4f882bb1032b6369eb0aef79ad9df1ea8bc404874d8cc3d0cd98" +dependencies = [ + "serde_core", +] + [[package]] name = "siphasher" -version = "0.3.11" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" +checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649" [[package]] name = "smawk" @@ -622,13 +675,43 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "toml" -version = "0.5.11" +version = "0.9.12+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863" dependencies = [ - "serde", + "indexmap", + "serde_core", + "serde_spanned", + "toml_datetime", + "toml_parser", + "toml_writer", + "winnow 0.7.13", ] +[[package]] +name = "toml_datetime" +version = "0.7.5+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_parser" +version = "1.1.3+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d38ac1cf9b95face32296c0a3ede1fdc270627c9d9c02a7274dd6d960dc4d56" +dependencies = [ + "winnow 1.0.4", +] + +[[package]] +name = "toml_writer" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d56353a2a665ad0f41a421187180aab746c8c325620617ad883a99a1cbe66d2" + [[package]] name = "topological-sort" version = "0.2.2" @@ -670,8 +753,8 @@ checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" [[package]] name = "uniffi" -version = "0.29.4" -source = "git+https://github.com/mozilla/uniffi-rs.git?tag=v0.29.4#00cd7e313cf73c78637161831ff17f8f53f7b824" +version = "0.31.2" +source = "git+https://github.com/mozilla/uniffi-rs.git?tag=v0.31.2#05b45b110f652d58d94c653992aeb3adc3a094f4" dependencies = [ "anyhow", "cargo_metadata", @@ -684,10 +767,10 @@ dependencies = [ [[package]] name = "uniffi-bindgen-cpp" -version = "0.9.0+v0.29.4" +version = "0.10.0+v0.31.2" dependencies = [ "anyhow", - "askama", + "askama 0.13.1", "camino", "cargo_metadata", "clap", @@ -763,25 +846,25 @@ dependencies = [ [[package]] name = "uniffi-example-arithmetic" version = "0.22.0" -source = "git+https://github.com/mozilla/uniffi-rs.git?tag=v0.29.4#00cd7e313cf73c78637161831ff17f8f53f7b824" +source = "git+https://github.com/mozilla/uniffi-rs.git?tag=v0.31.2#05b45b110f652d58d94c653992aeb3adc3a094f4" dependencies = [ - "thiserror 1.0.50", + "thiserror 2.0.16", "uniffi", ] [[package]] name = "uniffi-example-callbacks" version = "0.22.0" -source = "git+https://github.com/mozilla/uniffi-rs.git?tag=v0.29.4#00cd7e313cf73c78637161831ff17f8f53f7b824" +source = "git+https://github.com/mozilla/uniffi-rs.git?tag=v0.31.2#05b45b110f652d58d94c653992aeb3adc3a094f4" dependencies = [ - "thiserror 1.0.50", + "thiserror 2.0.16", "uniffi", ] [[package]] name = "uniffi-example-custom-types" version = "0.22.0" -source = "git+https://github.com/mozilla/uniffi-rs.git?tag=v0.29.4#00cd7e313cf73c78637161831ff17f8f53f7b824" +source = "git+https://github.com/mozilla/uniffi-rs.git?tag=v0.31.2#05b45b110f652d58d94c653992aeb3adc3a094f4" dependencies = [ "anyhow", "bytes", @@ -792,7 +875,7 @@ dependencies = [ [[package]] name = "uniffi-example-geometry" version = "0.22.0" -source = "git+https://github.com/mozilla/uniffi-rs.git?tag=v0.29.4#00cd7e313cf73c78637161831ff17f8f53f7b824" +source = "git+https://github.com/mozilla/uniffi-rs.git?tag=v0.31.2#05b45b110f652d58d94c653992aeb3adc3a094f4" dependencies = [ "uniffi", ] @@ -800,7 +883,7 @@ dependencies = [ [[package]] name = "uniffi-example-rondpoint" version = "0.22.0" -source = "git+https://github.com/mozilla/uniffi-rs.git?tag=v0.29.4#00cd7e313cf73c78637161831ff17f8f53f7b824" +source = "git+https://github.com/mozilla/uniffi-rs.git?tag=v0.31.2#05b45b110f652d58d94c653992aeb3adc3a094f4" dependencies = [ "uniffi", ] @@ -808,7 +891,7 @@ dependencies = [ [[package]] name = "uniffi-example-sprites" version = "0.22.0" -source = "git+https://github.com/mozilla/uniffi-rs.git?tag=v0.29.4#00cd7e313cf73c78637161831ff17f8f53f7b824" +source = "git+https://github.com/mozilla/uniffi-rs.git?tag=v0.31.2#05b45b110f652d58d94c653992aeb3adc3a094f4" dependencies = [ "uniffi", ] @@ -816,19 +899,19 @@ dependencies = [ [[package]] name = "uniffi-example-todolist" version = "0.22.0" -source = "git+https://github.com/mozilla/uniffi-rs.git?tag=v0.29.4#00cd7e313cf73c78637161831ff17f8f53f7b824" +source = "git+https://github.com/mozilla/uniffi-rs.git?tag=v0.31.2#05b45b110f652d58d94c653992aeb3adc3a094f4" dependencies = [ "once_cell", - "thiserror 1.0.50", + "thiserror 2.0.16", "uniffi", ] [[package]] name = "uniffi-example-traits" version = "0.22.0" -source = "git+https://github.com/mozilla/uniffi-rs.git?tag=v0.29.4#00cd7e313cf73c78637161831ff17f8f53f7b824" +source = "git+https://github.com/mozilla/uniffi-rs.git?tag=v0.31.2#05b45b110f652d58d94c653992aeb3adc3a094f4" dependencies = [ - "thiserror 1.0.50", + "thiserror 2.0.16", "uniffi", ] @@ -850,48 +933,48 @@ dependencies = [ [[package]] name = "uniffi-fixture-callbacks" version = "0.22.0" -source = "git+https://github.com/mozilla/uniffi-rs.git?tag=v0.29.4#00cd7e313cf73c78637161831ff17f8f53f7b824" +source = "git+https://github.com/mozilla/uniffi-rs.git?tag=v0.31.2#05b45b110f652d58d94c653992aeb3adc3a094f4" dependencies = [ - "thiserror 1.0.50", + "thiserror 2.0.16", "uniffi", ] [[package]] name = "uniffi-fixture-coverall" version = "0.22.0" -source = "git+https://github.com/mozilla/uniffi-rs.git?tag=v0.29.4#00cd7e313cf73c78637161831ff17f8f53f7b824" +source = "git+https://github.com/mozilla/uniffi-rs.git?tag=v0.31.2#05b45b110f652d58d94c653992aeb3adc3a094f4" dependencies = [ "once_cell", - "thiserror 1.0.50", + "thiserror 2.0.16", "uniffi", ] [[package]] name = "uniffi-fixture-docstring" version = "0.22.0" -source = "git+https://github.com/mozilla/uniffi-rs.git?tag=v0.29.4#00cd7e313cf73c78637161831ff17f8f53f7b824" +source = "git+https://github.com/mozilla/uniffi-rs.git?tag=v0.31.2#05b45b110f652d58d94c653992aeb3adc3a094f4" dependencies = [ - "thiserror 1.0.50", + "thiserror 2.0.16", "uniffi", ] [[package]] name = "uniffi-fixture-time" version = "0.22.0" -source = "git+https://github.com/mozilla/uniffi-rs.git?tag=v0.29.4#00cd7e313cf73c78637161831ff17f8f53f7b824" +source = "git+https://github.com/mozilla/uniffi-rs.git?tag=v0.31.2#05b45b110f652d58d94c653992aeb3adc3a094f4" dependencies = [ "chrono", - "thiserror 1.0.50", + "thiserror 2.0.16", "uniffi", ] [[package]] name = "uniffi-fixture-trait-methods" version = "0.22.0" -source = "git+https://github.com/mozilla/uniffi-rs.git?tag=v0.29.4#00cd7e313cf73c78637161831ff17f8f53f7b824" +source = "git+https://github.com/mozilla/uniffi-rs.git?tag=v0.31.2#05b45b110f652d58d94c653992aeb3adc3a094f4" dependencies = [ "once_cell", - "thiserror 1.0.50", + "thiserror 2.0.16", "uniffi", ] @@ -911,11 +994,11 @@ dependencies = [ [[package]] name = "uniffi_bindgen" -version = "0.29.4" -source = "git+https://github.com/mozilla/uniffi-rs.git?tag=v0.29.4#00cd7e313cf73c78637161831ff17f8f53f7b824" +version = "0.31.2" +source = "git+https://github.com/mozilla/uniffi-rs.git?tag=v0.31.2#05b45b110f652d58d94c653992aeb3adc3a094f4" dependencies = [ "anyhow", - "askama", + "askama 0.14.0", "camino", "cargo_metadata", "fs-err", @@ -936,8 +1019,8 @@ dependencies = [ [[package]] name = "uniffi_build" -version = "0.29.4" -source = "git+https://github.com/mozilla/uniffi-rs.git?tag=v0.29.4#00cd7e313cf73c78637161831ff17f8f53f7b824" +version = "0.31.2" +source = "git+https://github.com/mozilla/uniffi-rs.git?tag=v0.31.2#05b45b110f652d58d94c653992aeb3adc3a094f4" dependencies = [ "anyhow", "camino", @@ -946,8 +1029,8 @@ dependencies = [ [[package]] name = "uniffi_core" -version = "0.29.4" -source = "git+https://github.com/mozilla/uniffi-rs.git?tag=v0.29.4#00cd7e313cf73c78637161831ff17f8f53f7b824" +version = "0.31.2" +source = "git+https://github.com/mozilla/uniffi-rs.git?tag=v0.31.2#05b45b110f652d58d94c653992aeb3adc3a094f4" dependencies = [ "anyhow", "bytes", @@ -957,8 +1040,8 @@ dependencies = [ [[package]] name = "uniffi_internal_macros" -version = "0.29.4" -source = "git+https://github.com/mozilla/uniffi-rs.git?tag=v0.29.4#00cd7e313cf73c78637161831ff17f8f53f7b824" +version = "0.31.2" +source = "git+https://github.com/mozilla/uniffi-rs.git?tag=v0.31.2#05b45b110f652d58d94c653992aeb3adc3a094f4" dependencies = [ "anyhow", "indexmap", @@ -969,8 +1052,8 @@ dependencies = [ [[package]] name = "uniffi_macros" -version = "0.29.4" -source = "git+https://github.com/mozilla/uniffi-rs.git?tag=v0.29.4#00cd7e313cf73c78637161831ff17f8f53f7b824" +version = "0.31.2" +source = "git+https://github.com/mozilla/uniffi-rs.git?tag=v0.31.2#05b45b110f652d58d94c653992aeb3adc3a094f4" dependencies = [ "camino", "fs-err", @@ -985,8 +1068,8 @@ dependencies = [ [[package]] name = "uniffi_meta" -version = "0.29.4" -source = "git+https://github.com/mozilla/uniffi-rs.git?tag=v0.29.4#00cd7e313cf73c78637161831ff17f8f53f7b824" +version = "0.31.2" +source = "git+https://github.com/mozilla/uniffi-rs.git?tag=v0.31.2#05b45b110f652d58d94c653992aeb3adc3a094f4" dependencies = [ "anyhow", "siphasher", @@ -996,8 +1079,8 @@ dependencies = [ [[package]] name = "uniffi_pipeline" -version = "0.29.4" -source = "git+https://github.com/mozilla/uniffi-rs.git?tag=v0.29.4#00cd7e313cf73c78637161831ff17f8f53f7b824" +version = "0.31.2" +source = "git+https://github.com/mozilla/uniffi-rs.git?tag=v0.31.2#05b45b110f652d58d94c653992aeb3adc3a094f4" dependencies = [ "anyhow", "heck 0.5.0", @@ -1008,8 +1091,8 @@ dependencies = [ [[package]] name = "uniffi_udl" -version = "0.29.4" -source = "git+https://github.com/mozilla/uniffi-rs.git?tag=v0.29.4#00cd7e313cf73c78637161831ff17f8f53f7b824" +version = "0.31.2" +source = "git+https://github.com/mozilla/uniffi-rs.git?tag=v0.31.2#05b45b110f652d58d94c653992aeb3adc3a094f4" dependencies = [ "anyhow", "textwrap", @@ -1037,7 +1120,7 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "weedle2" version = "5.0.0" -source = "git+https://github.com/mozilla/uniffi-rs.git?tag=v0.29.4#00cd7e313cf73c78637161831ff17f8f53f7b824" +source = "git+https://github.com/mozilla/uniffi-rs.git?tag=v0.31.2#05b45b110f652d58d94c653992aeb3adc3a094f4" dependencies = [ "nom", ] @@ -1213,3 +1296,9 @@ checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" dependencies = [ "memchr", ] + +[[package]] +name = "winnow" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81" diff --git a/Cargo.toml b/Cargo.toml index 06da37f..6f7a295 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,5 +4,5 @@ members = ["bindgen", "fixtures"] resolver = "2" [workspace.dependencies] -uniffi = { git = "https://github.com/mozilla/uniffi-rs.git", tag = "v0.29.4" } -uniffi_bindgen = { git = "https://github.com/mozilla/uniffi-rs.git", tag = "v0.29.4", features = ["cargo-metadata"] } +uniffi = { git = "https://github.com/mozilla/uniffi-rs.git", tag = "v0.31.2" } +uniffi_bindgen = { git = "https://github.com/mozilla/uniffi-rs.git", tag = "v0.31.2", features = ["cargo-metadata"] } diff --git a/README.md b/README.md index b8d8ee5..bc06eef 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,12 @@ Newer Rust versions should also work fine. cargo install uniffi-bindgen-cpp --git https://github.com/NordSecurity/uniffi-bindgen-cpp --tag v0.8.1+v0.29.4 ``` +For this UniFFI 0.31 development branch, install directly from the checkout: + +```bash +cargo install --path bindgen +``` + # How to generate bindings ## Generating with a single UDL file diff --git a/bindgen/Cargo.toml b/bindgen/Cargo.toml index 468c458..7e4673e 100644 --- a/bindgen/Cargo.toml +++ b/bindgen/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uniffi-bindgen-cpp" -version = "0.9.0+v0.29.4" +version = "0.10.0+v0.31.2" edition = "2021" [dependencies] @@ -13,6 +13,6 @@ paste = "1" serde = "1" topological-sort = "0.2.2" textwrap = "0.16.0" -toml = "0.5" +toml = "0.9" uniffi_bindgen = { workspace = true } cargo_metadata = "0.19" diff --git a/bindgen/src/bindings/cpp/gen_cpp/compounds.rs b/bindgen/src/bindings/cpp/gen_cpp/compounds.rs index 1443802..ea1b571 100644 --- a/bindgen/src/bindings/cpp/gen_cpp/compounds.rs +++ b/bindgen/src/bindings/cpp/gen_cpp/compounds.rs @@ -1,5 +1,8 @@ use crate::bindings::cpp::CodeType; -use uniffi_bindgen::{backend::Type, interface::Literal, ComponentInterface}; +use uniffi_bindgen::{ + interface::{Literal, Type}, + ComponentInterface, +}; use crate::bindings::cpp::gen_cpp::filters::CppCodeOracle; diff --git a/bindgen/src/bindings/cpp/gen_cpp/enum_.rs b/bindgen/src/bindings/cpp/gen_cpp/enum_.rs index 6dd4ef9..0c81a9c 100644 --- a/bindgen/src/bindings/cpp/gen_cpp/enum_.rs +++ b/bindgen/src/bindings/cpp/gen_cpp/enum_.rs @@ -1,4 +1,4 @@ -use uniffi_bindgen::{backend::Literal, ComponentInterface}; +use uniffi_bindgen::{interface::Literal, ComponentInterface}; use crate::bindings::cpp::{ gen_cpp::filters::external_namespace_prefix, gen_cpp::filters::CppCodeOracle, CodeType, diff --git a/bindgen/src/bindings/cpp/gen_cpp/filters/mod.rs b/bindgen/src/bindings/cpp/gen_cpp/filters/mod.rs index b25fc02..1aa195a 100644 --- a/bindgen/src/bindings/cpp/gen_cpp/filters/mod.rs +++ b/bindgen/src/bindings/cpp/gen_cpp/filters/mod.rs @@ -1,7 +1,7 @@ use askama; use heck::{ToShoutySnakeCase, ToSnakeCase, ToUpperCamelCase}; use uniffi_bindgen::{ - interface::{Argument, AsType, FfiType, Literal, Object, Type, Variant}, + interface::{Argument, AsType, DefaultValue, FfiType, Literal, Object, Type, Variant}, ComponentInterface, }; @@ -273,15 +273,23 @@ pub(crate) fn object_names(obj: &Object) -> Result<(String, String)> { } pub(crate) fn literal_cpp( - literal: &Literal, + default_value: &DefaultValue, as_ct: &impl AsCodeType, enum_style: &EnumStyle, ci: &ComponentInterface, ) -> Result { + let literal = match default_value { + DefaultValue::Default => return Ok("{}".into()), + DefaultValue::Literal(Literal::Some { inner }) => { + return literal_cpp(inner, as_ct, enum_style, ci) + } + DefaultValue::Literal(literal) => literal, + }; + match literal { - Literal::Enum(name, _) => Ok(format!( + Literal::Enum(name, type_) => Ok(format!( "{}::{}", - as_ct.as_codetype().type_label(ci), + CppCodeOracle.find(type_).type_label(ci), CppCodeOracle.enum_variant_name(&name, enum_style), )), _ => Ok(as_ct.as_codetype().literal(literal, ci)), @@ -339,7 +347,7 @@ pub(crate) fn ffi_type_name(ffi_type: &FfiType) -> Result { FfiType::Int64 => "int64_t".into(), FfiType::Float32 => "float".into(), FfiType::Float64 => "double".into(), - FfiType::RustArcPtr(_) | FfiType::VoidPointer => "void *".into(), + FfiType::VoidPointer => "void *".into(), FfiType::RustBuffer(_) => "RustBuffer".into(), FfiType::ForeignBytes => "ForeignBytes".into(), FfiType::Callback(_) => "void *".into(), diff --git a/bindgen/src/bindings/cpp/gen_cpp/miscellany.rs b/bindgen/src/bindings/cpp/gen_cpp/miscellany.rs index a8fa315..ef32ec7 100644 --- a/bindgen/src/bindings/cpp/gen_cpp/miscellany.rs +++ b/bindgen/src/bindings/cpp/gen_cpp/miscellany.rs @@ -1,6 +1,6 @@ use crate::bindings::cpp::CodeType; use paste::paste; -use uniffi_bindgen::{backend::Literal, ComponentInterface}; +use uniffi_bindgen::{interface::Literal, ComponentInterface}; macro_rules! impl_code_type_for_miscellany { ($T:ty, $label:literal, $canonical_name:literal) => { diff --git a/bindgen/src/bindings/cpp/gen_cpp/mod.rs b/bindgen/src/bindings/cpp/gen_cpp/mod.rs index e6b0ae0..7a4bc6a 100644 --- a/bindgen/src/bindings/cpp/gen_cpp/mod.rs +++ b/bindgen/src/bindings/cpp/gen_cpp/mod.rs @@ -171,19 +171,36 @@ impl<'a> CppWrapperHeader<'a> { // We take into account only the Record and Enum types, as they are the // only types that can have member variables that reference other structures match type_ { - Type::Record { name, .. } => self - .ci - .get_record_definition(name.as_str()) - .map(|record| (name, record.iter_types())), - Type::Enum { name, .. } => self - .ci - .get_enum_definition(name.as_str()) - .map(|enum_| (name, enum_.iter_types())), + Type::Record { name, .. } => { + self.ci.get_record_definition(name.as_str()).map(|record| { + ( + name, + record + .fields() + .iter() + .flat_map(Field::iter_types) + .collect::>(), + ) + }) + } + Type::Enum { name, .. } => { + self.ci.get_enum_definition(name.as_str()).map(|enum_| { + ( + name, + enum_ + .variants() + .iter() + .flat_map(Variant::iter_types) + .collect::>(), + ) + }) + } _ => None, } }) .flat_map(|(name, types)| { types + .into_iter() .filter_map(type_name) .map(|field_name| DependencyLink { prec: field_name, @@ -193,9 +210,18 @@ impl<'a> CppWrapperHeader<'a> { .collect::>(); let mut sorted: Vec = Vec::new(); + let mut emitted_names = BTreeSet::new(); while !definition_topology.peek_all().is_empty() { - let list = definition_topology.pop_all(); + let mut list = definition_topology.pop_all(); + list.sort(); for name in list { + // TopologicalSort preserves duplicate dependency links. A record or + // rich enum can mention the same named type more than once, causing + // that type to be returned once per link. Definitions, however, + // must be emitted exactly once. + if !emitted_names.insert(name) { + continue; + } match self.ci.get_type(name) { // External types are defined in their own namespace's header with their own // converters declared, which we `#include`. They must not enter the local @@ -214,7 +240,7 @@ impl<'a> CppWrapperHeader<'a> { } let rest = types - .filter(|&t| !sorted.contains(t)) + .filter(|t| type_name(t).map_or(true, |name| !emitted_names.contains(name))) .cloned() .collect::>(); diff --git a/bindgen/src/bindings/cpp/gen_cpp/object.rs b/bindgen/src/bindings/cpp/gen_cpp/object.rs index 3088cba..4dd7fa2 100644 --- a/bindgen/src/bindings/cpp/gen_cpp/object.rs +++ b/bindgen/src/bindings/cpp/gen_cpp/object.rs @@ -1,4 +1,7 @@ -use uniffi_bindgen::{backend::Literal, interface::ObjectImpl, ComponentInterface}; +use uniffi_bindgen::{ + interface::{Literal, ObjectImpl}, + ComponentInterface, +}; use crate::bindings::cpp::{ gen_cpp::filters::callback_interface_name, gen_cpp::filters::external_namespace_prefix, diff --git a/bindgen/src/bindings/cpp/gen_cpp/primitives.rs b/bindgen/src/bindings/cpp/gen_cpp/primitives.rs index 19db704..11c5a50 100644 --- a/bindgen/src/bindings/cpp/gen_cpp/primitives.rs +++ b/bindgen/src/bindings/cpp/gen_cpp/primitives.rs @@ -1,6 +1,9 @@ use crate::bindings::cpp::CodeType; use paste::paste; -use uniffi_bindgen::{backend::Literal, interface::Radix, ComponentInterface}; +use uniffi_bindgen::{ + interface::{Literal, Radix}, + ComponentInterface, +}; fn render_literal(literal: &Literal) -> String { match literal { diff --git a/bindgen/src/bindings/cpp/gen_cpp/record.rs b/bindgen/src/bindings/cpp/gen_cpp/record.rs index e15ce0d..73d6605 100644 --- a/bindgen/src/bindings/cpp/gen_cpp/record.rs +++ b/bindgen/src/bindings/cpp/gen_cpp/record.rs @@ -1,4 +1,4 @@ -use uniffi_bindgen::{backend::Literal, ComponentInterface}; +use uniffi_bindgen::{interface::Literal, ComponentInterface}; use crate::bindings::cpp::{ gen_cpp::filters::external_namespace_prefix, gen_cpp::filters::CppCodeOracle, CodeType, diff --git a/bindgen/src/bindings/cpp/mod.rs b/bindgen/src/bindings/cpp/mod.rs index 7b938e7..7ccd27d 100644 --- a/bindgen/src/bindings/cpp/mod.rs +++ b/bindgen/src/bindings/cpp/mod.rs @@ -5,7 +5,7 @@ use std::{fmt::Debug, fs}; use anyhow::Result; use serde::{Deserialize, Serialize}; use uniffi_bindgen::{ - backend::Literal, BindingGenerator, Component, ComponentInterface, GenerationSettings, + interface::Literal, BindingGenerator, Component, ComponentInterface, GenerationSettings, }; use self::gen_cpp::{generate_cpp_bindings, Bindings}; @@ -93,9 +93,16 @@ impl BindingGenerator for CppBindingGenerator { components: &[uniffi_bindgen::Component], ) -> Result<()> { for Component { ci, config, .. } in components { - if ci.has_async_fns() || ci.has_async_callback_interface_definition() { + if ci.has_async_callback_interface_definition() { unimplemented!( - "Cpp bindgen does not support async functions! Namespace: {}", + "Cpp bindgen does not support async callback interfaces! Namespace: {}", + ci.namespace() + ); + } + + if ci.has_async_fns() { + eprintln!( + "Warning: omitting unsupported async functions from namespace: {}", ci.namespace() ); } diff --git a/bindgen/src/bindings/cpp/templates/callback.hpp b/bindgen/src/bindings/cpp/templates/callback.hpp index 54efb62..ea000f3 100644 --- a/bindgen/src/bindings/cpp/templates/callback.hpp +++ b/bindgen/src/bindings/cpp/templates/callback.hpp @@ -21,13 +21,15 @@ namespace uniffi { {%- endfor %} static void uniffi_free(uint64_t uniffi_handle); + static uint64_t uniffi_clone(uint64_t uniffi_handle); static void init(); private: static inline {{ vtable|ffi_type_name }} vtable = {{ vtable|ffi_type_name}} { + .uniffi_free = reinterpret_cast(&uniffi_free), + .uniffi_clone = reinterpret_cast(&uniffi_clone), {%- for (ffi_callback, meth) in vtable_methods.iter() %} .{{ meth.name()|var_name }} = reinterpret_cast(&{{ meth.name()|var_name }}), {%- endfor %} - .uniffi_free = reinterpret_cast(&uniffi_free) }; }; } diff --git a/bindgen/src/bindings/cpp/templates/callback_iface_tmpl.cpp b/bindgen/src/bindings/cpp/templates/callback_iface_tmpl.cpp index d881585..4ef44a3 100644 --- a/bindgen/src/bindings/cpp/templates/callback_iface_tmpl.cpp +++ b/bindgen/src/bindings/cpp/templates/callback_iface_tmpl.cpp @@ -42,6 +42,12 @@ void {{ trait_impl }}::uniffi_free(uint64_t uniffi_handle) { {{ ffi_converter_name }}::handle_map.erase(uniffi_handle); } +uint64_t {{ trait_impl }}::uniffi_clone(uint64_t uniffi_handle) { + return {{ ffi_converter_name }}::handle_map.insert( + {{ ffi_converter_name }}::handle_map.at(uniffi_handle) + ); +} + void {{ trait_impl }}::init() { {{ ffi_init_callback.name() }}(vtable); } diff --git a/bindgen/src/bindings/cpp/templates/enum.hpp b/bindgen/src/bindings/cpp/templates/enum.hpp index bcb687c..6deb25f 100644 --- a/bindgen/src/bindings/cpp/templates/enum.hpp +++ b/bindgen/src/bindings/cpp/templates/enum.hpp @@ -10,6 +10,9 @@ enum class {{ type_name }}: int32_t { {%- endif %} {%- endfor %} }; +{%- let methods = e.methods() %} +{%- let uniffi_trait_methods = e.uniffi_trait_methods() %} +{% include "flat_enum_methods.hpp" %} {%- else %} namespace uniffi { struct {{ ffi_converter_name }}; @@ -24,7 +27,7 @@ struct {{ type_name }} { struct {{ variant|variant_name(config.enum_style) }} { {%- for field in variant.fields() %} {%- call macros::docstring(field, 8) %} - {{ field|type_name(ci) }} {{ field.name()|var_name }} + {{ field|type_name(ci) }} {% call macros::field_name(field, loop.index) %} {%- match field.default_value() %} {%- when Some with (literal) %} = {{ literal|literal_cpp(field, config.enum_style, ci) }};{%- else -%}; {%- endmatch %} @@ -56,6 +59,10 @@ struct {{ type_name }} { return variant; } + {%- let methods = e.methods() %} + {%- let uniffi_trait_methods = e.uniffi_trait_methods() %} + {% include "value_methods.hpp" %} + private: std::variant<{% for variant in e.variants() %}{{ variant|variant_name(config.enum_style) }}{% if !loop.last %}, {% endif %}{% endfor %}> variant; diff --git a/bindgen/src/bindings/cpp/templates/enum_tmpl.cpp b/bindgen/src/bindings/cpp/templates/enum_tmpl.cpp index 48b947c..602890c 100644 --- a/bindgen/src/bindings/cpp/templates/enum_tmpl.cpp +++ b/bindgen/src/bindings/cpp/templates/enum_tmpl.cpp @@ -74,7 +74,7 @@ RustBuffer {{ ffi_converter_name }}::lower(const {{ type_name }} &val) { case {{ loop.index }}: return {{ type_name }}::{{ variant|variant_name(config.enum_style) }} { {%- for field in variant.fields() %} - .{{field.name()|var_name}} = {{ field|read_fn }}(stream), + .{% call macros::field_name(field, loop.index) %} = {{ field|read_fn }}(stream), {%- endfor %} }; {% endfor %} @@ -93,7 +93,7 @@ void {{ ffi_converter_name }}::write(RustStream &stream, const {{ type_name }} & {%- for variant in e.variants() %} {% if !loop.first %}else {% endif %}if constexpr (std::is_same_v) { {%- for field in variant.fields() %} - {{ field|write_fn }}(stream, {{ field.as_type()|cpp_deref(ci) }}arg.{{ field.name()|var_name }}); + {{ field|write_fn }}(stream, {{ field.as_type()|cpp_deref(ci) }}arg.{% call macros::field_name(field, loop.index) %}); {%- endfor %} } {%- endfor %} @@ -114,7 +114,7 @@ uint64_t {{ ffi_converter_name }}::allocation_size(const {{ type_name|class_name {% if !loop.first %}else {% endif %}if constexpr (std::is_same_v) { uint64_t size = 0; {%- for field in variant.fields() %} - size += {{ field|allocation_size_fn }}({{ field.as_type()|cpp_deref(ci) }}arg.{{ field.name()|var_name }}); + size += {{ field|allocation_size_fn }}({{ field.as_type()|cpp_deref(ci) }}arg.{% call macros::field_name(field, loop.index) %}); {%- endfor %} return size; } diff --git a/bindgen/src/bindings/cpp/templates/err.hpp b/bindgen/src/bindings/cpp/templates/err.hpp index bcec06f..35528ed 100644 --- a/bindgen/src/bindings/cpp/templates/err.hpp +++ b/bindgen/src/bindings/cpp/templates/err.hpp @@ -18,6 +18,11 @@ struct {{ class_name }}: std::runtime_error { throw *this; } + {%- let methods = e.methods() %} + {%- let uniffi_trait_methods = e.uniffi_trait_methods() %} + {%- let type_name = class_name %} + {% include "value_methods.hpp" %} + protected: virtual int32_t get_variant_idx() const { return 0; diff --git a/bindgen/src/bindings/cpp/templates/flat_enum_methods.cpp b/bindgen/src/bindings/cpp/templates/flat_enum_methods.cpp new file mode 100644 index 0000000..5534b8a --- /dev/null +++ b/bindgen/src/bindings/cpp/templates/flat_enum_methods.cpp @@ -0,0 +1,44 @@ +{%- for method in methods %} +{%- if !method.is_async() %} +{% match method.return_type() %}{% when Some with (return_type) %}{{ return_type|type_name(ci) }} {% else %}void {% endmatch -%} +{{ method.name()|fn_name }}({{ type_name }} _uniffi_self{% if !method.arguments().is_empty() %}, {% endif %}{% call macros::param_list(method) %}) { + {%- match method.return_type() %} + {%- when Some with (return_type) %} + return uniffi::{{ return_type|lift_fn }}({% call macros::rust_call_with_value(method, typ) %}); + {%- when None %} + {% call macros::rust_call_with_value(method, typ) %}; + {%- endmatch %} +} +{%- endif %} +{%- endfor %} + +{%- if let Some(method) = uniffi_trait_methods.display_fmt %} +std::string to_string({{ type_name }} _uniffi_self) { + return uniffi::{{ Type::String.borrow()|lift_fn }}({% call macros::rust_call_with_value(method, typ) %}); +} +{%- endif %} +{%- if let Some(method) = uniffi_trait_methods.debug_fmt %} +std::string to_debug_string({{ type_name }} _uniffi_self) { + return uniffi::{{ Type::String.borrow()|lift_fn }}({% call macros::rust_call_with_value(method, typ) %}); +} +{%- endif %} +{%- if let Some(method) = uniffi_trait_methods.eq_eq %} +bool eq({{ type_name }} _uniffi_self, {{ type_name }} other) { + return uniffi::{{ Type::Boolean.borrow()|lift_fn }}({% call macros::rust_call_with_value(method, typ) %}); +} +{%- endif %} +{%- if let Some(method) = uniffi_trait_methods.eq_ne %} +bool ne({{ type_name }} _uniffi_self, {{ type_name }} other) { + return uniffi::{{ Type::Boolean.borrow()|lift_fn }}({% call macros::rust_call_with_value(method, typ) %}); +} +{%- endif %} +{%- if let Some(method) = uniffi_trait_methods.hash_hash %} +uint64_t hash({{ type_name }} _uniffi_self) { + return uniffi::{{ Type::UInt64.borrow()|lift_fn }}({% call macros::rust_call_with_value(method, typ) %}); +} +{%- endif %} +{%- if let Some(method) = uniffi_trait_methods.ord_cmp %} +int8_t cmp({{ type_name }} _uniffi_self, {{ type_name }} other) { + return uniffi::{{ Type::Int8.borrow()|lift_fn }}({% call macros::rust_call_with_value(method, typ) %}); +} +{%- endif %} diff --git a/bindgen/src/bindings/cpp/templates/flat_enum_methods.hpp b/bindgen/src/bindings/cpp/templates/flat_enum_methods.hpp new file mode 100644 index 0000000..0b2b0e4 --- /dev/null +++ b/bindgen/src/bindings/cpp/templates/flat_enum_methods.hpp @@ -0,0 +1,30 @@ +{%- for method in methods %} +{%- if !method.is_async() %} +{%- call macros::docstring(method, 0) %} +{% match method.return_type() %}{% when Some with (return_type) %}{{ return_type|type_name(ci) }} {% else %}void {% endmatch -%} +{{ method.name()|fn_name }}({{ type_name }} _uniffi_self{% if !method.arguments().is_empty() %}, {% endif %}{% call macros::param_list(method) %}); +{%- endif %} +{%- endfor %} + +{%- if uniffi_trait_methods.display_fmt.is_some() %} +/** Calls the value's Rust `Display` implementation. */ +std::string to_string({{ type_name }} _uniffi_self); +{%- endif %} +{%- if uniffi_trait_methods.debug_fmt.is_some() %} +/** Calls the value's Rust `Debug` implementation. */ +std::string to_debug_string({{ type_name }} _uniffi_self); +{%- endif %} +{%- if uniffi_trait_methods.eq_eq.is_some() %} +/** Calls the value's Rust `Eq` implementation. */ +bool eq({{ type_name }} _uniffi_self, {{ type_name }} other); +/** Calls the value's Rust `Ne` implementation. */ +bool ne({{ type_name }} _uniffi_self, {{ type_name }} other); +{%- endif %} +{%- if uniffi_trait_methods.hash_hash.is_some() %} +/** Calls the value's Rust `Hash` implementation. */ +uint64_t hash({{ type_name }} _uniffi_self); +{%- endif %} +{%- if uniffi_trait_methods.ord_cmp.is_some() %} +/** Calls the value's Rust `Ord` implementation. */ +int8_t cmp({{ type_name }} _uniffi_self, {{ type_name }} other); +{%- endif %} diff --git a/bindgen/src/bindings/cpp/templates/handle_map.cpp b/bindgen/src/bindings/cpp/templates/handle_map.cpp index 8607aa3..289e900 100644 --- a/bindgen/src/bindings/cpp/templates/handle_map.cpp +++ b/bindgen/src/bindings/cpp/templates/handle_map.cpp @@ -14,7 +14,8 @@ template struct HandleMap { auto handle = this->cur_handle; this->map.insert({ handle, impl }); - this->cur_handle += 1; + // UniFFI reserves odd values for handles created by foreign bindings. + this->cur_handle += 2; return handle; } @@ -39,6 +40,6 @@ template struct HandleMap { HandleMap &operator=(HandleMap &&) = delete; std::mutex mutex; - uint64_t cur_handle = 0; + uint64_t cur_handle = 1; std::map> map; }; diff --git a/bindgen/src/bindings/cpp/templates/macros.cpp b/bindgen/src/bindings/cpp/templates/macros.cpp index 934ac89..0cf6f7c 100644 --- a/bindgen/src/bindings/cpp/templates/macros.cpp +++ b/bindgen/src/bindings/cpp/templates/macros.cpp @@ -25,6 +25,47 @@ {%- call arg_list_lowered(func) -%}) {%- endmacro %} +{% macro rust_call_with_self(func, self_type) -%} + uniffi::rust_call( + {{ func.ffi_func().name() }}, +{%- match func.throws_type() %} +{% when Some with (e) %} + uniffi::{{ e|ffi_error_converter_name }}::lift, +{%- else %} + nullptr, +{%- endmatch %} + uniffi::{{ self_type|lower_fn }}(*this) +{%- if !func.arguments().is_empty() %}, {% else %}{% endif %} + {%- call arg_list_lowered(func) -%}) +{%- endmacro %} + +{% macro rust_call_with_value(func, self_type) -%} + uniffi::rust_call( + {{ func.ffi_func().name() }}, +{%- match func.throws_type() %} +{% when Some with (e) %} + uniffi::{{ e|ffi_error_converter_name }}::lift, +{%- else %} + nullptr, +{%- endmatch %} + uniffi::{{ self_type|lower_fn }}(_uniffi_self) +{%- if !func.arguments().is_empty() %}, {% else %}{% endif %} + {%- call arg_list_lowered(func) -%}) +{%- endmacro %} + +{% macro rust_call_with_self_and_other(func, self_type) -%} + uniffi::rust_call( + {{ func.ffi_func().name() }}, +{%- match func.throws_type() %} +{% when Some with (e) %} + uniffi::{{ e|ffi_error_converter_name }}::lift, +{%- else %} + nullptr, +{%- endmatch %} + uniffi::{{ self_type|lower_fn }}(*this), + uniffi::{{ self_type|lower_fn }}(other)) +{%- endmacro %} + {% macro param_list(func) %} {%- for arg in func.arguments() -%} {{ arg|parameter(ci) }} @@ -42,7 +83,7 @@ v{{- field_num -}} {% macro arg_list_lowered(func) %} {%- for arg in func.arguments() -%} -uniffi::{{ arg|lower_fn }}({{ arg.name()|var_name }}) +uniffi::{{ arg|lower_fn }}({{ arg.as_type()|cpp_deref(ci) }}{{ arg.name()|var_name }}) {%- if !loop.last -%}, {% endif -%} {% endfor -%} {% endmacro %} diff --git a/bindgen/src/bindings/cpp/templates/obj.cpp b/bindgen/src/bindings/cpp/templates/obj.cpp index 3f1de51..a818598 100644 --- a/bindgen/src/bindings/cpp/templates/obj.cpp +++ b/bindgen/src/bindings/cpp/templates/obj.cpp @@ -14,9 +14,9 @@ namespace uniffi { {%- endif %} -{{ impl_class_name }}::{{ impl_class_name }}(void *ptr): instance(ptr) {} +{{ impl_class_name }}::{{ impl_class_name }}(uint64_t handle): instance(handle) {} -{{ impl_class_name }}::{{ impl_class_name }}(const {{ impl_class_name }} &other) : instance(nullptr) { +{{ impl_class_name }}::{{ impl_class_name }}(const {{ impl_class_name }} &other) : instance(0) { if (other.instance) { instance = other._uniffi_internal_clone_pointer(); } @@ -30,21 +30,26 @@ namespace uniffi { {% match obj.primary_constructor() -%} {%- when Some with (ctor) %} +{%- if !ctor.is_async() %} {{ type_name }} {{ impl_class_name }}::init({% call macros::param_list(ctor) %}) { return {{ type_name }}( new {{ impl_class_name }}({%- call macros::rust_call(ctor) -%}) ); } +{%- endif %} {% else -%} {% endmatch -%} {% for ctor in obj.alternate_constructors() %} +{%- if !ctor.is_async() %} {{ type_name }} {{ impl_class_name }}::{{ ctor.name() }}({% call macros::param_list(ctor) %}) { return {{ type_name }}(new {{ impl_class_name }}({% call macros::rust_call(ctor) %})); } +{%- endif %} {% endfor %} {%- for method in obj.methods() %} +{%- if !method.is_async() %} {% match method.return_type() %}{% when Some with (return_type) %}{{ return_type|type_name(ci) }} {% else %}void {% endmatch -%} {{ impl_class_name }}::{{ method.name()|fn_name }}({% call macros::param_list(method) %}) { auto ptr = this->_uniffi_internal_clone_pointer(); @@ -55,6 +60,7 @@ namespace uniffi { {% call macros::rust_call_with_prefix("ptr", method) -%}; {%- endmatch %} } +{%- endif %} {%- endfor %} {{ impl_class_name }}::~{{ impl_class_name }}() { @@ -65,7 +71,7 @@ namespace uniffi { ); } -void *{{ impl_class_name }}::_uniffi_internal_clone_pointer() const { +uint64_t {{ impl_class_name }}::_uniffi_internal_clone_pointer() const { return uniffi::rust_call( {{ obj.ffi_object_clone().name() }}, nullptr, @@ -94,5 +100,9 @@ bool {{ impl_class_name }}::ne(const {{ type_name }} &other) const { uint64_t {{ impl_class_name }}::hash() const { return uniffi::{{ Type::UInt64.borrow()|lift_fn }}({% call macros::rust_call_with_prefix("this->_uniffi_internal_clone_pointer()", hash) %}); } +{% when UniffiTrait::Ord { cmp } %} +int8_t {{ impl_class_name }}::cmp(const {{ type_name }} &other) const { + return uniffi::{{ Type::Int8.borrow()|lift_fn }}({% call macros::rust_call_with_prefix("this->_uniffi_internal_clone_pointer()", cmp) %}); +} {% endmatch %} {%- endfor %} diff --git a/bindgen/src/bindings/cpp/templates/obj.hpp b/bindgen/src/bindings/cpp/templates/obj.hpp index fb02106..c0398ac 100644 --- a/bindgen/src/bindings/cpp/templates/obj.hpp +++ b/bindgen/src/bindings/cpp/templates/obj.hpp @@ -38,20 +38,26 @@ struct {{ impl_class_name }} {%- match obj.primary_constructor() %} {%- when Some with (ctor) %} + {%- if !ctor.is_async() %} {%- call macros::docstring(ctor, 4) %} static {{ type_name }} init({% call macros::param_list(ctor) %}); + {%- endif %} {%- else %} {%- endmatch %} {%- for ctor in obj.alternate_constructors() %} + {%- if !ctor.is_async() %} {%- call macros::docstring(ctor, 4) %} static {{ type_name }} {{ ctor.name() }}({% call macros::param_list(ctor) %}); + {%- endif %} {%- endfor %} {%- for method in obj.methods() %} + {%- if !method.is_async() %} {%- call macros::docstring(method, 4) %} {% match method.return_type() %}{% when Some with (return_type) %}{{ return_type|type_name(ci) }} {% else %}void {% endmatch %} {{- method.name()|fn_name }}({% call macros::param_list(method) %}); + {%- endif %} {%- endfor %} {%- for method in obj.uniffi_traits() %} @@ -80,6 +86,11 @@ struct {{ impl_class_name }} * Returns a hash of the object, internally calls Rust's `Hash` trait. */ uint64_t hash() const; + {%- when UniffiTrait::Ord { cmp } %} + /** + * Three-way comparison, internally calls Rust's `Ord` trait. + */ + int8_t cmp(const {{ type_name }} &other) const; {%- endmatch %} {%- endfor %} @@ -89,9 +100,9 @@ struct {{ impl_class_name }} private: {{ impl_class_name }}(const {{ impl_class_name }} &); - {{ impl_class_name }}(void *); + {{ impl_class_name }}(uint64_t); - void *_uniffi_internal_clone_pointer() const; + uint64_t _uniffi_internal_clone_pointer() const; - void *instance = nullptr; + uint64_t instance = 0; }; diff --git a/bindgen/src/bindings/cpp/templates/obj_conv.cpp b/bindgen/src/bindings/cpp/templates/obj_conv.cpp index 27f70ef..2bb7bbe 100644 --- a/bindgen/src/bindings/cpp/templates/obj_conv.cpp +++ b/bindgen/src/bindings/cpp/templates/obj_conv.cpp @@ -12,28 +12,37 @@ } {% endif %} -{{ type_name }} {{ ffi_converter_name }}::lift(void *ptr) { - return {{ type_name }}(new {{ impl_class_name }}(ptr)); +{{ type_name }} {{ ffi_converter_name }}::lift(uint64_t handle) { + {%- if obj.has_callback_interface() %} + if ((handle & 1) == 1) { + auto obj = handle_map.at(handle); + handle_map.erase(handle); + return obj; + } + {%- endif %} + return {{ type_name }}(new {{ impl_class_name }}(handle)); } -void *{{ ffi_converter_name }}::lower(const {{ type_name }} &obj) { +uint64_t {{ ffi_converter_name }}::lower(const {{ type_name }} &obj) { {%- if obj.has_callback_interface() %} - auto ptr = handle_map.insert(obj); - return reinterpret_cast(ptr); + if (auto rust_obj = std::dynamic_pointer_cast<{{ impl_class_name }}>(obj)) { + return rust_obj->_uniffi_internal_clone_pointer(); + } + return handle_map.insert(obj); {%- else %} return reinterpret_cast<{{ impl_class_name}}*>(obj.get())->_uniffi_internal_clone_pointer(); {%- endif %} } {{ type_name }} {{ ffi_converter_name }}::read(RustStream &stream) { - std::uintptr_t ptr; - stream >> ptr; + uint64_t handle; + stream >> handle; - return {{ ffi_converter_name}}::lift(reinterpret_cast(ptr)); + return {{ ffi_converter_name}}::lift(handle); } void {{ ffi_converter_name }}::write(RustStream &stream, const {{ type_name }} &obj) { - stream << reinterpret_cast({{ ffi_converter_name }}::lower(obj)); + stream << {{ ffi_converter_name }}::lower(obj); } uint64_t {{ ffi_converter_name }}::allocation_size(const {{ type_name }} &) { diff --git a/bindgen/src/bindings/cpp/templates/obj_conv.hpp b/bindgen/src/bindings/cpp/templates/obj_conv.hpp index 682f408..e19f802 100644 --- a/bindgen/src/bindings/cpp/templates/obj_conv.hpp +++ b/bindgen/src/bindings/cpp/templates/obj_conv.hpp @@ -10,8 +10,8 @@ struct {{ typ|ffi_error_converter_name}} { {% endif %} struct {{ typ|ffi_converter_name }} { - static {{ type_name }} lift(void *); - static void *lower(const {{ type_name }} &); + static {{ type_name }} lift(uint64_t); + static uint64_t lower(const {{ type_name }} &); static {{ type_name }} read(RustStream &); static void write(RustStream &, const {{ type_name }} &); static uint64_t allocation_size(const {{ type_name }} &); diff --git a/bindgen/src/bindings/cpp/templates/rec.hpp b/bindgen/src/bindings/cpp/templates/rec.hpp index e0e2902..973bf45 100644 --- a/bindgen/src/bindings/cpp/templates/rec.hpp +++ b/bindgen/src/bindings/cpp/templates/rec.hpp @@ -8,4 +8,8 @@ struct {{ type_name }} { {%- when Some with (literal) %} = {{ literal|literal_cpp(field, config.enum_style, ci) }};{%- else -%}; {%- endmatch %} {%- endfor %} + + {%- let methods = rec.methods() %} + {%- let uniffi_trait_methods = rec.uniffi_trait_methods() %} + {% include "value_methods.hpp" %} }; diff --git a/bindgen/src/bindings/cpp/templates/scaffolding/macros.cpp b/bindgen/src/bindings/cpp/templates/scaffolding/macros.cpp index d8ff781..5292f5f 100644 --- a/bindgen/src/bindings/cpp/templates/scaffolding/macros.cpp +++ b/bindgen/src/bindings/cpp/templates/scaffolding/macros.cpp @@ -73,3 +73,11 @@ obj->{{ scaffolding_fn.name() }}( {% endfor %} {%- if ffi_func.has_rust_call_status_arg() %}RustCallStatus *out_status{% endif -%}) {% endmacro %} + +{% macro field_name(field, field_num) %} +{%- if field.name().is_empty() -%} +v{{- field_num -}} +{%- else -%} +{{ field.name()|var_name }} +{%- endif -%} +{%- endmacro %} diff --git a/bindgen/src/bindings/cpp/templates/scaffolding/obj.cpp b/bindgen/src/bindings/cpp/templates/scaffolding/obj.cpp index 240d41b..e36458d 100644 --- a/bindgen/src/bindings/cpp/templates/scaffolding/obj.cpp +++ b/bindgen/src/bindings/cpp/templates/scaffolding/obj.cpp @@ -1,17 +1,16 @@ -{{ type_name }} {{ ffi_converter_name }}::lift(void *ptr) { - return {{ name }}_map.at((uint64_t)ptr); +{{ type_name }} {{ ffi_converter_name }}::lift(uint64_t handle) { + return {{ name }}_map.at(handle); } -void *{{ ffi_converter_name }}::lower(const {{ type_name }} &obj) { - auto ret = {{ name }}_map.insert(obj); - return (void *)ret; +uint64_t {{ ffi_converter_name }}::lower(const {{ type_name }} &obj) { + return {{ name }}_map.insert(obj); } {{ type_name }} {{ ffi_converter_name }}::read(RustStream &stream) { - std::uintptr_t ptr; - stream >> ptr; + uint64_t handle; + stream >> handle; - return {{ name }}_map.at(ptr); + return {{ name }}_map.at(handle); } void {{ ffi_converter_name }}::write(RustStream &stream, const {{ type_name }} &obj) { diff --git a/bindgen/src/bindings/cpp/templates/scaffolding/obj.hpp b/bindgen/src/bindings/cpp/templates/scaffolding/obj.hpp index 5d69641..c2acefe 100644 --- a/bindgen/src/bindings/cpp/templates/scaffolding/obj.hpp +++ b/bindgen/src/bindings/cpp/templates/scaffolding/obj.hpp @@ -1,7 +1,7 @@ {%- let type_name = typ|type_name(ci) %} struct {{ typ|ffi_converter_name }} { - static {{ type_name }} lift(void *); - static void *lower(const {{ type_name }} &); + static {{ type_name }} lift(uint64_t); + static uint64_t lower(const {{ type_name }} &); static {{ type_name }} read(RustStream &); static void write(RustStream &, const {{ type_name }} &); static int32_t allocation_size(const {{ type_name }} &); diff --git a/bindgen/src/bindings/cpp/templates/types.cpp b/bindgen/src/bindings/cpp/templates/types.cpp index 47ba6e0..a486253 100644 --- a/bindgen/src/bindings/cpp/templates/types.cpp +++ b/bindgen/src/bindings/cpp/templates/types.cpp @@ -10,6 +10,23 @@ {%- match typ %} {%- when Type::Object { module_path, name, imp } %} {% include "obj.cpp" %} +{%- when Type::Record { module_path, name } %} +{%- let rec = ci.get_record_definition(name).unwrap() %} +{%- let methods = rec.methods() %} +{%- let uniffi_trait_methods = rec.uniffi_trait_methods() %} +{% include "value_methods.cpp" %} +{%- when Type::Enum { name, module_path } %} +{%- let e = ci.get_enum_definition(name).unwrap() %} +{%- let methods = e.methods() %} +{%- let uniffi_trait_methods = e.uniffi_trait_methods() %} +{%- if ci.is_name_used_as_error(name) %} +{%- let type_name = typ|canonical_name %} +{% include "value_methods.cpp" %} +{%- else if e.is_flat() %} +{% include "flat_enum_methods.cpp" %} +{%- else %} +{% include "value_methods.cpp" %} +{%- endif %} {%- else %} {%- endmatch %} {% endfor ~%} diff --git a/bindgen/src/bindings/cpp/templates/value_methods.cpp b/bindgen/src/bindings/cpp/templates/value_methods.cpp new file mode 100644 index 0000000..5ef16e7 --- /dev/null +++ b/bindgen/src/bindings/cpp/templates/value_methods.cpp @@ -0,0 +1,44 @@ +{%- for method in methods %} +{%- if !method.is_async() %} +{% match method.return_type() %}{% when Some with (return_type) %}{{ return_type|type_name(ci) }} {% else %}void {% endmatch -%} +{{ type_name }}::{{ method.name()|fn_name }}({% call macros::param_list(method) %}) const { + {%- match method.return_type() %} + {%- when Some with (return_type) %} + return uniffi::{{ return_type|lift_fn }}({% call macros::rust_call_with_self(method, typ) %}); + {%- when None %} + {% call macros::rust_call_with_self(method, typ) %}; + {%- endmatch %} +} +{%- endif %} +{%- endfor %} + +{%- if let Some(method) = uniffi_trait_methods.display_fmt %} +std::string {{ type_name }}::to_string() const { + return uniffi::{{ Type::String.borrow()|lift_fn }}({% call macros::rust_call_with_self(method, typ) %}); +} +{%- endif %} +{%- if let Some(method) = uniffi_trait_methods.debug_fmt %} +std::string {{ type_name }}::to_debug_string() const { + return uniffi::{{ Type::String.borrow()|lift_fn }}({% call macros::rust_call_with_self(method, typ) %}); +} +{%- endif %} +{%- if let Some(method) = uniffi_trait_methods.eq_eq %} +bool {{ type_name }}::eq(const {{ type_name }} &other) const { + return uniffi::{{ Type::Boolean.borrow()|lift_fn }}({% call macros::rust_call_with_self_and_other(method, typ) %}); +} +{%- endif %} +{%- if let Some(method) = uniffi_trait_methods.eq_ne %} +bool {{ type_name }}::ne(const {{ type_name }} &other) const { + return uniffi::{{ Type::Boolean.borrow()|lift_fn }}({% call macros::rust_call_with_self_and_other(method, typ) %}); +} +{%- endif %} +{%- if let Some(method) = uniffi_trait_methods.hash_hash %} +uint64_t {{ type_name }}::hash() const { + return uniffi::{{ Type::UInt64.borrow()|lift_fn }}({% call macros::rust_call_with_self(method, typ) %}); +} +{%- endif %} +{%- if let Some(method) = uniffi_trait_methods.ord_cmp %} +int8_t {{ type_name }}::cmp(const {{ type_name }} &other) const { + return uniffi::{{ Type::Int8.borrow()|lift_fn }}({% call macros::rust_call_with_self_and_other(method, typ) %}); +} +{%- endif %} diff --git a/bindgen/src/bindings/cpp/templates/value_methods.hpp b/bindgen/src/bindings/cpp/templates/value_methods.hpp new file mode 100644 index 0000000..038a826 --- /dev/null +++ b/bindgen/src/bindings/cpp/templates/value_methods.hpp @@ -0,0 +1,42 @@ +{%- for method in methods %} +{%- if !method.is_async() %} +{%- call macros::docstring(method, 4) %} +{% match method.return_type() %}{% when Some with (return_type) %}{{ return_type|type_name(ci) }} {% else %}void {% endmatch -%} +{{ method.name()|fn_name }}({% call macros::param_list(method) %}) const; +{%- endif %} +{%- endfor %} + +{%- if uniffi_trait_methods.display_fmt.is_some() %} + /** + * Returns a string representation of the value, internally calls Rust's `Display` trait. + */ + std::string to_string() const; +{%- endif %} +{%- if uniffi_trait_methods.debug_fmt.is_some() %} + /** + * Returns a string representation of the value, internally calls Rust's `Debug` trait. + */ + std::string to_debug_string() const; +{%- endif %} +{%- if uniffi_trait_methods.eq_eq.is_some() %} + /** + * Equality check, internally calls Rust's `Eq` trait. + */ + bool eq(const {{ type_name }} &other) const; + /** + * Inequality check, internally calls Rust's `Ne` trait. + */ + bool ne(const {{ type_name }} &other) const; +{%- endif %} +{%- if uniffi_trait_methods.hash_hash.is_some() %} + /** + * Returns a hash of the value, internally calls Rust's `Hash` trait. + */ + uint64_t hash() const; +{%- endif %} +{%- if uniffi_trait_methods.ord_cmp.is_some() %} + /** + * Three-way comparison, internally calls Rust's `Ord` trait. + */ + int8_t cmp(const {{ type_name }} &other) const; +{%- endif %} diff --git a/bindgen/src/bindings/cpp/templates/wrapper.cpp b/bindgen/src/bindings/cpp/templates/wrapper.cpp index b3ebc23..2473e82 100644 --- a/bindgen/src/bindings/cpp/templates/wrapper.cpp +++ b/bindgen/src/bindings/cpp/templates/wrapper.cpp @@ -127,6 +127,8 @@ void rust_call_trait_interface_with_error(RustCallStatus* status, F make_call, W {{ type_helper_code }} {%- for func in ci.function_definitions() %} +{%- if !func.is_async() %} {% include "fn_def.cpp" %} +{%- endif %} {% endfor -%} } // namespace {{ namespace }} diff --git a/bindgen/src/bindings/cpp/templates/wrapper.hpp b/bindgen/src/bindings/cpp/templates/wrapper.hpp index 462b5f2..91f1738 100644 --- a/bindgen/src/bindings/cpp/templates/wrapper.hpp +++ b/bindgen/src/bindings/cpp/templates/wrapper.hpp @@ -170,6 +170,7 @@ void rustbuffer_free(RustBuffer); } // namespace uniffi {%~ for func in ci.function_definitions() %} +{%- if !func.is_async() %} {%- call macros::docstring(func, 0) %} {%- match func.return_type() %} {%- when Some with (return_type) %} @@ -177,5 +178,6 @@ void rustbuffer_free(RustBuffer); {%- when None %} void {{ func.name()|fn_name }}({% call macros::param_list(func) %}); {%- endmatch %} +{%- endif %} {%- endfor %} } // namespace {{ namespace }} diff --git a/cpp-tests/tests/coverall/main.cpp b/cpp-tests/tests/coverall/main.cpp index 385a8e6..b2a05a2 100644 --- a/cpp-tests/tests/coverall/main.cpp +++ b/cpp-tests/tests/coverall/main.cpp @@ -447,7 +447,7 @@ void test_path() { ASSERT_EQ(2, traits[1]->strong_count()); traits[0]->set_parent(traits[1]); - ASSERT_EQ(2, traits[1]->strong_count()); + ASSERT_EQ(3, traits[1]->strong_count()); ASSERT_EQ(std::vector {"node-2"} , coverall::ancestor_names(traits[0])); ASSERT_EQ(std::vector {} , coverall::ancestor_names(traits[1])); diff --git a/cpp-tests/tests/enum_style_test/main.cpp b/cpp-tests/tests/enum_style_test/main.cpp index 6c82a36..92d0498 100644 --- a/cpp-tests/tests/enum_style_test/main.cpp +++ b/cpp-tests/tests/enum_style_test/main.cpp @@ -9,5 +9,17 @@ int main() { auto complex = enum_style_test::get_complex_enum(); enum_style_test::set_complex_enum(complex); + auto repeated = enum_style_test::roundtrip_repeated_dependencies({ + enum_style_test::SimpleEnum::VARIANT_ONE, + enum_style_test::SimpleEnum::VARIANT_TWO, + }); + ASSERT_EQ(repeated.first, enum_style_test::SimpleEnum::VARIANT_ONE); + ASSERT_EQ(repeated.second, enum_style_test::SimpleEnum::VARIANT_TWO); + + enum_style_test::OptionalEnumDefault optional_enum_default; + ASSERT_EQ(optional_enum_default.value, enum_style_test::SimpleEnum::VARIANT_ONE); + auto roundtripped = enum_style_test::roundtrip_optional_enum_default(optional_enum_default); + ASSERT_EQ(roundtripped.value, enum_style_test::SimpleEnum::VARIANT_ONE); + return 0; } diff --git a/cpp-tests/tests/trait_methods/main.cpp b/cpp-tests/tests/trait_methods/main.cpp index dc866a6..3c07f9c 100644 --- a/cpp-tests/tests/trait_methods/main.cpp +++ b/cpp-tests/tests/trait_methods/main.cpp @@ -66,9 +66,61 @@ void test_proc_methods() { ASSERT_NE(trait->to_debug_string(), trait2->to_debug_string()); } +void test_record_trait_methods() { + trait_methods::TraitRecord first { "same", 1 }; + trait_methods::TraitRecord second { "same", 2 }; + trait_methods::TraitRecord different { "different", 1 }; + + ASSERT_EQ(first.to_debug_string(), "TraitRecord { s: \"same\", i: 1 }"); + ASSERT_TRUE(first.eq(second)); + ASSERT_FALSE(first.ne(second)); + ASSERT_FALSE(first.eq(different)); + ASSERT_EQ(first.hash(), second.hash()); + ASSERT_EQ(first.cmp(second), 0); +} + +void test_enum_trait_methods() { + trait_methods::TraitEnum first = trait_methods::TraitEnum::kS { "one" }; + trait_methods::TraitEnum second = trait_methods::TraitEnum::kS { "two" }; + trait_methods::TraitEnum integer = trait_methods::TraitEnum::kI { 1 }; + + ASSERT_EQ(first.to_string(), "TraitEnum::S(\"one\")"); + ASSERT_EQ(first.to_debug_string(), "S(\"one\")"); + ASSERT_TRUE(first.eq(second)); + ASSERT_EQ(first.hash(), second.hash()); + ASSERT_TRUE(first.cmp(integer) < 0); + + auto flat = trait_methods::get_flat_trait_enum(0); + ASSERT_EQ(trait_methods::to_string(flat), "FlatTraitEnum::flat-alpha"); + ASSERT_EQ(trait_methods::to_debug_string(flat), "Alpha"); +} + +void test_error_trait_methods() { + try { + trait_methods::throw_api_failure(0); + } catch (const trait_methods::ApiFailure &error) { + ASSERT_EQ(error.to_string(), "api network issue"); + ASSERT_EQ(error.to_debug_string(), "NetworkIssue"); + static_cast(error.hash()); + + try { + trait_methods::throw_api_failure(1); + } catch (const trait_methods::ApiFailure &other) { + ASSERT_FALSE(error.eq(other)); + ASSERT_TRUE(error.ne(other)); + ASSERT_TRUE(error.cmp(other) < 0); + return; + } + } + throw std::runtime_error("Expected ApiFailure"); +} + int main() { test_trait_methods(); test_proc_methods(); + test_record_trait_methods(); + test_enum_trait_methods(); + test_error_trait_methods(); return 0; -} \ No newline at end of file +} diff --git a/cpp-tests/tests/type_flattening/main.cpp b/cpp-tests/tests/type_flattening/main.cpp index 7069da3..6acbc65 100644 --- a/cpp-tests/tests/type_flattening/main.cpp +++ b/cpp-tests/tests/type_flattening/main.cpp @@ -8,10 +8,35 @@ int main() { auto structure = type_flattening::get_struct(value); ASSERT_NE(structure.optional_arc, nullptr); ASSERT_EQ(structure.optional_arc->get_value(), value); + ASSERT_TRUE(structure.has_object()); auto roundtrip = type_flattening::struct_roundtrip(structure); ASSERT_NE(roundtrip.optional_arc, nullptr); ASSERT_EQ(roundtrip.optional_arc->get_value(), value); + ASSERT_EQ(structure.adjusted_value(8), 50); + try { + structure.adjusted_value(-1); + ASSERT_TRUE(false); + } catch (const type_flattening::adjustment_error::NegativeAdjustment& error) { + ASSERT_TRUE(error.is_negative()); + ASSERT_EQ(error.adjustment, -1); + } + + auto next = type_flattening::next(type_flattening::FlatValue::kFirst); + ASSERT_EQ(next, type_flattening::FlatValue::kSecond); + + ASSERT_TRUE(type_flattening::eq(type_flattening::FlatValue::kFirst, + type_flattening::FlatValue::kFirst)); + ASSERT_TRUE(type_flattening::ne(type_flattening::FlatValue::kFirst, + type_flattening::FlatValue::kSecond)); + ASSERT_EQ(type_flattening::hash(type_flattening::FlatValue::kFirst), + type_flattening::hash(type_flattening::FlatValue::kFirst)); + ASSERT_TRUE(type_flattening::cmp(type_flattening::FlatValue::kFirst, + type_flattening::FlatValue::kSecond) < 0); + + type_flattening::RichValue rich = type_flattening::RichValue::kText { "hello" }; + ASSERT_EQ(rich.describe(), "text:hello"); + return 0; } diff --git a/fixtures/Cargo.toml b/fixtures/Cargo.toml index 476486c..197551d 100644 --- a/fixtures/Cargo.toml +++ b/fixtures/Cargo.toml @@ -7,19 +7,19 @@ edition = "2021" crate-type = ["cdylib", "lib", "staticlib"] [dependencies] -uniffi-example-arithmetic = { git = "https://github.com/mozilla/uniffi-rs.git", tag = "v0.29.4" } -uniffi-example-callbacks = { git = "https://github.com/mozilla/uniffi-rs.git", tag = "v0.29.4" } -uniffi-example-custom-types = { git = "https://github.com/mozilla/uniffi-rs.git", tag = "v0.29.4" } -uniffi-example-geometry = { git = "https://github.com/mozilla/uniffi-rs.git", tag = "v0.29.4" } -uniffi-example-rondpoint = { git = "https://github.com/mozilla/uniffi-rs.git", tag = "v0.29.4" } -uniffi-example-sprites = { git = "https://github.com/mozilla/uniffi-rs.git", tag = "v0.29.4" } -uniffi-example-todolist = { git = "https://github.com/mozilla/uniffi-rs.git", tag = "v0.29.4" } -uniffi-example-traits = { git = "https://github.com/mozilla/uniffi-rs.git", tag = "v0.29.4" } -uniffi-fixture-callbacks = { git = "https://github.com/mozilla/uniffi-rs.git", tag = "v0.29.4" } -uniffi-fixture-coverall = { git = "https://github.com/mozilla/uniffi-rs.git", tag = "v0.29.4" } -uniffi-fixture-docstring = { git = "https://github.com/mozilla/uniffi-rs.git", tag = "v0.29.4" } -uniffi-fixture-time = { git = "https://github.com/mozilla/uniffi-rs.git", tag = "v0.29.4" } -uniffi-fixture-trait-methods = { git = "https://github.com/mozilla/uniffi-rs.git", tag = "v0.29.4" } +uniffi-example-arithmetic = { git = "https://github.com/mozilla/uniffi-rs.git", tag = "v0.31.2" } +uniffi-example-callbacks = { git = "https://github.com/mozilla/uniffi-rs.git", tag = "v0.31.2" } +uniffi-example-custom-types = { git = "https://github.com/mozilla/uniffi-rs.git", tag = "v0.31.2" } +uniffi-example-geometry = { git = "https://github.com/mozilla/uniffi-rs.git", tag = "v0.31.2" } +uniffi-example-rondpoint = { git = "https://github.com/mozilla/uniffi-rs.git", tag = "v0.31.2" } +uniffi-example-sprites = { git = "https://github.com/mozilla/uniffi-rs.git", tag = "v0.31.2" } +uniffi-example-todolist = { git = "https://github.com/mozilla/uniffi-rs.git", tag = "v0.31.2" } +uniffi-example-traits = { git = "https://github.com/mozilla/uniffi-rs.git", tag = "v0.31.2" } +uniffi-fixture-callbacks = { git = "https://github.com/mozilla/uniffi-rs.git", tag = "v0.31.2" } +uniffi-fixture-coverall = { git = "https://github.com/mozilla/uniffi-rs.git", tag = "v0.31.2" } +uniffi-fixture-docstring = { git = "https://github.com/mozilla/uniffi-rs.git", tag = "v0.31.2" } +uniffi-fixture-time = { git = "https://github.com/mozilla/uniffi-rs.git", tag = "v0.31.2" } +uniffi-fixture-trait-methods = { git = "https://github.com/mozilla/uniffi-rs.git", tag = "v0.31.2" } uniffi-error-types-builtin = { path = "error-types-builtin" } uniffi-custom-types-builtin = { path = "custom-types-builtin" } diff --git a/fixtures/enum-style-test/src/enum_style_test.udl b/fixtures/enum-style-test/src/enum_style_test.udl index b805102..5366b26 100644 --- a/fixtures/enum-style-test/src/enum_style_test.udl +++ b/fixtures/enum-style-test/src/enum_style_test.udl @@ -10,10 +10,24 @@ interface ComplexEnum { VariantTwo(f32 flt); }; +// Repeating the same named dependency used to make the C++ topological sorter +// emit that dependency's definition more than once. +dictionary RepeatedDependencies { + SimpleEnum first; + SimpleEnum second; +}; + +dictionary OptionalEnumDefault { + SimpleEnum? value = "VariantOne"; +}; + namespace enum_style_test { SimpleEnum get_simple_enum(); void set_simple_enum(SimpleEnum e); ComplexEnum get_complex_enum(); void set_complex_enum(ComplexEnum e); + + RepeatedDependencies roundtrip_repeated_dependencies(RepeatedDependencies value); + OptionalEnumDefault roundtrip_optional_enum_default(OptionalEnumDefault value); }; diff --git a/fixtures/enum-style-test/src/lib.rs b/fixtures/enum-style-test/src/lib.rs index 067a6e7..44d3c30 100644 --- a/fixtures/enum-style-test/src/lib.rs +++ b/fixtures/enum-style-test/src/lib.rs @@ -9,6 +9,15 @@ enum ComplexEnum { VariantTwo { flt: f32 }, } +struct RepeatedDependencies { + first: SimpleEnum, + second: SimpleEnum, +} + +struct OptionalEnumDefault { + value: Option, +} + fn get_simple_enum() -> SimpleEnum { SimpleEnum::VariantOne } @@ -21,4 +30,12 @@ fn get_complex_enum() -> ComplexEnum { fn set_complex_enum(_: ComplexEnum) {} +fn roundtrip_repeated_dependencies(value: RepeatedDependencies) -> RepeatedDependencies { + value +} + +fn roundtrip_optional_enum_default(value: OptionalEnumDefault) -> OptionalEnumDefault { + value +} + uniffi::include_scaffolding!("enum_style_test"); diff --git a/fixtures/ext-types-export/src/lib.rs b/fixtures/ext-types-export/src/lib.rs index 593d6cf..b411b96 100644 --- a/fixtures/ext-types-export/src/lib.rs +++ b/fixtures/ext-types-export/src/lib.rs @@ -32,6 +32,7 @@ impl Counter { self.value.load(std::sync::atomic::Ordering::SeqCst) } } +#[uniffi::trait_interface] pub trait Greeter: Send + Sync { fn greet(&self) -> String; } @@ -41,6 +42,7 @@ pub enum Shape { Rectangle { width: f64, height: f64 }, } +#[uniffi::trait_interface] pub trait Vehicle: Send + Sync { fn year(&self) -> i32; } diff --git a/fixtures/type-flattening/src/lib.rs b/fixtures/type-flattening/src/lib.rs index ce66939..3024ec0 100644 --- a/fixtures/type-flattening/src/lib.rs +++ b/fixtures/type-flattening/src/lib.rs @@ -18,6 +18,81 @@ pub struct Structure { pub optional_arc: Option>, } +#[uniffi::export] +impl Structure { + pub fn has_object(&self) -> bool { + self.optional_arc.is_some() + } + + pub fn adjusted_value(&self, adjustment: i32) -> Result { + if adjustment < 0 { + return Err(AdjustmentError::NegativeAdjustment { adjustment }); + } + + Ok(self + .optional_arc + .as_ref() + .map_or(adjustment, |object| object.value + adjustment)) + } +} + +#[derive(uniffi::Enum, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[uniffi::export(Eq, Ord, Hash)] +pub enum FlatValue { + First, + Second, +} + +#[uniffi::export] +impl FlatValue { + pub fn next(&self) -> Self { + match self { + Self::First => Self::Second, + Self::Second => Self::First, + } + } +} + +#[derive(uniffi::Enum)] +pub enum RichValue { + Text(String), + Number(i32), +} + +#[uniffi::export] +impl RichValue { + pub fn describe(&self) -> String { + match self { + Self::Text(value) => format!("text:{value}"), + Self::Number(value) => format!("number:{value}"), + } + } +} + +#[derive(Debug, uniffi::Error)] +pub enum AdjustmentError { + NegativeAdjustment { adjustment: i32 }, +} + +impl std::fmt::Display for AdjustmentError { + fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::NegativeAdjustment { adjustment } => { + write!(formatter, "negative adjustment: {adjustment}") + } + } + } +} + +impl std::error::Error for AdjustmentError {} + +#[uniffi::export] +impl AdjustmentError { + pub fn is_negative(&self) -> bool { + matches!(self, Self::NegativeAdjustment { .. }) + } +} + #[uniffi::export] pub fn get_struct(value: i32) -> Structure { Structure {