diff --git a/Cargo.lock b/Cargo.lock index 6462518..1e10454 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -214,6 +214,12 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" +[[package]] +name = "either" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" + [[package]] name = "farm" version = "0.1.0" @@ -254,12 +260,27 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + [[package]] name = "is_terminal_polyfill" version = "1.70.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.11" @@ -271,6 +292,7 @@ name = "kitchen" version = "0.1.0" dependencies = [ "farm", + "money", "uniffi", ] @@ -344,6 +366,8 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" name = "money" version = "0.1.0" dependencies = [ + "hex", + "itertools", "uniffi", ] @@ -363,6 +387,12 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "oneshot-uniffi" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c548d5c78976f6955d72d0ced18c48ca07030f7a1d4024529fedd7c1c01b29c" + [[package]] name = "paste" version = "1.0.15" @@ -559,7 +589,7 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "uniffi" version = "0.27.1" -source = "git+https://github.com/mozilla/uniffi-rs/?rev=aa338ccb435d81d36f7adef8954bcffebdd4a7d1#aa338ccb435d81d36f7adef8954bcffebdd4a7d1" +source = "git+https://github.com/bendk/uniffi-rs/?rev=4b65c3796bc3743404e1ee2a7b4ab51e24589aa6#4b65c3796bc3743404e1ee2a7b4ab51e24589aa6" dependencies = [ "anyhow", "camino", @@ -573,12 +603,13 @@ dependencies = [ [[package]] name = "uniffi_bindgen" version = "0.27.1" -source = "git+https://github.com/mozilla/uniffi-rs/?rev=aa338ccb435d81d36f7adef8954bcffebdd4a7d1#aa338ccb435d81d36f7adef8954bcffebdd4a7d1" +source = "git+https://github.com/bendk/uniffi-rs/?rev=4b65c3796bc3743404e1ee2a7b4ab51e24589aa6#4b65c3796bc3743404e1ee2a7b4ab51e24589aa6" dependencies = [ "anyhow", "askama", "camino", "cargo_metadata", + "clap", "fs-err", "glob", "goblin", @@ -596,7 +627,7 @@ dependencies = [ [[package]] name = "uniffi_build" version = "0.27.1" -source = "git+https://github.com/mozilla/uniffi-rs/?rev=aa338ccb435d81d36f7adef8954bcffebdd4a7d1#aa338ccb435d81d36f7adef8954bcffebdd4a7d1" +source = "git+https://github.com/bendk/uniffi-rs/?rev=4b65c3796bc3743404e1ee2a7b4ab51e24589aa6#4b65c3796bc3743404e1ee2a7b4ab51e24589aa6" dependencies = [ "anyhow", "camino", @@ -606,7 +637,7 @@ dependencies = [ [[package]] name = "uniffi_checksum_derive" version = "0.27.1" -source = "git+https://github.com/mozilla/uniffi-rs/?rev=aa338ccb435d81d36f7adef8954bcffebdd4a7d1#aa338ccb435d81d36f7adef8954bcffebdd4a7d1" +source = "git+https://github.com/bendk/uniffi-rs/?rev=4b65c3796bc3743404e1ee2a7b4ab51e24589aa6#4b65c3796bc3743404e1ee2a7b4ab51e24589aa6" dependencies = [ "quote", "syn", @@ -615,13 +646,14 @@ dependencies = [ [[package]] name = "uniffi_core" version = "0.27.1" -source = "git+https://github.com/mozilla/uniffi-rs/?rev=aa338ccb435d81d36f7adef8954bcffebdd4a7d1#aa338ccb435d81d36f7adef8954bcffebdd4a7d1" +source = "git+https://github.com/bendk/uniffi-rs/?rev=4b65c3796bc3743404e1ee2a7b4ab51e24589aa6#4b65c3796bc3743404e1ee2a7b4ab51e24589aa6" dependencies = [ "anyhow", "bytes", "camino", "log", "once_cell", + "oneshot-uniffi", "paste", "static_assertions", ] @@ -629,7 +661,7 @@ dependencies = [ [[package]] name = "uniffi_macros" version = "0.27.1" -source = "git+https://github.com/mozilla/uniffi-rs/?rev=aa338ccb435d81d36f7adef8954bcffebdd4a7d1#aa338ccb435d81d36f7adef8954bcffebdd4a7d1" +source = "git+https://github.com/bendk/uniffi-rs/?rev=4b65c3796bc3743404e1ee2a7b4ab51e24589aa6#4b65c3796bc3743404e1ee2a7b4ab51e24589aa6" dependencies = [ "bincode", "camino", @@ -646,7 +678,7 @@ dependencies = [ [[package]] name = "uniffi_meta" version = "0.27.1" -source = "git+https://github.com/mozilla/uniffi-rs/?rev=aa338ccb435d81d36f7adef8954bcffebdd4a7d1#aa338ccb435d81d36f7adef8954bcffebdd4a7d1" +source = "git+https://github.com/bendk/uniffi-rs/?rev=4b65c3796bc3743404e1ee2a7b4ab51e24589aa6#4b65c3796bc3743404e1ee2a7b4ab51e24589aa6" dependencies = [ "anyhow", "bytes", @@ -657,7 +689,7 @@ dependencies = [ [[package]] name = "uniffi_testing" version = "0.27.1" -source = "git+https://github.com/mozilla/uniffi-rs/?rev=aa338ccb435d81d36f7adef8954bcffebdd4a7d1#aa338ccb435d81d36f7adef8954bcffebdd4a7d1" +source = "git+https://github.com/bendk/uniffi-rs/?rev=4b65c3796bc3743404e1ee2a7b4ab51e24589aa6#4b65c3796bc3743404e1ee2a7b4ab51e24589aa6" dependencies = [ "anyhow", "camino", @@ -669,7 +701,7 @@ dependencies = [ [[package]] name = "uniffi_udl" version = "0.27.1" -source = "git+https://github.com/mozilla/uniffi-rs/?rev=aa338ccb435d81d36f7adef8954bcffebdd4a7d1#aa338ccb435d81d36f7adef8954bcffebdd4a7d1" +source = "git+https://github.com/bendk/uniffi-rs/?rev=4b65c3796bc3743404e1ee2a7b4ab51e24589aa6#4b65c3796bc3743404e1ee2a7b4ab51e24589aa6" dependencies = [ "anyhow", "textwrap", @@ -693,7 +725,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "weedle2" version = "5.0.0" -source = "git+https://github.com/mozilla/uniffi-rs/?rev=aa338ccb435d81d36f7adef8954bcffebdd4a7d1#aa338ccb435d81d36f7adef8954bcffebdd4a7d1" +source = "git+https://github.com/bendk/uniffi-rs/?rev=4b65c3796bc3743404e1ee2a7b4ab51e24589aa6#4b65c3796bc3743404e1ee2a7b4ab51e24589aa6" dependencies = [ "nom", ] diff --git a/Cargo.toml b/Cargo.toml index f4622fb..a303507 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ members = [ ] [workspace.dependencies] -uniffi = { git = "https://github.com/mozilla/uniffi-rs/", rev = "aa338ccb435d81d36f7adef8954bcffebdd4a7d1" } +uniffi = { git = "https://github.com/bendk/uniffi-rs/", rev = "4b65c3796bc3743404e1ee2a7b4ab51e24589aa6" } [profile.release] incremental = false diff --git a/crates/chef/src/chef.udl b/crates/chef/src/chef.udl index c594cc3..386eefe 100644 --- a/crates/chef/src/chef.udl +++ b/crates/chef/src/chef.udl @@ -1 +1 @@ -namespace chef {}; \ No newline at end of file +namespace chef {}; diff --git a/crates/chef/src/models.rs b/crates/chef/src/models.rs index eea3cf4..ef41832 100644 --- a/crates/chef/src/models.rs +++ b/crates/chef/src/models.rs @@ -7,6 +7,7 @@ use std::ops::AddAssign; pub struct Chef { pub name: String, pub money: RwLock, + pub bag_of_bytes: BagOfBytes, } #[uniffi::export] @@ -16,6 +17,7 @@ impl Chef { Arc::new(Self { name, money: RwLock::new(money), + bag_of_bytes: BagOfBytes::from(vec![0xde, 0xad, 0xbe, 0xef]), }) } diff --git a/crates/kitchen/Cargo.toml b/crates/kitchen/Cargo.toml index 7a6eff3..a4e7760 100644 --- a/crates/kitchen/Cargo.toml +++ b/crates/kitchen/Cargo.toml @@ -6,6 +6,7 @@ build = "build.rs" [dependencies] uniffi = { workspace = true, features = ["cli"] } +money = { path = "../money" } farm = { path = "../farm" } [build-dependencies] diff --git a/crates/kitchen/src/lib.rs b/crates/kitchen/src/lib.rs index caf8578..bfabfed 100644 --- a/crates/kitchen/src/lib.rs +++ b/crates/kitchen/src/lib.rs @@ -4,6 +4,7 @@ pub mod prelude { pub use crate::models::*; pub(crate) use farm::prelude::*; + pub(crate) use money::prelude::*; } pub use prelude::*; diff --git a/crates/kitchen/src/models.rs b/crates/kitchen/src/models.rs index 1fc0394..eeb5f03 100644 --- a/crates/kitchen/src/models.rs +++ b/crates/kitchen/src/models.rs @@ -1,10 +1,11 @@ use crate::prelude::*; -#[derive(Default, Clone, Copy, Debug, PartialEq, Eq, uniffi::Record)] +#[derive(Default, Clone, Debug, PartialEq, Eq, uniffi::Record)] pub struct Fridge { pub egg: EggBox, pub butter: ButterBlock, pub lemons: Lemons, + pub bag_of_bytes: BagOfBytes, } impl Fridge { pub fn stock_with(produce: farm::Produce) -> Self { @@ -12,6 +13,7 @@ impl Fridge { egg: EggBox::from(produce.clone()), butter: ButterBlock::from(produce.clone()), lemons: Lemons::from(produce.clone()), + bag_of_bytes: BagOfBytes::from(vec![0xde, 0xad, 0xbe, 0xef]), } } } diff --git a/crates/lemon_meringue_pie/tests/bindings/pie_baking.kts b/crates/lemon_meringue_pie/tests/bindings/pie_baking.kts index 7c70eeb..7016f25 100644 --- a/crates/lemon_meringue_pie/tests/bindings/pie_baking.kts +++ b/crates/lemon_meringue_pie/tests/bindings/pie_baking.kts @@ -7,7 +7,7 @@ import com.sajjon.meringue.* import com.sajjon.lemon.filling.* import com.sajjon.kitchen.* -fun test() { +fun test_bake_pie() { val chef = Chef(name = "Auguste Gusteau", money = Money(amount = 50u)) val farm = Farm(money = Money(amount = 200u)) val produce = farm.produce() @@ -23,4 +23,34 @@ fun test() { assert(farm.balance() == 175.toULong()) } +fun test_bag_of_bytes() { + val f0 = Fridge( + egg = EggBox.TWELVE, + butter = ButterBlock(weight = 1u), + lemons = Lemons(count = 1u), + bagOfBytes = listOf(1.toUByte()) + ) + val f1 = Fridge( + egg = EggBox.TWELVE, + butter = ButterBlock(weight = 1u), + lemons = Lemons(count = 1u), + bagOfBytes = listOf(1.toUByte()) + ) + assert(f0 == f1) + + var x = newBagOfBytesFromHexString(hex = "adbe") + var y = newBagOfBytesFromHexString(hex = "adbe") + x = newBagOfBytesPrependDe(bagOfBytes = x) + x = newBagOfBytesAppendEf(bagOfBytes = x) + y = newBagOfBytesAppendEf(bagOfBytes = y) + y = newBagOfBytesPrependDe(bagOfBytes = y) + assert(x == y) + assert(bagOfBytesToHexString(bagOfBytes = x) == "deadbeef") +} + +fun test() { + test_bake_pie() + test_bag_of_bytes() +} + test() \ No newline at end of file diff --git a/crates/money/Cargo.toml b/crates/money/Cargo.toml index 2a688ae..8d78999 100644 --- a/crates/money/Cargo.toml +++ b/crates/money/Cargo.toml @@ -5,6 +5,8 @@ edition = "2021" build = "build.rs" [dependencies] +itertools = "0.13.0" +hex = "0.4.3" uniffi = { workspace = true, features = ["cli"] } [build-dependencies] diff --git a/crates/money/src/models.rs b/crates/money/src/models.rs index b1df354..cbe05fc 100644 --- a/crates/money/src/models.rs +++ b/crates/money/src/models.rs @@ -1,5 +1,116 @@ +use itertools::*; use std::ops::{AddAssign, SubAssign}; +#[derive(Clone, Debug, PartialEq, Eq, Default)] +pub struct BagOfBytes { + pub bytes: Vec, +} + +#[uniffi::export] +pub fn bag_of_bytes_to_hex_string(bag_of_bytes: BagOfBytes) -> String { + hex::encode(bag_of_bytes.bytes) +} + +/// # Panics +/// Panics if hex is not a valid hex string +#[uniffi::export] +pub fn new_bag_of_bytes_from_hex_string(hex: String) -> BagOfBytes { + BagOfBytes { + bytes: hex::decode(hex).unwrap(), + } +} + +#[uniffi::export] +pub fn new_bag_of_bytes_prepend_de(bag_of_bytes: BagOfBytes) -> BagOfBytes { + let mut vec = bag_of_bytes.bytes; + vec.insert(0, 0xde); + BagOfBytes { bytes: vec } +} + +#[uniffi::export] +pub fn new_bag_of_bytes_append_ef(bag_of_bytes: BagOfBytes) -> BagOfBytes { + let mut vec = bag_of_bytes.bytes; + vec.push(0xef); + BagOfBytes { bytes: vec } +} + +impl BagOfBytes { + pub fn len(&self) -> usize { + self.bytes.len() + } + + pub fn is_empty(&self) -> bool { + self.bytes.is_empty() + } + + /// Returns a clone of the inner bytes as a `Vec`. + pub fn to_vec(&self) -> Vec { + Vec::from(self.bytes()) + } + + /// Returns a references to the inner array slice. + pub fn bytes(&self) -> &[u8] { + &self.bytes + } +} + +fn twos_complement_of_u8(u: u8) -> i8 { + // Yes, it is this easy, Rust does all the heavy lifting + u as i8 +} + +fn twos_complement_of_i8(i: i8) -> u8 { + // Yes, it is this easy, Rust does all the heavy lifting + i as u8 +} + +impl From> for BagOfBytes { + fn from(value: Vec) -> Self { + Self { bytes: value } + } +} + +impl From<&[u8]> for BagOfBytes { + /// Instantiates a new `BagOfBytes` from the bytes. + fn from(value: &[u8]) -> Self { + Self { + bytes: value.to_vec(), + } + } +} + +/* + Expose `BagOfBytes` to Uniffi as `sequence`, unfortunately we cannot + use `sequence` because it results in: + + /uniffi-rs-6f89edd2a1ffa4bd/fb8dd5c/uniffi_bindgen/src/interface/universe.rs:50:17: + assertion `left == right` failed + left: Custom { module_path: "profile", name: "BagOfBytes", builtin: Bytes } + right: Custom { module_path: "profile", name: "BagOfBytes", builtin: Sequence { inner_type: UInt8 } } + + So HACK HACK HACK we use `sequence` (`Vec`) instead as an intermediary `Builtin`. + + However, in `uniffi.toml` we provide `from_custom`` / `into_custom`` for Kotlin and Swift + which using two's complement maps back Vec -> Vec, meaning Kotlin and Swift actually + never see the `i8`, and only works with u8. + + So we translate: + Kotlin: `Rust[BagOfBytes <:2's comp.:> Vec] <:2's comp:> [Kotlin]List` + Swift: `Rust[BagOfBytes <:2's comp.:> Vec] <:2's comp:> [Swift]Foundation.Data` + +*/ +uniffi::custom_type!(BagOfBytes, Vec, { + from_custom: |s| s.to_vec() + .into_iter() + .map(twos_complement_of_u8) + .collect_vec(), + try_into_custom: |s| Ok(s + .into_iter() + .map(twos_complement_of_i8) + .collect_vec() + .into()), +}); + #[derive(uniffi::Record, Default, Clone, PartialEq, Eq, Copy, Debug)] pub struct Money { pub amount: u64, @@ -34,3 +145,16 @@ impl SubAssign for Money { self.sub_assign(rhs.amount) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_bag_of_bytes() { + let mut x = new_bag_of_bytes_from_hex_string("adbe".to_owned()); + x = new_bag_of_bytes_append_ef(x); + x = new_bag_of_bytes_prepend_de(x); + assert_eq!(bag_of_bytes_to_hex_string(x), "deadbeef".to_owned()); + } +} diff --git a/crates/money/src/money.udl b/crates/money/src/money.udl index 926def9..584341c 100644 --- a/crates/money/src/money.udl +++ b/crates/money/src/money.udl @@ -1 +1,10 @@ -namespace money {}; \ No newline at end of file +namespace money {}; + +// HERE BE DRAGONS +// Due to Kotlin equals being broken for ByteArray +// Which otherwise UniFFI converts `Vec` to, we MUST use +// our own "bag of bytes" which we convert to a Kotlin `List` +// which DOES have a working equals! +// HERE BE DRAGONS +[Custom] +typedef sequence BagOfBytes; \ No newline at end of file diff --git a/crates/money/uniffi.toml b/crates/money/uniffi.toml index 31a3586..e62574b 100644 --- a/crates/money/uniffi.toml +++ b/crates/money/uniffi.toml @@ -2,3 +2,16 @@ namespace = "money" [bindings.kotlin] package_name = "com.sajjon.money" + + +[bindings.swift.custom_types.BagOfBytes] +type_name = "Data" +imports = ["Foundation"] +into_custom = "{ Data({}.map({ i8 in UInt8(bitPattern: i8) })) }()" +from_custom = "{ {}.map({ u8 in Int8(bitPattern: u8) }) }()" + +[bindings.kotlin.custom_types.BagOfBytes] +type_name = "List" +imports = [] +into_custom = "{}.map({ it.toUByte() })" +from_custom = "{}.map({ it.toByte() })"