From 5ccf5879fa9d706e5ce665b9aee1028f6d399224 Mon Sep 17 00:00:00 2001 From: Martin Geisler Date: Mon, 24 Jun 2024 11:56:07 +0200 Subject: [PATCH 01/62] Clarify pointer alignment for `Handle` Fixes #2165. --- uniffi_core/src/ffi/handle.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uniffi_core/src/ffi/handle.rs b/uniffi_core/src/ffi/handle.rs index 8ee2f46c35..b56c16d538 100644 --- a/uniffi_core/src/ffi/handle.rs +++ b/uniffi_core/src/ffi/handle.rs @@ -12,7 +12,7 @@ /// For all currently supported architectures and hopefully any ones we add in the future: /// * 0 is an invalid value. /// * The lowest bit will always be set for foreign handles and never set for Rust ones (since the -/// leaked pointer will be aligned). +/// leaked pointer will be aligned to `size_of::>()` == `size_of::<*const T>()`). /// /// Rust handles are mainly managed is through the [crate::HandleAlloc] trait. #[derive(Copy, Clone, Default, Debug, PartialEq, Eq)] From 6e0e4e4b946564f821b20b34dc8fcd23ad85de62 Mon Sep 17 00:00:00 2001 From: Andre Kazmierczak Date: Fri, 28 Jun 2024 21:37:49 +0200 Subject: [PATCH 02/62] Swift: Use bytesNoCopy initializer for conversion between RustBuffer and Data --- .../src/bindings/swift/templates/RustBufferTemplate.swift | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/uniffi_bindgen/src/bindings/swift/templates/RustBufferTemplate.swift b/uniffi_bindgen/src/bindings/swift/templates/RustBufferTemplate.swift index e36feb0010..cf8eec4019 100644 --- a/uniffi_bindgen/src/bindings/swift/templates/RustBufferTemplate.swift +++ b/uniffi_bindgen/src/bindings/swift/templates/RustBufferTemplate.swift @@ -37,9 +37,11 @@ fileprivate extension ForeignBytes { fileprivate extension Data { init(rustBuffer: RustBuffer) { - // TODO: This copies the buffer. Can we read directly from a - // Rust buffer? - self.init(bytes: rustBuffer.data!, count: Int(rustBuffer.len)) + self.init( + bytesNoCopy: rustBuffer.data!, + count: Int(rustBuffer.len), + deallocator: .none + ) } } From 5deff4cb116c4097ff0674fbf43d7cf3e3e7e062 Mon Sep 17 00:00:00 2001 From: Martin Geisler Date: Fri, 21 Jun 2024 16:49:24 +0200 Subject: [PATCH 03/62] Make `RustBuffer::from_raw_parts` comment more precise The method is used from outside of this module, so the comment seems unnecessary: the method should either be private or it should be public. --- uniffi_core/src/ffi/rustbuffer.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/uniffi_core/src/ffi/rustbuffer.rs b/uniffi_core/src/ffi/rustbuffer.rs index 47eafbdb96..7044861dab 100644 --- a/uniffi_core/src/ffi/rustbuffer.rs +++ b/uniffi_core/src/ffi/rustbuffer.rs @@ -78,9 +78,6 @@ impl RustBuffer { /// Creates a `RustBuffer` from its constituent fields. /// - /// This is intended mainly as an internal convenience function and should not - /// be used outside of this module. - /// /// # Safety /// /// You must ensure that the raw parts uphold the documented invariants of this class. From 1575a2019c26271ec82c66477c8986f3088e9794 Mon Sep 17 00:00:00 2001 From: Martin Geisler Date: Sat, 22 Jun 2024 11:24:34 +0200 Subject: [PATCH 04/62] Limit `RustBuffer::from_raw_parts` visibility to `pub(crate)` The method is not meant to be used outside of the crate. --- uniffi_core/src/ffi/rustbuffer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uniffi_core/src/ffi/rustbuffer.rs b/uniffi_core/src/ffi/rustbuffer.rs index 7044861dab..9c718aa611 100644 --- a/uniffi_core/src/ffi/rustbuffer.rs +++ b/uniffi_core/src/ffi/rustbuffer.rs @@ -81,7 +81,7 @@ impl RustBuffer { /// # Safety /// /// You must ensure that the raw parts uphold the documented invariants of this class. - pub unsafe fn from_raw_parts(data: *mut u8, len: u64, capacity: u64) -> Self { + pub(crate) unsafe fn from_raw_parts(data: *mut u8, len: u64, capacity: u64) -> Self { Self { capacity, len, From a62f937e72de9d9b3a4811d1d0d855a55a595572 Mon Sep 17 00:00:00 2001 From: Ben Dean-Kawamura Date: Fri, 24 May 2024 19:39:36 -0400 Subject: [PATCH 05/62] Remote types: interfaces and proc macros ADR --- ...remote-types-interfaces-and-proc-macros.md | 275 ++++++++++++++++++ 1 file changed, 275 insertions(+) create mode 100644 docs/adr/0009-remote-types-interfaces-and-proc-macros.md diff --git a/docs/adr/0009-remote-types-interfaces-and-proc-macros.md b/docs/adr/0009-remote-types-interfaces-and-proc-macros.md new file mode 100644 index 0000000000..a46157f458 --- /dev/null +++ b/docs/adr/0009-remote-types-interfaces-and-proc-macros.md @@ -0,0 +1,275 @@ +# Remote types: proc-macros and interfaces + +* Status: proposed +* Deciders: Ben Dean-Kawamura, Mark Hammond, Jonas Platte, Alexander Cyon +* Date: 2024-05-24 + +Discussion and approval: [PR 2130](https://github.com/mozilla/uniffi-rs/pull/2130) + +## Context and Problem Statement + +We want to expose APIs which are described naturally in our Rust implementation. For example, + +```rust +fn set_level(level: log::Level) -> Result<(), anyhow::Error> { ... } +``` + +(or maybe `serde::Value` etc) - things naturally expressed by our implementation. + +These `Error/Level/Value`s are "remote types" -- types defined in 3rd-party crates -- and so require special handling from UniFFI. + +One reason, discussed in ADR-0006, is the Rust orphan rule, but the more fundamental reason +is that UniFFI needs to know about the enough about the type to generate the FFI. +As we will discuss, UDL helps with metadata collection, but that still leaves proc-macros. + +This ADR will explore: + - Adding support for collecting this metadata for proc-macro-based generation + - Adding interface type support to both UDL and proc-macros. + +## The current state + +UniFFI currently supports re-declaring remote records/enums in UDL files using the normal syntax. +For example, users can use `Log::Level` in their interface by creating a type alias `type LogLevel = log::Level`, then adding this definition to the UDL: + + +```idl +enum LogLevel { + "Error", + "Warn", + "Info", + "Debug", + "Trace", +} +``` + +UniFFI exposed functions/structs/etc could then use `log::Level` as a param/struct member/etc directly in the API. + +Proc-macros obviously can't arrange for a `#[derive(uniffi::Enum)]]` around `log::Level`, +or a `#[uniffi::export]` around `anyhow::Error`, but we want some way of making that work. + +## Considered Options + +### [Option 1] expose remote types directly + +We could continue to expose remote types directly, similar to how it currently works in UDL. +One issue here is that proc-macro generation is based attributes that wrap an item, however there's no way for a user to add an attribute to a remote type. +However, macros can work around this issue. + +```rust +type LogLevel = log::Level; + +#[uniffi::remote] +pub enum LogLevel { + Error = 1, + Warn = 2, + Info = 3, + Debug = 4, + Trace = 5, +} +``` + +The `remote` macro would generate all scaffolding code needed to handle `LogLevel`. +The `enum LogLevel` item would not end up in the expanded code. + +This could also work for interfaces: + +```rust +type AnyhowError = anyhow::Error; + +#[uniffi::remote] +impl AnyhowError { + // Expose the `to_string` method (technically, `to_string` comes from the `Display` trait, but that + // doesn't matter for foreign consumers. Since the item definition is not used for the + // scaffolding code and will not be present in the expanded code, it can be left empty. + pub fn to_string(&self) -> String; +} +``` + +One issue with this approach is that most methods defined by the source crate will not be compatible with UniFFI. +To get around this, UniFFI will allow defining extra methods for the FFI: + +```rust +type AnyhowError = anyhow::Error; + +#[uniffi::remote] +impl AnyhowError { + // [anyhow::Error::is] is a generic method, which can't be exported by UniFFI. + // Instead, define extra FFI methods which forward to the original method. + // This code will result in extra scaffolding methods being generated that can be called from foreign languages. + // These methods will not be callable from Rust. + + fn is_foo_error(&self) -> bool { + self.is::() + } + + fn is_bar_error(&self) -> bool { + self.is::() + } +} +``` + +### [Option 1a] use a function-style macro + +The same idea could also be spelled out using a function-style macro rather than an attribute macro: + +```rust +type LogLevel = log::Level; + +uniffi::remote!( + pub enum LogLevel { + Error = 1, + Warn = 2, + Info = 3, + Debug = 4, + Trace = 5, + } +); + +type AnyhowError = anyhow::Error; + +uniffi::remote!( + impl AnyhowError { + pub fn to_string(&self) -> String; + + fn is_foo_error(&self) -> bool { + self.is::() + } + + fn is_bar_error(&self) -> bool { + self.is::() + } + } +); +``` + +### [Option 2] use custom-type conversion to expose the type + +An alternate strategy would be to use a custom-type conversion from that type into a local type that does implement the UniFFI traits. +These examples will use the custom type syntax from #2087, since I think it looks nicer than the current `UniffiCustomTypeConverter` based code. + +```rust +/// Define a type that mirrors `Log::Level` +#[derive(uniffi::Enum)] +pub enum LogLevel { + Error = 1, + Warn = 2, + Info = 3, + Debug = 4, + Trace = 5, +} + +/// Define a custom type conversion from `log::Level` to the above type. +uniffi::custom_type!(log::Level, LogLevel, { + from_custom: |l| match l { + log::Level::Error => LogLevel::Error, + log::Level::Warn => LogLevel::Warn, + log::Level::Info => LogLevel::Info, + log::Level::Debug => LogLevel::Debug, + log::Level::Trace => LogLevel::Trace, + }, + try_into_custom: |l| Ok(match l ({ + LogLevel::Error => log::Level::Error, + LogLevel::Warn => log::Level::Warn, + LogLevel::Info => log::Level::Info, + LogLevel::Debug => log::Level::Debug, + LogLevel::Trace => log::Level::Trace, + }) +}) + +/// Interfaces can use the newtype pattern +#[derive(uniffi::Object)] +pub struct AnyhowError(anyhow::Error); + +uniffi::custom_newtype!(anyhow::Error, AnyhowError). + +// We can define methods directly with this approach, no need for extension traits. +#[uniffi::export] +impl AnyhowError { + fn is_foo_error(&self) -> bool { + self.0.is::() + } + + fn is_bar_error(&self) -> bool { + self.0.is::() + } + + fn message(&self) -> String { + self.0.to_string() + } +} +``` + +#### Two types + +One drawback of this approach is that we have to equivalent, but different types. +Rust code would need to use `anyhow::Error` in their signatures, while foreign code would use `AnyhowError`. +Since the types are almost exactly the same, but named slightly different and with slightly different methods, it can be awkward to document this distinction -- both by UniFFI for library authors and by library authors for their consumers. + +### [Option 3] hybrid approach + +We could try to combine the best of both worlds by using option 2 for records/structs and option 1 for interfaces. + +## Pros and Cons of the Options + +### [Option 1] expose remote types directly + +* Good, because both the foreign code and Rust code can use the same type names. +* Good, because it has a low amount of boilerplate code (assuming we provide the `remote_extend!` macro). +* Bad, because we need to define extension traits for remote interfaces types. +* Bad, because it can be confusing to see a type declaration that the `uniffi::remote!` macro will eventually throw away. + +### [Option 1a] use an function-like macro + +(compared to option 1) + +* Bad, because the item declaration looks less natural. +* Good, since it makes it a bit more obvious that the item declaration will be thrown away. + +### [Option 2] use custom-type conversion to expose the type + +* Good, because adding methods to remote interface types is natural. +* Bad, because having two equivalent but different types could cause confusion. +* Bad, because users have to write out the trivial struct/enum conversions. + +### [Option 3] hybrid approach + +* Good, because adding methods to remote interface types is natural. +* Good, because both the foreign code and Rust code can use the same type names for struct/record types. +* Bad, because there will be two types for interface types. +* Good, because it has a low amount of boilerplate code. +* Bad, because mixing the two systems increases the overall complexity and risk of confusion. + +## Decision Outcome + +Option 1 + +## Discussion: how to extend remote interfaces + +Option 1 proposes extending remote interfaces by adding FFI methods. +An alternative would be to use extension traits, for example: + +```rust +pub trait AnyhowErrorExt { + fn is_foo_error(&self) -> bool; + fn is_bar_error(&self) -> bool; +} + +impl AnyhowErrorExt for anyhow::Error { + fn is_foo_error(&self) -> bool { + self.is::() + } + + fn is_bar_error(&self) -> bool { + self.is::() + } +} +``` + +This extension trait code could be generated by UniFFI using the same macro syntax. The main +difference between the two approaches is that the extension trait methods could be called from Rust +(though users might have to jump through some hoops to import the trait), while FFI methods could +only be called from the foreign language. + +We decided to propose FFI method solution, since in most cases there's no reason to call these +methods from Rust. Users can always define extension traits by hand, which is the recommended +solution for traits that are intended to be used by other Rust code. From 56f4ed8a66e12d2eb76d94718584d64f4dd21e06 Mon Sep 17 00:00:00 2001 From: Ben Dean-Kawamura Date: Fri, 28 Jun 2024 20:06:37 -0400 Subject: [PATCH 06/62] Fix the documentation around references `Option<&T>` was never supported as far as I can tell. I added some tests on top of e10a7f95d6b5bd8b4788d8f0eb77aea20dfb655b, where that documentation was added and it didn't work. --- docs/manual/src/proc_macro/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/manual/src/proc_macro/index.md b/docs/manual/src/proc_macro/index.md index caa9ceeafc..264a2921f8 100644 --- a/docs/manual/src/proc_macro/index.md +++ b/docs/manual/src/proc_macro/index.md @@ -107,7 +107,7 @@ Arguments and receivers can also be references to these types, for example: ```rust // Input data types as references #[uniffi::export] -fn process_data(a: &MyRecord, b: &MyEnum, c: Option<&MyRecord>) { +fn process_data(a: &MyRecord, b: &MyEnum, c: &Option) { ... } From 8bc192e3f752aad974ebb344d13c99a15667faca Mon Sep 17 00:00:00 2001 From: Mark Hammond Date: Tue, 2 Jul 2024 14:11:33 -0400 Subject: [PATCH 07/62] `RustCallStatus::error_buf` is now `ManuallyDrop` and not `MaybeUninit`. (#2173) `error_buf` is owned by the foreign side. Because we assume it has been initialized we use `ManuallyDrop` instead of `MaybeUninit`, which clarifies ownership and avoids unsafe code. Fixes #2168 --- uniffi_core/src/ffi/ffiserialize.rs | 14 ++- uniffi_core/src/ffi/foreignfuture.rs | 2 +- uniffi_core/src/ffi/rustcalls.rs | 90 +++++++------------ uniffi_core/src/ffi/rustfuture/tests.rs | 17 ++-- uniffi_core/src/ffi_converter_traits.rs | 10 +-- .../src/export/callback_interface.rs | 2 +- 6 files changed, 54 insertions(+), 81 deletions(-) diff --git a/uniffi_core/src/ffi/ffiserialize.rs b/uniffi_core/src/ffi/ffiserialize.rs index 2ef91a8270..5af0df85b1 100644 --- a/uniffi_core/src/ffi/ffiserialize.rs +++ b/uniffi_core/src/ffi/ffiserialize.rs @@ -3,7 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use crate::{Handle, RustBuffer, RustCallStatus, RustCallStatusCode}; -use std::{mem::MaybeUninit, ptr::NonNull}; +use std::{mem::ManuallyDrop, ptr::NonNull}; /// FFIBuffer element /// @@ -228,15 +228,13 @@ impl FfiSerialize for RustCallStatus { let code = unsafe { buf[0].i8 }; Self { code: RustCallStatusCode::try_from(code).unwrap_or(RustCallStatusCode::UnexpectedError), - error_buf: MaybeUninit::new(RustBuffer::get(&buf[1..])), + error_buf: ManuallyDrop::new(RustBuffer::get(&buf[1..])), } } fn put(buf: &mut [FfiBufferElement], value: Self) { buf[0].i8 = value.code as i8; - // Safety: This is okay even if the error buf is not initialized. It just means we'll be - // copying the garbage data. - unsafe { RustBuffer::put(&mut buf[1..], value.error_buf.assume_init()) } + RustBuffer::put(&mut buf[1..], ManuallyDrop::into_inner(value.error_buf)) } } @@ -278,8 +276,8 @@ mod test { rust_buffer.capacity(), ); let handle = Handle::from_raw(101).unwrap(); - let rust_call_status = RustCallStatus::new(); - let rust_call_status_error_buf = unsafe { rust_call_status.error_buf.assume_init_ref() }; + let rust_call_status = RustCallStatus::default(); + let rust_call_status_error_buf = &rust_call_status.error_buf; let orig_rust_call_status_buffer_data = ( rust_call_status_error_buf.data_pointer(), rust_call_status_error_buf.len(), @@ -334,7 +332,7 @@ mod test { let rust_call_status2 = ::read(&mut buf_reader); assert_eq!(rust_call_status2.code, RustCallStatusCode::Success); - let rust_call_status2_error_buf = unsafe { rust_call_status2.error_buf.assume_init() }; + let rust_call_status2_error_buf = ManuallyDrop::into_inner(rust_call_status2.error_buf); assert_eq!( ( rust_call_status2_error_buf.data_pointer(), diff --git a/uniffi_core/src/ffi/foreignfuture.rs b/uniffi_core/src/ffi/foreignfuture.rs index 87d8b9a4e7..804beaef62 100644 --- a/uniffi_core/src/ffi/foreignfuture.rs +++ b/uniffi_core/src/ffi/foreignfuture.rs @@ -147,7 +147,7 @@ mod test { *data, ForeignFutureResult { return_value: >::lower(value), - call_status: RustCallStatus::new(), + call_status: RustCallStatus::default(), }, ); } diff --git a/uniffi_core/src/ffi/rustcalls.rs b/uniffi_core/src/ffi/rustcalls.rs index 91d3fe2472..cdc02b91d0 100644 --- a/uniffi_core/src/ffi/rustcalls.rs +++ b/uniffi_core/src/ffi/rustcalls.rs @@ -12,7 +12,7 @@ //! exception use crate::{FfiDefault, Lower, RustBuffer, UniFfiTag}; -use std::mem::MaybeUninit; +use std::mem::ManuallyDrop; use std::panic; /// Represents the success/error of a rust call @@ -42,52 +42,43 @@ use std::panic; #[repr(C)] pub struct RustCallStatus { pub code: RustCallStatusCode, - // code is signed because unsigned types are experimental in Kotlin - pub error_buf: MaybeUninit, - // error_buf is MaybeUninit to avoid dropping the value that the consumer code sends in: - // - Consumers should send in a zeroed out RustBuffer. In this case dropping is a no-op and - // avoiding the drop is a small optimization. - // - If consumers pass in invalid data, then we should avoid trying to drop it. In - // particular, we don't want to try to free any data the consumer has allocated. - // - // `MaybeUninit` requires unsafe code, since we are preventing rust from dropping the value. - // To use this safely we need to make sure that no code paths set this twice, since that will - // leak the first `RustBuffer`. + // error_buf is owned by the foreign side. + // - Whatever we are passed, we must never free. This however implies we must be passed + // an empty `RustBuffer` otherwise it would leak when we replace it with our own. + // - On error we will set it to a `RustBuffer` we expect the foreign side to free. + // We assume initialization, which means we can use `ManuallyDrop` instead of + // `MaybeUninit`, which avoids unsafe code and clarifies ownership. + // We must take care to not set this twice to avoid leaking the first `RustBuffer`. + pub error_buf: ManuallyDrop, } -impl RustCallStatus { - pub fn new() -> Self { +impl Default for RustCallStatus { + fn default() -> Self { Self { code: RustCallStatusCode::Success, - error_buf: MaybeUninit::new(RustBuffer::new()), + error_buf: Default::default(), } } +} +impl RustCallStatus { pub fn cancelled() -> Self { Self { code: RustCallStatusCode::Cancelled, - error_buf: MaybeUninit::new(RustBuffer::new()), + error_buf: Default::default(), } } pub fn error(message: impl Into) -> Self { Self { code: RustCallStatusCode::UnexpectedError, - error_buf: MaybeUninit::new(>::lower(message.into())), - } - } -} - -impl Default for RustCallStatus { - fn default() -> Self { - Self { - code: RustCallStatusCode::Success, - error_buf: MaybeUninit::uninit(), + error_buf: ManuallyDrop::new(>::lower(message.into())), } } } /// Result of a FFI call to a Rust function +/// Value is signed to avoid Kotlin's experimental unsigned types. #[repr(i8)] #[derive(Debug, PartialEq, Eq)] pub enum RustCallStatusCode { @@ -171,11 +162,7 @@ where // Callback returned an Err. Ok(Err(buf)) => { out_status.code = RustCallStatusCode::Error; - unsafe { - // Unsafe because we're setting the `MaybeUninit` value, see above for safety - // invariants. - out_status.error_buf.as_mut_ptr().write(buf); - } + *out_status.error_buf = buf; None } // Callback panicked @@ -196,11 +183,9 @@ where >::lower(message) })); if let Ok(buf) = message_result { - unsafe { - // Unsafe because we're setting the `MaybeUninit` value, see above for safety - // invariants. - out_status.error_buf.as_mut_ptr().write(buf); - } + // If this was ever set twice we'd leak the old value - but because this is the only + // place where it is set, and this is only called once, no leaks should exist in practice. + *out_status.error_buf = buf; } // Ignore the error case. We've done all that we can at this point. In the bindings // code, we handle this by checking if `error_buf` still has an empty `RustBuffer` and @@ -215,13 +200,6 @@ mod test { use super::*; use crate::{test_util::TestError, Lift, LowerReturn}; - fn create_call_status() -> RustCallStatus { - RustCallStatus { - code: RustCallStatusCode::Success, - error_buf: MaybeUninit::new(RustBuffer::new()), - } - } - fn test_callback(a: u8) -> Result { match a { 0 => Ok(100), @@ -232,7 +210,7 @@ mod test { #[test] fn test_rust_call() { - let mut status = create_call_status(); + let mut status = RustCallStatus::default(); let return_value = rust_call(&mut status, || { as LowerReturn>::lower_return(test_callback(0)) }); @@ -244,23 +222,21 @@ mod test { as LowerReturn>::lower_return(test_callback(1)) }); assert_eq!(status.code, RustCallStatusCode::Error); - unsafe { - assert_eq!( - >::try_lift(status.error_buf.assume_init()).unwrap(), - TestError("Error".to_owned()) - ); - } + assert_eq!( + >::try_lift(ManuallyDrop::into_inner(status.error_buf)) + .unwrap(), + TestError("Error".to_owned()) + ); - let mut status = create_call_status(); + let mut status = RustCallStatus::default(); rust_call(&mut status, || { as LowerReturn>::lower_return(test_callback(2)) }); assert_eq!(status.code, RustCallStatusCode::UnexpectedError); - unsafe { - assert_eq!( - >::try_lift(status.error_buf.assume_init()).unwrap(), - "Unexpected value: 2" - ); - } + assert_eq!( + >::try_lift(ManuallyDrop::into_inner(status.error_buf)) + .unwrap(), + "Unexpected value: 2" + ); } } diff --git a/uniffi_core/src/ffi/rustfuture/tests.rs b/uniffi_core/src/ffi/rustfuture/tests.rs index 369ef2eabc..67dd47525b 100644 --- a/uniffi_core/src/ffi/rustfuture/tests.rs +++ b/uniffi_core/src/ffi/rustfuture/tests.rs @@ -1,6 +1,7 @@ use once_cell::sync::OnceCell; use std::{ future::Future, + mem::ManuallyDrop, panic, pin::Pin, sync::{Arc, Mutex}, @@ -126,15 +127,13 @@ fn test_error() { let (_, call_status) = complete(rust_future); assert_eq!(call_status.code, RustCallStatusCode::Error); - unsafe { - assert_eq!( - >::try_lift_from_rust_buffer( - call_status.error_buf.assume_init() - ) - .unwrap(), - TestError::from("Something went wrong"), - ) - } + assert_eq!( + >::try_lift_from_rust_buffer(ManuallyDrop::into_inner( + call_status.error_buf + )) + .unwrap(), + TestError::from("Something went wrong"), + ) } // Once `complete` is called, the inner future should be released, even if wakers still hold a diff --git a/uniffi_core/src/ffi_converter_traits.rs b/uniffi_core/src/ffi_converter_traits.rs index 1b4fdb7333..1a7f2b2aac 100644 --- a/uniffi_core/src/ffi_converter_traits.rs +++ b/uniffi_core/src/ffi_converter_traits.rs @@ -50,7 +50,7 @@ //! These traits should not be used directly, only in generated code, and the generated code should //! have fixture tests to test that everything works correctly together. -use std::{borrow::Borrow, sync::Arc}; +use std::{borrow::Borrow, mem::ManuallyDrop, sync::Arc}; use anyhow::bail; use bytes::Buf; @@ -339,12 +339,12 @@ pub unsafe trait LiftReturn: Sized { Self::handle_callback_unexpected_error(UnexpectedUniFFICallbackError::new(e)) }), RustCallStatusCode::Error => { - Self::lift_error(unsafe { call_status.error_buf.assume_init() }) + Self::lift_error(ManuallyDrop::into_inner(call_status.error_buf)) } _ => { - let e = >::try_lift(unsafe { - call_status.error_buf.assume_init() - }) + let e = >::try_lift( + ManuallyDrop::into_inner(call_status.error_buf), + ) .unwrap_or_else(|e| format!("(Error lifting message: {e}")); Self::handle_callback_unexpected_error(UnexpectedUniFFICallbackError::new(e)) } diff --git a/uniffi_macros/src/export/callback_interface.rs b/uniffi_macros/src/export/callback_interface.rs index e9621913b7..fe60b85ae3 100644 --- a/uniffi_macros/src/export/callback_interface.rs +++ b/uniffi_macros/src/export/callback_interface.rs @@ -222,7 +222,7 @@ fn gen_method_impl(sig: &FnSignature, vtable_cell: &Ident) -> syn::Result #return_ty { let vtable = #vtable_cell.get(); - let mut uniffi_call_status = ::uniffi::RustCallStatus::new(); + let mut uniffi_call_status = ::uniffi::RustCallStatus::default(); let mut uniffi_return_value: #lift_return_type = ::uniffi::FfiDefault::ffi_default(); (vtable.#ident)(self.handle, #(#lower_exprs,)* &mut uniffi_return_value, &mut uniffi_call_status); #lift_foreign_return(uniffi_return_value, uniffi_call_status) From 0b17a08872ae6b990d2b0bc172c9a919017b1a45 Mon Sep 17 00:00:00 2001 From: Mark Hammond Date: Thu, 4 Jul 2024 11:32:46 -0400 Subject: [PATCH 08/62] Mention procmacros in the intro docs and other minor doc tweaks (#2171) Co-authored-by: Jan-Erik Rediger --- docs/manual/src/Motivation.md | 8 ++++---- docs/manual/src/index.md | 8 +++++--- fixtures/ext-types/README.md | 17 ++++++++++++----- fixtures/ext-types/sub-lib/README.md | 2 +- 4 files changed, 22 insertions(+), 13 deletions(-) diff --git a/docs/manual/src/Motivation.md b/docs/manual/src/Motivation.md index 1cea93d61c..d10edb0a49 100644 --- a/docs/manual/src/Motivation.md +++ b/docs/manual/src/Motivation.md @@ -28,11 +28,11 @@ continues to evolve. Using UniFFI, you can: * Implement your software component as a `cdylib` crate in Rust; let's say the code is in `./src/lib.rs`. -* Specify the desired component API using an *Interface Definition Language* (specifically, a variant of WebIDL) in a separate file like `./src/lib.udl`. -* Run `uniffi-bindgen scaffolding ./src/lib.udl` to generate a bunch of boilerplate rust code that exposes this API as a C-compatible FFI layer, - and include it as part of your crate. +* Optionally, describe parts of your component API using proc-macros directly in `lib.rs`. +* Optionally, describe parts of your component API using an *Interface Definition Language* in a separate file like `./src/lib.udl`. UniFFI will generate a bunch of boilerplate Rust code that exposes this API as a C-compatible FFI layer, and include it as part of your crate. * `cargo build` your crate as normal to produce a shared library. -* Run `uniffi-bindgen generate ./src/lib.udl -l kotlin` to generate a Kotlin library that can load your shared library +* Run `uniffi-bindgen generate ... -l kotlin` (see [the bindgen docs](./tutorial/foreign_language_bindings.md) for omitted arg details) + to generate a Kotlin library that can load your shared library and expose it to Kotlin code using your nice high-level component API! * Or `-l swift` or `-l python` to produce bindings for other languages. diff --git a/docs/manual/src/index.md b/docs/manual/src/index.md index e6cc886b5c..c0fa836645 100644 --- a/docs/manual/src/index.md +++ b/docs/manual/src/index.md @@ -3,12 +3,14 @@ UniFFI is a tool that automatically generates foreign-language bindings targeting Rust libraries. The repository can be found on [github](https://github.com/mozilla/uniffi-rs/). It fits in the practice of consolidating business logic in a single Rust library while targeting multiple platforms, making it simpler to develop and maintain a cross-platform codebase. -Note that this tool will not help you ship a Rust library to these platforms, but simply not have to write bindings code by hand. [Related](https://i.kym-cdn.com/photos/images/newsfeed/000/572/078/d6d.jpg). +Note that this tool will not help you ship a Rust library to these platforms, but it will help you avoid writing bindings code by hand. +[Related](https://i.kym-cdn.com/photos/images/newsfeed/000/572/078/d6d.jpg). ## Design -UniFFI requires to write an Interface Definition Language (based on [WebIDL](https://heycam.github.io/webidl/)) file describing the methods and data structures available to the targeted languages. -This .udl (UniFFI Definition Language) file, whose definitions must match with the exposed Rust code, is then used to generate Rust *scaffolding* code and foreign-languages *bindings*. This process can take place either during the build process or be manually initiated by the developer. +UniFFI requires you to describe your interface via either proc-macros or in an Interface Definition Language (based on [WebIDL](https://webidl.spec.whatwg.org/)) file. +These definitions describe the methods and data structures available to the targeted languages, and are used to generate Rust *scaffolding* code and foreign-language *bindings*. +This process can take place either during the build process or be manually initiated by the developer. ![uniffi diagram](./uniffi_diagram.png) diff --git a/fixtures/ext-types/README.md b/fixtures/ext-types/README.md index 91f9ba421a..cb11c8ab61 100644 --- a/fixtures/ext-types/README.md +++ b/fixtures/ext-types/README.md @@ -1,6 +1,13 @@ -This directory contains the tests for external types -- types defined in one crate and used in a -different one. +This directory contains the tests for external types - cross-crate depedencies and libraries +to tie them together. -- `guid` and `uniffi-one` are dependent crates that define types exported by UniFFI -- `lib` is a library crate that depends on `guid` and `uniffi-one` -- `proc-macro-lib` is another library crate, but this one uses proc-macros rather than UDL files +- `lib` is a library crate that depends on various other crates. +- `proc-macro-lib` is another library crate, but this one uses proc-macros rather than UDL files. + +The various other crates all inter-relate and are ultimately consumed by the above libs: +- `custom-types` is all about wrapping types (eg, Guid, Handle) in a native type (eg, String, u64) +- `uniffi-one` is just a normal other crate also using uniffi. +- `sub-lib` itself consumes and exposes the other types. +- `external-crate` doesn't depend on uniffi but has types we expose. + +etc. diff --git a/fixtures/ext-types/sub-lib/README.md b/fixtures/ext-types/sub-lib/README.md index 41c153fde4..48fd1db921 100644 --- a/fixtures/ext-types/sub-lib/README.md +++ b/fixtures/ext-types/sub-lib/README.md @@ -1,3 +1,3 @@ -This is a "sub library" - it is itself a "library" which consumes types from +This is a "sub library" - ie, a crate which consumes types from other external crates and *also* exports its own composite types and trait implementations to be consumed by the "main" library. From c5da61baf98d421f2da521b1ab085f576695178d Mon Sep 17 00:00:00 2001 From: Mark Hammond Date: Thu, 4 Jul 2024 15:29:16 -0400 Subject: [PATCH 09/62] Use fully-qualified names to the stdlib's `core` library --- uniffi_core/src/ffi/ffiserialize.rs | 2 +- uniffi_meta/src/types.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/uniffi_core/src/ffi/ffiserialize.rs b/uniffi_core/src/ffi/ffiserialize.rs index 5af0df85b1..50ae78840a 100644 --- a/uniffi_core/src/ffi/ffiserialize.rs +++ b/uniffi_core/src/ffi/ffiserialize.rs @@ -81,7 +81,7 @@ pub trait FfiSerialize: Sized { fn write(buf: &mut &mut [FfiBufferElement], value: Self) { Self::put(buf, value); // Lifetime dance taken from `bytes::BufMut` - let (_, new_buf) = core::mem::take(buf).split_at_mut(Self::SIZE); + let (_, new_buf) = ::core::mem::take(buf).split_at_mut(Self::SIZE); *buf = new_buf; } } diff --git a/uniffi_meta/src/types.rs b/uniffi_meta/src/types.rs index 51bf156b50..afb6218933 100644 --- a/uniffi_meta/src/types.rs +++ b/uniffi_meta/src/types.rs @@ -146,7 +146,7 @@ impl Type { } // A trait so various things can turn into a type. -pub trait AsType: core::fmt::Debug { +pub trait AsType: ::core::fmt::Debug { fn as_type(&self) -> Type; } From f4ca11f2ebc665efa8f959eab9b04e7e856f3688 Mon Sep 17 00:00:00 2001 From: Jonas Platte Date: Mon, 8 Jul 2024 02:28:03 +0200 Subject: [PATCH 10/62] Create fixture proc-macro-no-implicit-prelude (#2180) --- Cargo.lock | 7 + Cargo.toml | 1 + .../proc-macro-no-implicit-prelude/Cargo.toml | 25 ++ .../proc-macro-no-implicit-prelude/build.rs | 9 + .../src/callback_interface.rs | 24 + .../proc-macro-no-implicit-prelude/src/lib.rs | 413 ++++++++++++++++++ .../src/proc-macro.udl | 36 ++ .../uniffi.toml | 2 + .../ui/interface_not_sync_and_send.stderr | 4 +- uniffi_bindgen/src/scaffolding/mod.rs | 14 +- uniffi_core/src/ffi/callbackinterface.rs | 2 +- uniffi_core/src/ffi_converter_traits.rs | 2 +- uniffi_core/src/lib.rs | 2 +- uniffi_macros/src/custom.rs | 8 +- uniffi_macros/src/enum_.rs | 4 +- uniffi_macros/src/error.rs | 8 +- .../src/export/callback_interface.rs | 6 +- uniffi_macros/src/export/scaffolding.rs | 24 +- uniffi_macros/src/export/trait_interface.rs | 38 +- uniffi_macros/src/export/utrait.rs | 4 +- uniffi_macros/src/fnsig.rs | 8 +- uniffi_macros/src/object.rs | 18 +- uniffi_macros/src/record.rs | 2 +- uniffi_macros/src/setup_scaffolding.rs | 45 +- uniffi_macros/src/test.rs | 10 +- 25 files changed, 636 insertions(+), 80 deletions(-) create mode 100644 fixtures/proc-macro-no-implicit-prelude/Cargo.toml create mode 100644 fixtures/proc-macro-no-implicit-prelude/build.rs create mode 100644 fixtures/proc-macro-no-implicit-prelude/src/callback_interface.rs create mode 100644 fixtures/proc-macro-no-implicit-prelude/src/lib.rs create mode 100644 fixtures/proc-macro-no-implicit-prelude/src/proc-macro.udl create mode 100644 fixtures/proc-macro-no-implicit-prelude/uniffi.toml diff --git a/Cargo.lock b/Cargo.lock index ec5562171a..c0f83c5727 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1784,6 +1784,13 @@ dependencies = [ "uniffi", ] +[[package]] +name = "uniffi-fixture-proc-macro-no-implicit-prelude" +version = "0.22.0" +dependencies = [ + "uniffi", +] + [[package]] name = "uniffi-fixture-regression-callbacks-omit-labels" version = "0.22.0" diff --git a/Cargo.toml b/Cargo.toml index b2fb6d899a..1160898623 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,6 +42,7 @@ members = [ "fixtures/keywords/swift", "fixtures/metadata", "fixtures/proc-macro", + "fixtures/proc-macro-no-implicit-prelude", "fixtures/regressions/enum-without-i32-helpers", "fixtures/regressions/fully-qualified-types", "fixtures/regressions/kotlin-experimental-unsigned-types", diff --git a/fixtures/proc-macro-no-implicit-prelude/Cargo.toml b/fixtures/proc-macro-no-implicit-prelude/Cargo.toml new file mode 100644 index 0000000000..e56fd77196 --- /dev/null +++ b/fixtures/proc-macro-no-implicit-prelude/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "uniffi-fixture-proc-macro-no-implicit-prelude" +version = "0.22.0" +authors = ["Firefox Sync Team "] +edition = "2018" +license = "MPL-2.0" +publish = false + +[lib] +name = "uniffi_proc_macro_nip" +crate-type = ["lib", "cdylib"] + +[features] +default = ["myfeature"] +myfeature = [] + +[dependencies] +# Add the "scaffolding-ffi-buffer-fns" feature to make sure things can build correctly +uniffi = { workspace = true, features = ["scaffolding-ffi-buffer-fns"] } + +[build-dependencies] +uniffi = { workspace = true, features = ["build", "scaffolding-ffi-buffer-fns"] } + +[dev-dependencies] +uniffi = { workspace = true, features = ["bindgen-tests"] } diff --git a/fixtures/proc-macro-no-implicit-prelude/build.rs b/fixtures/proc-macro-no-implicit-prelude/build.rs new file mode 100644 index 0000000000..a652fcd339 --- /dev/null +++ b/fixtures/proc-macro-no-implicit-prelude/build.rs @@ -0,0 +1,9 @@ +/* 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/. */ + +fn main() { + // generate_scaffolding would work here, but we use the _for_crate version for + // test coverage. + uniffi::generate_scaffolding_for_crate("src/proc-macro.udl", "uniffi_proc_macro").unwrap(); +} diff --git a/fixtures/proc-macro-no-implicit-prelude/src/callback_interface.rs b/fixtures/proc-macro-no-implicit-prelude/src/callback_interface.rs new file mode 100644 index 0000000000..ca88186511 --- /dev/null +++ b/fixtures/proc-macro-no-implicit-prelude/src/callback_interface.rs @@ -0,0 +1,24 @@ +/* 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::marker::Sized; + +use crate::{BasicError, Object, RecordWithBytes}; + +#[::uniffi::export(callback_interface)] +pub trait TestCallbackInterface { + fn do_nothing(&self); + fn add(&self, a: u32, b: u32) -> u32; + fn optional(&self, a: ::std::option::Option) -> u32; + fn with_bytes(&self, rwb: RecordWithBytes) -> ::std::vec::Vec; + fn try_parse_int(&self, value: ::std::string::String) + -> ::std::result::Result; + fn callback_handler(&self, h: ::std::sync::Arc) -> u32; + fn get_other_callback_interface(&self) -> ::std::boxed::Box; +} + +#[::uniffi::export(callback_interface)] +pub trait OtherCallbackInterface { + fn multiply(&self, a: u32, b: u32) -> u32; +} diff --git a/fixtures/proc-macro-no-implicit-prelude/src/lib.rs b/fixtures/proc-macro-no-implicit-prelude/src/lib.rs new file mode 100644 index 0000000000..1823929cb8 --- /dev/null +++ b/fixtures/proc-macro-no-implicit-prelude/src/lib.rs @@ -0,0 +1,413 @@ +/* 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/. */ +#![no_implicit_prelude] + +// Let's not have the macros care about derive being shadowed in the macro namespace for now.. +use ::std::prelude::rust_2021::derive; +// Required for now because `static-assertions` (used internally) macros assume it to be in scope. +use ::std::marker::Sized; + +mod callback_interface; + +use callback_interface::TestCallbackInterface; + +#[derive(::uniffi::Record)] +pub struct One { + inner: i32, +} + +#[::uniffi::export] +pub fn one_inner_by_ref(one: &One) -> i32 { + one.inner +} + +#[derive(::uniffi::Record)] +pub struct Two { + a: ::std::string::String, +} + +#[derive(::uniffi::Record)] +pub struct NestedRecord { + // This used to result in an error in bindings generation + user_type_in_builtin_generic: ::std::option::Option, +} + +#[derive(::uniffi::Record)] +pub struct Three { + obj: ::std::sync::Arc, +} + +#[derive(::uniffi::Record, Debug, PartialEq)] +pub struct RecordWithBytes { + some_bytes: ::std::vec::Vec, +} + +// An object that's not used anywhere (ie, in records, function signatures, etc) +// should not break things. +#[derive(::uniffi::Object)] +pub struct Unused; + +#[::uniffi::export] +impl Unused { + #[::uniffi::constructor] + fn new() -> ::std::sync::Arc { + ::std::sync::Arc::new(Self) + } +} + +#[::uniffi::export] +pub trait Trait: ::std::marker::Send + ::std::marker::Sync { + // Test the absence of `with_foreign` by inputting reference arguments, which is + // incompatible with callback interfaces + #[allow(clippy::ptr_arg)] + fn concat_strings(&self, a: &str, b: &str) -> ::std::string::String; +} + +struct TraitImpl {} + +impl Trait for TraitImpl { + fn concat_strings(&self, a: &str, b: &str) -> ::std::string::String { + ::std::format!("{a}{b}") + } +} + +#[::uniffi::export(with_foreign)] +pub trait TraitWithForeign: ::std::marker::Send + ::std::marker::Sync { + fn name(&self) -> ::std::string::String; +} + +struct RustTraitImpl {} + +impl TraitWithForeign for RustTraitImpl { + fn name(&self) -> ::std::string::String { + use ::std::string::ToString; + "RustTraitImpl".to_string() + } +} + +#[derive(::uniffi::Object)] +pub struct Object; + +#[cfg_attr(feature = "myfeature", ::uniffi::export)] +impl Object { + #[cfg_attr(feature = "myfeature", ::uniffi::constructor)] + fn new() -> ::std::sync::Arc { + ::std::sync::Arc::new(Self) + } + + #[::uniffi::constructor] + fn named_ctor(arg: u32) -> Self { + _ = arg; + // This constructor returns Self directly. UniFFI ensures that it's wrapped in an Arc + // before sending it across the FFI. + Self + } + + fn is_heavy(&self) -> MaybeBool { + MaybeBool::Uncertain + } + + fn is_other_heavy(&self, other: &Self) -> MaybeBool { + other.is_heavy() + } + + fn get_trait( + &self, + inc: ::std::option::Option<::std::sync::Arc>, + ) -> ::std::sync::Arc { + inc.unwrap_or_else(|| ::std::sync::Arc::new(TraitImpl {})) + } + + fn get_trait_with_foreign( + &self, + inc: ::std::option::Option<::std::sync::Arc>, + ) -> ::std::sync::Arc { + inc.unwrap_or_else(|| ::std::sync::Arc::new(RustTraitImpl {})) + } + + fn take_error(&self, e: BasicError) -> u32 { + ::std::assert!(::std::matches!(e, BasicError::InvalidInput)); + 42 + } +} + +#[::uniffi::export] +fn concat_strings_by_ref(t: &dyn Trait, a: &str, b: &str) -> ::std::string::String { + t.concat_strings(a, b) +} + +#[::uniffi::export] +fn make_one(inner: i32) -> One { + One { inner } +} + +#[::uniffi::export] +fn take_two(two: Two) -> ::std::string::String { + two.a +} + +#[::uniffi::export] +fn make_hashmap(k: i8, v: u64) -> ::std::collections::HashMap { + ::std::convert::From::from([(k, v)]) +} + +#[::uniffi::export] +fn return_hashmap(h: ::std::collections::HashMap) -> ::std::collections::HashMap { + h +} + +#[::uniffi::export] +fn take_record_with_bytes(rwb: RecordWithBytes) -> ::std::vec::Vec { + rwb.some_bytes +} + +#[::uniffi::export] +fn call_callback_interface(cb: ::std::boxed::Box) { + use ::std::{assert_eq, matches, option::Option::*, result::Result::*, string::ToString, vec}; + + cb.do_nothing(); + assert_eq!(cb.add(1, 1), 2); + assert_eq!(cb.optional(Some(1)), 1); + assert_eq!(cb.optional(None), 0); + assert_eq!( + cb.with_bytes(RecordWithBytes { + some_bytes: vec![9, 8, 7], + }), + vec![9, 8, 7] + ); + assert_eq!(Ok(10), cb.try_parse_int("10".to_string())); + assert_eq!( + Err(BasicError::InvalidInput), + cb.try_parse_int("ten".to_string()) + ); + assert!(matches!( + cb.try_parse_int("force-unexpected-error".to_string()), + Err(BasicError::UnexpectedError { .. }), + )); + assert_eq!(42, cb.callback_handler(Object::new())); + + assert_eq!(6, cb.get_other_callback_interface().multiply(2, 3)); +} + +// Type that's defined in the UDL and not wrapped with #[::uniffi::export] +pub struct Zero { + inner: ::std::string::String, +} + +#[::uniffi::export] +fn make_zero() -> Zero { + use ::std::borrow::ToOwned; + Zero { + inner: "ZERO".to_owned(), + } +} + +#[::uniffi::export] +fn make_record_with_bytes() -> RecordWithBytes { + RecordWithBytes { + some_bytes: ::std::vec![0, 1, 2, 3, 4], + } +} + +#[derive(::uniffi::Enum)] +pub enum MaybeBool { + True, + False, + Uncertain, +} + +#[derive(::uniffi::Enum)] +pub enum MixedEnum { + None, + String(::std::string::String), + Int(i64), + Both(::std::string::String, i64), + All { s: ::std::string::String, i: i64 }, +} + +#[::uniffi::export] +fn get_mixed_enum(v: ::std::option::Option) -> MixedEnum { + v.unwrap_or(MixedEnum::Int(1)) +} + +#[repr(u8)] +#[derive(::uniffi::Enum)] +pub enum ReprU8 { + One = 1, + Three = 0x3, +} + +#[::uniffi::export] +fn enum_identity(value: MaybeBool) -> MaybeBool { + value +} + +#[derive(::uniffi::Error, Debug, PartialEq, Eq)] +pub enum BasicError { + InvalidInput, + OsError, + UnexpectedError { reason: ::std::string::String }, +} + +impl ::std::fmt::Display for BasicError { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + f.write_str(match self { + Self::InvalidInput => "InvalidInput", + Self::OsError => "OsError", + Self::UnexpectedError { .. } => "UnexpectedError", + }) + } +} + +impl ::std::error::Error for BasicError {} + +impl ::std::convert::From<::uniffi::UnexpectedUniFFICallbackError> for BasicError { + fn from(e: ::uniffi::UnexpectedUniFFICallbackError) -> Self { + Self::UnexpectedError { reason: e.reason } + } +} + +#[::uniffi::export] +fn always_fails() -> ::std::result::Result<(), BasicError> { + ::std::result::Result::Err(BasicError::OsError) +} + +#[derive(Debug, ::uniffi::Error)] +#[uniffi(flat_error)] +#[non_exhaustive] +pub enum FlatError { + InvalidInput, + + // Inner types that aren't FFI-convertible, as well as unnamed fields, + // are allowed for flat errors + OsError(::std::io::Error), +} + +impl ::std::fmt::Display for FlatError { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + match self { + Self::InvalidInput => f.write_str("Invalid input"), + Self::OsError(e) => ::std::write!(f, "OS error: {e}"), + } + } +} + +#[::uniffi::export] +impl Object { + fn do_stuff(&self, times: u32) -> ::std::result::Result<(), FlatError> { + match times { + 0 => ::std::result::Result::Err(FlatError::InvalidInput), + _ => { + // do stuff + ::std::result::Result::Ok(()) + } + } + } +} + +// defined in UDL. +fn get_one(one: ::std::option::Option) -> One { + one.unwrap_or(One { inner: 0 }) +} + +fn get_bool(b: ::std::option::Option) -> MaybeBool { + b.unwrap_or(MaybeBool::Uncertain) +} + +fn get_object(o: ::std::option::Option<::std::sync::Arc>) -> ::std::sync::Arc { + o.unwrap_or_else(Object::new) +} + +fn get_trait(o: ::std::option::Option<::std::sync::Arc>) -> ::std::sync::Arc { + o.unwrap_or_else(|| ::std::sync::Arc::new(TraitImpl {})) +} + +fn get_trait_with_foreign( + o: ::std::option::Option<::std::sync::Arc>, +) -> ::std::sync::Arc { + o.unwrap_or_else(|| ::std::sync::Arc::new(RustTraitImpl {})) +} + +#[derive(Default)] +struct Externals { + one: ::std::option::Option, + bool: ::std::option::Option, +} + +fn get_externals(e: ::std::option::Option) -> Externals { + e.unwrap_or_default() +} + +#[::uniffi::export] +pub fn join(parts: &[::std::string::String], sep: &str) -> ::std::string::String { + parts.join(sep) +} + +// Custom names +#[derive(::uniffi::Object)] +pub struct Renamed; + +// `renamed_new` becomes the default constructor because it's named `new` +#[::uniffi::export] +impl Renamed { + #[::uniffi::constructor(name = "new")] + fn renamed_new() -> ::std::sync::Arc { + ::std::sync::Arc::new(Self) + } + + #[::uniffi::method(name = "func")] + fn renamed_func(&self) -> bool { + true + } +} + +#[::uniffi::export(name = "rename_test")] +fn renamed_rename_test() -> bool { + true +} + +/// Test defaults on Records +#[derive(::uniffi::Record)] +pub struct RecordWithDefaults { + no_default_string: ::std::string::String, + #[uniffi(default = true)] + boolean: bool, + #[uniffi(default = 42)] + integer: i32, + #[uniffi(default = 4.2)] + float_var: f64, + #[uniffi(default=[])] + vec: ::std::vec::Vec, + #[uniffi(default=None)] + opt_vec: ::std::option::Option<::std::vec::Vec>, + #[uniffi(default = Some(42))] + opt_integer: ::std::option::Option, +} + +/// Test defaults on top-level functions +#[::uniffi::export(default(num = 21))] +fn double_with_default(num: i32) -> i32 { + num + num +} + +/// Test defaults on constructors / methods +#[derive(::uniffi::Object)] +pub struct ObjectWithDefaults { + num: i32, +} + +#[::uniffi::export] +impl ObjectWithDefaults { + #[::uniffi::constructor(default(num = 30))] + fn new(num: i32) -> Self { + Self { num } + } + + #[::uniffi::method(default(other = 12))] + fn add_to_num(&self, other: i32) -> i32 { + self.num + other + } +} + +::uniffi::include_scaffolding!("proc-macro"); diff --git a/fixtures/proc-macro-no-implicit-prelude/src/proc-macro.udl b/fixtures/proc-macro-no-implicit-prelude/src/proc-macro.udl new file mode 100644 index 0000000000..9896f2e633 --- /dev/null +++ b/fixtures/proc-macro-no-implicit-prelude/src/proc-macro.udl @@ -0,0 +1,36 @@ +// Use this to test that types defined in the UDL can be used in the proc-macros +dictionary Zero { + string inner; +}; + +// And all of these for the opposite - proc-macro types used in UDL. +[Rust="record"] +typedef extern One; + +[Rust="enum"] +typedef extern MaybeBool; + +[Rust="interface"] +typedef extern Object; + +[Rust="trait"] +typedef extern Trait; + +[Rust="trait_with_foreign"] +typedef extern TraitWithForeign; + +// Then stuff defined here but referencing the imported types. +dictionary Externals { + One? one; + MaybeBool? bool; +}; + +// Namespace different from crate name. +namespace proc_macro { + One get_one(One? one); + MaybeBool get_bool(MaybeBool? b); + Object get_object(Object? o); + Trait get_trait(Trait? t); + TraitWithForeign get_trait_with_foreign(TraitWithForeign? t); + Externals get_externals(Externals? e); +}; diff --git a/fixtures/proc-macro-no-implicit-prelude/uniffi.toml b/fixtures/proc-macro-no-implicit-prelude/uniffi.toml new file mode 100644 index 0000000000..09899a8b65 --- /dev/null +++ b/fixtures/proc-macro-no-implicit-prelude/uniffi.toml @@ -0,0 +1,2 @@ +[bindings.kotlin] +package_name = "uniffi.fixture.proc_macro" diff --git a/fixtures/uitests/tests/ui/interface_not_sync_and_send.stderr b/fixtures/uitests/tests/ui/interface_not_sync_and_send.stderr index f32a1e4a25..19eaba8de0 100644 --- a/fixtures/uitests/tests/ui/interface_not_sync_and_send.stderr +++ b/fixtures/uitests/tests/ui/interface_not_sync_and_send.stderr @@ -16,7 +16,7 @@ note: required by a bound in `_::{closure#0}::assert_impl_all` | | #[::uniffi::udl_derive(Object)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_impl_all` - = note: this error originates in the macro `uniffi::deps::static_assertions::assert_impl_all` which comes from the expansion of the attribute macro `::uniffi::udl_derive` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `::uniffi::deps::static_assertions::assert_impl_all` which comes from the expansion of the attribute macro `::uniffi::udl_derive` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: `Cell` cannot be shared between threads safely --> $OUT_DIR[uniffi_uitests]/counter.uniffi.rs @@ -55,7 +55,7 @@ note: required by a bound in `_::{closure#0}::assert_impl_all` | 26 | #[derive(uniffi::Object)] | ^^^^^^^^^^^^^^ required by this bound in `assert_impl_all` - = note: this error originates in the macro `uniffi::deps::static_assertions::assert_impl_all` which comes from the expansion of the derive macro `uniffi::Object` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `::uniffi::deps::static_assertions::assert_impl_all` which comes from the expansion of the derive macro `uniffi::Object` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: `Cell` cannot be shared between threads safely --> tests/ui/interface_not_sync_and_send.rs:27:12 diff --git a/uniffi_bindgen/src/scaffolding/mod.rs b/uniffi_bindgen/src/scaffolding/mod.rs index 7fd81831aa..9c410cc714 100644 --- a/uniffi_bindgen/src/scaffolding/mod.rs +++ b/uniffi_bindgen/src/scaffolding/mod.rs @@ -36,24 +36,24 @@ mod filters { Type::Float32 => "f32".into(), Type::Float64 => "f64".into(), Type::Boolean => "bool".into(), - Type::String => "String".into(), - Type::Bytes => "Vec".into(), - Type::Timestamp => "std::time::SystemTime".into(), - Type::Duration => "std::time::Duration".into(), + Type::String => "::std::string::String".into(), + Type::Bytes => "::std::vec::Vec".into(), + Type::Timestamp => "::std::time::SystemTime".into(), + Type::Duration => "::std::time::Duration".into(), Type::Enum { name, .. } | Type::Record { name, .. } => format!("r#{name}"), Type::Object { name, imp, .. } => { - format!("std::sync::Arc<{}>", imp.rust_name_for(name)) + format!("::std::sync::Arc<{}>", imp.rust_name_for(name)) } Type::CallbackInterface { name, .. } => format!("Box"), Type::Optional { inner_type } => { - format!("std::option::Option<{}>", type_rs(inner_type)?) + format!("::std::option::Option<{}>", type_rs(inner_type)?) } Type::Sequence { inner_type } => format!("std::vec::Vec<{}>", type_rs(inner_type)?), Type::Map { key_type, value_type, } => format!( - "std::collections::HashMap<{}, {}>", + "::std::collections::HashMap<{}, {}>", type_rs(key_type)?, type_rs(value_type)? ), diff --git a/uniffi_core/src/ffi/callbackinterface.rs b/uniffi_core/src/ffi/callbackinterface.rs index e7a4faab64..33db6f0928 100644 --- a/uniffi_core/src/ffi/callbackinterface.rs +++ b/uniffi_core/src/ffi/callbackinterface.rs @@ -195,7 +195,7 @@ macro_rules! convert_unexpected_error { fn get_converter(&self) -> $crate::UnexpectedUniFFICallbackErrorConverterSpecialized; } - impl> GetConverterSpecialized for T { + impl> GetConverterSpecialized for T { fn get_converter(&self) -> $crate::UnexpectedUniFFICallbackErrorConverterSpecialized { $crate::UnexpectedUniFFICallbackErrorConverterSpecialized } diff --git a/uniffi_core/src/ffi_converter_traits.rs b/uniffi_core/src/ffi_converter_traits.rs index 1a7f2b2aac..86e29e7f50 100644 --- a/uniffi_core/src/ffi_converter_traits.rs +++ b/uniffi_core/src/ffi_converter_traits.rs @@ -541,7 +541,7 @@ macro_rules! derive_ffi_traits { type ReturnType = >::FfiType; fn lower_return(obj: Self) -> $crate::deps::anyhow::Result { - Ok(>::lower(obj)) + ::std::result::Result::Ok(>::lower(obj)) } } }; diff --git a/uniffi_core/src/lib.rs b/uniffi_core/src/lib.rs index 1135753371..2d55006ba7 100644 --- a/uniffi_core/src/lib.rs +++ b/uniffi_core/src/lib.rs @@ -176,7 +176,7 @@ macro_rules! ffi_converter_rust_buffer_lift_and_lower { let mut buf = vec.as_slice(); let value = >::try_read(&mut buf)?; match $crate::deps::bytes::Buf::remaining(&buf) { - 0 => Ok(value), + 0 => ::std::result::Result::Ok(value), n => $crate::deps::anyhow::bail!( "junk data left in buffer after lifting (count: {n})", ), diff --git a/uniffi_macros/src/custom.rs b/uniffi_macros/src/custom.rs index 5a52fc4964..991a74f305 100644 --- a/uniffi_macros/src/custom.rs +++ b/uniffi_macros/src/custom.rs @@ -41,7 +41,7 @@ pub(crate) fn expand_ffi_converter_custom_type( #lower(#from_custom(obj)) } - fn try_lift(v: Self::FfiType) -> uniffi::Result<#ident> { + fn try_lift(v: Self::FfiType) -> ::uniffi::Result<#ident> { #into_custom(#try_lift(v)?) } @@ -49,7 +49,7 @@ pub(crate) fn expand_ffi_converter_custom_type( #write(#from_custom(obj), buf); } - fn try_read(buf: &mut &[u8]) -> uniffi::Result<#ident> { + fn try_read(buf: &mut &[u8]) -> ::uniffi::Result<#ident> { #into_custom(#try_read(buf)?) } @@ -85,8 +85,8 @@ fn custom_ffi_type_converter(ident: &Ident, builtin: &Path) -> syn::Result uniffi::Result { - Ok(#ident(val)) + fn into_custom(val: Self::Builtin) -> ::uniffi::Result { + ::std::result::Result::Ok(#ident(val)) } fn from_custom(obj: Self) -> Self::Builtin { diff --git a/uniffi_macros/src/enum_.rs b/uniffi_macros/src/enum_.rs index d17bca053d..b17b5ba639 100644 --- a/uniffi_macros/src/enum_.rs +++ b/uniffi_macros/src/enum_.rs @@ -211,7 +211,7 @@ fn enum_or_error_ffi_converter_impl( .collect(); if item.is_non_exhaustive() { write_match_arms.push(quote! { - _ => panic!("Unexpected variant in non-exhaustive enum"), + _ => ::std::panic!("Unexpected variant in non-exhaustive enum"), }) } let write_impl = quote! { @@ -238,7 +238,7 @@ fn enum_or_error_ffi_converter_impl( let try_read_impl = quote! { ::uniffi::check_remaining(buf, 4)?; - Ok(match ::uniffi::deps::bytes::Buf::get_i32(buf) { + ::std::result::Result::Ok(match ::uniffi::deps::bytes::Buf::get_i32(buf) { #(#try_read_match_arms)* v => ::uniffi::deps::anyhow::bail!(#error_format_string, v), }) diff --git a/uniffi_macros/src/error.rs b/uniffi_macros/src/error.rs index 03191ae404..39e3deca93 100644 --- a/uniffi_macros/src/error.rs +++ b/uniffi_macros/src/error.rs @@ -88,7 +88,7 @@ fn flat_error_ffi_converter_impl(item: &EnumItem, options: &DeriveOptions) -> To .collect(); if item.is_non_exhaustive() { match_arms.push(quote! { - _ => panic!("Unexpected variant in non-exhaustive enum"), + _ => ::std::panic!("Unexpected variant in non-exhaustive enum"), }) } @@ -127,7 +127,7 @@ fn flat_error_ffi_converter_impl(item: &EnumItem, options: &DeriveOptions) -> To type FfiType = ::uniffi::RustBuffer; fn try_read(buf: &mut &[::std::primitive::u8]) -> ::uniffi::deps::anyhow::Result { - Ok(match ::uniffi::deps::bytes::Buf::get_i32(buf) { + ::std::result::Result::Ok(match ::uniffi::deps::bytes::Buf::get_i32(buf) { #(#match_arms)* v => ::uniffi::deps::anyhow::bail!("Invalid #ident enum value: {}", v), }) @@ -152,11 +152,11 @@ fn flat_error_ffi_converter_impl(item: &EnumItem, options: &DeriveOptions) -> To type FfiType = ::uniffi::RustBuffer; fn try_read(buf: &mut &[::std::primitive::u8]) -> ::uniffi::deps::anyhow::Result { - panic!("Can't lift flat errors") + ::std::panic!("Can't lift flat errors") } fn try_lift(v: ::uniffi::RustBuffer) -> ::uniffi::deps::anyhow::Result { - panic!("Can't lift flat errors") + ::std::panic!("Can't lift flat errors") } } } diff --git a/uniffi_macros/src/export/callback_interface.rs b/uniffi_macros/src/export/callback_interface.rs index fe60b85ae3..23009712e1 100644 --- a/uniffi_macros/src/export/callback_interface.rs +++ b/uniffi_macros/src/export/callback_interface.rs @@ -150,11 +150,11 @@ pub fn ffi_converter_callback_interface_impl( type FfiType = u64; fn try_lift(v: Self::FfiType) -> ::uniffi::deps::anyhow::Result { - Ok(::std::boxed::Box::new(<#trait_impl_ident>::new(v))) + ::std::result::Result::Ok(::std::boxed::Box::new(<#trait_impl_ident>::new(v))) } fn try_read(buf: &mut &[u8]) -> ::uniffi::deps::anyhow::Result { - use uniffi::deps::bytes::Buf; + use ::uniffi::deps::bytes::Buf; ::uniffi::check_remaining(buf, 8)?; #try_lift_self(buf.get_u64()) } @@ -222,7 +222,7 @@ fn gen_method_impl(sig: &FnSignature, vtable_cell: &Ident) -> syn::Result #return_ty { let vtable = #vtable_cell.get(); - let mut uniffi_call_status = ::uniffi::RustCallStatus::default(); + let mut uniffi_call_status: ::uniffi::RustCallStatus = ::std::default::Default::default(); let mut uniffi_return_value: #lift_return_type = ::uniffi::FfiDefault::ffi_default(); (vtable.#ident)(self.handle, #(#lower_exprs,)* &mut uniffi_return_value, &mut uniffi_call_status); #lift_foreign_return(uniffi_return_value, uniffi_call_status) diff --git a/uniffi_macros/src/export/scaffolding.rs b/uniffi_macros/src/export/scaffolding.rs index 1dfbc43a2b..7f4b15a3b0 100644 --- a/uniffi_macros/src/export/scaffolding.rs +++ b/uniffi_macros/src/export/scaffolding.rs @@ -144,9 +144,13 @@ impl ScaffoldingBits { // pointer. quote! { { - let boxed_foreign_arc = unsafe { Box::from_raw(uniffi_self_lowered as *mut ::std::sync::Arc) }; + let boxed_foreign_arc = unsafe { + ::std::boxed::Box::from_raw( + uniffi_self_lowered as *mut ::std::sync::Arc, + ) + }; // Take a clone for our own use. - Ok(*boxed_foreign_arc) + ::std::result::Result::Ok(*boxed_foreign_arc) } } } else { @@ -155,8 +159,10 @@ impl ScaffoldingBits { let lift_closure = sig.lift_closure(Some(quote! { match #try_lift_self { - Ok(v) => v, - Err(e) => return Err(("self", e)) + ::std::result::Result::Ok(v) => v, + ::std::result::Result::Err(e) => { + return ::std::result::Result::Err(("self", e)); + } } })); let call_params = sig.rust_call_params(true); @@ -260,11 +266,11 @@ pub(super) fn gen_ffi_function( let uniffi_lift_args = #lift_closure; ::uniffi::rust_call(call_status, || { #lower_return(match uniffi_lift_args() { - Ok(uniffi_args) => { + ::std::result::Result::Ok(uniffi_args) => { let uniffi_result = #rust_fn_call; #convert_result } - Err((arg_name, anyhow_error)) => { + ::std::result::Result::Err((arg_name, anyhow_error)) => { #handle_failed_lift(arg_name, anyhow_error) }, }) @@ -288,7 +294,7 @@ pub(super) fn gen_ffi_function( ::uniffi::deps::log::debug!(#name); let uniffi_lift_args = #lift_closure; match uniffi_lift_args() { - Ok(uniffi_args) => { + ::std::result::Result::Ok(uniffi_args) => { ::uniffi::rust_future_new::<_, #return_ty, _>( async move { let uniffi_result = #future_expr.await; @@ -297,7 +303,7 @@ pub(super) fn gen_ffi_function( crate::UniFfiTag ) }, - Err((arg_name, anyhow_error)) => { + ::std::result::Result::Err((arg_name, anyhow_error)) => { ::uniffi::rust_future_new::<_, #return_ty, _>( async move { #handle_failed_lift(arg_name, anyhow_error) }, crate::UniFfiTag, @@ -332,7 +338,7 @@ fn ffi_buffer_scaffolding_fn( ) { let mut arg_buf = unsafe { ::std::slice::from_raw_parts(arg_ptr, ::uniffi::ffi_buffer_size!(#(#type_list),*)) }; let mut return_buf = unsafe { ::std::slice::from_raw_parts_mut(return_ptr, ::uniffi::ffi_buffer_size!(#return_type, ::uniffi::RustCallStatus)) }; - let mut out_status = ::uniffi::RustCallStatus::default(); + let mut out_status: ::uniffi::RustCallStatus = ::std::default::Default::default(); let return_value = #fn_ident( #( diff --git a/uniffi_macros/src/export/trait_interface.rs b/uniffi_macros/src/export/trait_interface.rs index 51eb94d031..18d01a5d38 100644 --- a/uniffi_macros/src/export/trait_interface.rs +++ b/uniffi_macros/src/export/trait_interface.rs @@ -54,10 +54,14 @@ pub(super) fn gen_trait_scaffolding( ptr: *const ::std::ffi::c_void, call_status: &mut ::uniffi::RustCallStatus ) -> *const ::std::ffi::c_void { - uniffi::rust_call(call_status, || { - let ptr = ptr as *mut std::sync::Arc; - let arc = unsafe { ::std::sync::Arc::clone(&*ptr) }; - Ok(::std::boxed::Box::into_raw(::std::boxed::Box::new(arc)) as *const ::std::ffi::c_void) + ::uniffi::rust_call(call_status, || { + let ptr = ptr as *mut ::std::sync::Arc; + let arc: ::std::sync::Arc<_> = unsafe { ::std::clone::Clone::clone(&*ptr) }; + ::std::result::Result::Ok( + ::std::boxed::Box::into_raw( + ::std::boxed::Box::new(arc), + ) as *const ::std::ffi::c_void + ) }) } @@ -74,10 +78,14 @@ pub(super) fn gen_trait_scaffolding( ptr: *const ::std::ffi::c_void, call_status: &mut ::uniffi::RustCallStatus ) { - uniffi::rust_call(call_status, || { - assert!(!ptr.is_null()); - drop(unsafe { ::std::boxed::Box::from_raw(ptr as *mut std::sync::Arc) }); - Ok(()) + ::uniffi::rust_call(call_status, || { + ::std::assert!(!ptr.is_null()); + ::std::mem::drop(unsafe { + ::std::boxed::Box::from_raw( + ptr as *mut ::std::sync::Arc, + ) + }); + ::std::result::Result::Ok(()) }); } }; @@ -123,14 +131,16 @@ pub(crate) fn ffi_converter( let trait_impl_ident = callback_interface::trait_impl_ident(&trait_name); quote! { fn try_lift(v: Self::FfiType) -> ::uniffi::deps::anyhow::Result<::std::sync::Arc> { - Ok(::std::sync::Arc::new(<#trait_impl_ident>::new(v as u64))) + ::std::result::Result::Ok(::std::sync::Arc::new(<#trait_impl_ident>::new(v as u64))) } } } else { quote! { fn try_lift(v: Self::FfiType) -> ::uniffi::deps::anyhow::Result<::std::sync::Arc> { unsafe { - Ok(*::std::boxed::Box::from_raw(v as *mut ::std::sync::Arc)) + ::std::result::Result::Ok( + *::std::boxed::Box::from_raw(v as *mut ::std::sync::Arc), + ) } } } @@ -148,7 +158,9 @@ pub(crate) fn ffi_converter( // if they are not, but unfortunately it fails with an unactionably obscure error message. // By asserting the requirement explicitly, we help Rust produce a more scrutable error message // and thus help the user debug why the requirement isn't being met. - uniffi::deps::static_assertions::assert_impl_all!(dyn #trait_ident: ::core::marker::Sync, ::core::marker::Send); + ::uniffi::deps::static_assertions::assert_impl_all!( + dyn #trait_ident: ::core::marker::Sync, ::core::marker::Send + ); unsafe #impl_spec { type FfiType = *const ::std::os::raw::c_void; @@ -159,11 +171,11 @@ pub(crate) fn ffi_converter( #try_lift - fn write(obj: ::std::sync::Arc, buf: &mut Vec) { + fn write(obj: ::std::sync::Arc, buf: &mut ::std::vec::Vec) { ::uniffi::deps::static_assertions::const_assert!(::std::mem::size_of::<*const ::std::ffi::c_void>() <= 8); ::uniffi::deps::bytes::BufMut::put_u64( buf, - #lower_self(obj) as u64, + #lower_self(obj) as ::std::primitive::u64, ); } diff --git a/uniffi_macros/src/export/utrait.rs b/uniffi_macros/src/export/utrait.rs index 7663348931..6e133cedbd 100644 --- a/uniffi_macros/src/export/utrait.rs +++ b/uniffi_macros/src/export/utrait.rs @@ -103,14 +103,14 @@ pub(crate) fn expand_uniffi_trait_export( let method_eq = quote! { fn uniffi_trait_eq_eq(&self, other: &#self_ident) -> bool { use ::std::cmp::PartialEq; - uniffi::deps::static_assertions::assert_impl_all!(#self_ident: PartialEq); // This object has a trait method which requires `PartialEq` be implemented. + ::uniffi::deps::static_assertions::assert_impl_all!(#self_ident: PartialEq); // This object has a trait method which requires `PartialEq` be implemented. PartialEq::eq(self, other) } }; let method_ne = quote! { fn uniffi_trait_eq_ne(&self, other: &#self_ident) -> bool { use ::std::cmp::PartialEq; - uniffi::deps::static_assertions::assert_impl_all!(#self_ident: PartialEq); // This object has a trait method which requires `PartialEq` be implemented. + ::uniffi::deps::static_assertions::assert_impl_all!(#self_ident: PartialEq); // This object has a trait method which requires `PartialEq` be implemented. PartialEq::ne(self, other) } }; diff --git a/uniffi_macros/src/fnsig.rs b/uniffi_macros/src/fnsig.rs index d66aefeae2..19774dc3ba 100644 --- a/uniffi_macros/src/fnsig.rs +++ b/uniffi_macros/src/fnsig.rs @@ -159,14 +159,16 @@ impl FnSignature { let name = &arg.name; quote! { match #try_lift(#ident) { - Ok(v) => v, - Err(e) => return Err((#name, e)), + ::std::result::Result::Ok(v) => v, + ::std::result::Result::Err(e) => { + return ::std::result::Result::Err((#name, e)) + } } } }); let all_lifts = self_lift.into_iter().chain(arg_lifts); quote! { - move || Ok(( + move || ::std::result::Result::Ok(( #(#all_lifts,)* )) } diff --git a/uniffi_macros/src/object.rs b/uniffi_macros/src/object.rs index 280a673d4d..65b17229b0 100644 --- a/uniffi_macros/src/object.rs +++ b/uniffi_macros/src/object.rs @@ -67,9 +67,9 @@ pub fn expand_object(input: DeriveInput, options: DeriveOptions) -> syn::Result< ptr: *const ::std::ffi::c_void, call_status: &mut ::uniffi::RustCallStatus ) -> *const ::std::ffi::c_void { - uniffi::rust_call(call_status, || { + ::uniffi::rust_call(call_status, || { unsafe { ::std::sync::Arc::increment_strong_count(ptr) }; - Ok(ptr) + ::std::result::Result::Ok(ptr) }) } @@ -79,13 +79,13 @@ pub fn expand_object(input: DeriveInput, options: DeriveOptions) -> syn::Result< ptr: *const ::std::ffi::c_void, call_status: &mut ::uniffi::RustCallStatus ) { - uniffi::rust_call(call_status, || { + ::uniffi::rust_call(call_status, || { assert!(!ptr.is_null()); let ptr = ptr.cast::<#ident>(); unsafe { ::std::sync::Arc::decrement_strong_count(ptr); } - Ok(()) + ::std::result::Result::Ok(()) }); } @@ -119,7 +119,9 @@ fn interface_impl(object: &ObjectItem, options: &DeriveOptions) -> TokenStream { // if they are not, but unfortunately it fails with an unactionably obscure error message. // By asserting the requirement explicitly, we help Rust produce a more scrutable error message // and thus help the user debug why the requirement isn't being met. - uniffi::deps::static_assertions::assert_impl_all!(#ident: ::core::marker::Sync, ::core::marker::Send); + ::uniffi::deps::static_assertions::assert_impl_all!( + #ident: ::core::marker::Sync, ::core::marker::Send + ); #[doc(hidden)] #[automatically_derived] @@ -148,7 +150,7 @@ fn interface_impl(object: &ObjectItem, options: &DeriveOptions) -> TokenStream { /// When lifting, we receive an owned `Arc` that the foreign language code cloned. fn try_lift(v: Self::FfiType) -> ::uniffi::Result<::std::sync::Arc> { let v = v as *const #ident; - Ok(unsafe { ::std::sync::Arc::::from_raw(v) }) + ::std::result::Result::Ok(unsafe { ::std::sync::Arc::::from_raw(v) }) } /// When writing as a field of a complex structure, make a clone and transfer ownership @@ -159,9 +161,9 @@ fn interface_impl(object: &ObjectItem, options: &DeriveOptions) -> TokenStream { /// Safety: when freeing the resulting pointer, the foreign-language code must /// call the destructor function specific to the type `T`. Calling the destructor /// function for other types may lead to undefined behaviour. - fn write(obj: ::std::sync::Arc, buf: &mut Vec) { + fn write(obj: ::std::sync::Arc, buf: &mut ::std::vec::Vec) { ::uniffi::deps::static_assertions::const_assert!(::std::mem::size_of::<*const ::std::ffi::c_void>() <= 8); - ::uniffi::deps::bytes::BufMut::put_u64(buf, #lower_arc(obj) as u64); + ::uniffi::deps::bytes::BufMut::put_u64(buf, #lower_arc(obj) as ::std::primitive::u64); } /// When reading as a field of a complex structure, we receive a "borrow" of the `Arc` diff --git a/uniffi_macros/src/record.rs b/uniffi_macros/src/record.rs index ec4b46303b..74629d1915 100644 --- a/uniffi_macros/src/record.rs +++ b/uniffi_macros/src/record.rs @@ -94,7 +94,7 @@ fn record_ffi_converter_impl( } fn try_read(buf: &mut &[::std::primitive::u8]) -> ::uniffi::deps::anyhow::Result { - Ok(Self { #try_read_fields }) + ::std::result::Result::Ok(Self { #try_read_fields }) } const TYPE_ID_META: ::uniffi::MetadataBuffer = ::uniffi::MetadataBuffer::from_code(::uniffi::metadata::codes::TYPE_RECORD) diff --git a/uniffi_macros/src/setup_scaffolding.rs b/uniffi_macros/src/setup_scaffolding.rs index c82e9389bb..83bb5ce83b 100644 --- a/uniffi_macros/src/setup_scaffolding.rs +++ b/uniffi_macros/src/setup_scaffolding.rs @@ -36,7 +36,7 @@ pub fn setup_scaffolding(namespace: String) -> Result { #[allow(clippy::missing_safety_doc, missing_docs)] #[doc(hidden)] #[no_mangle] - pub extern "C" fn #ffi_contract_version_ident() -> u32 { + pub extern "C" fn #ffi_contract_version_ident() -> ::std::primitive::u32 { #UNIFFI_CONTRACT_VERSION } @@ -45,13 +45,15 @@ pub fn setup_scaffolding(namespace: String) -> Result { /// /// See `uniffi_bindgen::macro_metadata` for how this is used. - const #namespace_const_ident: ::uniffi::MetadataBuffer = ::uniffi::MetadataBuffer::from_code(::uniffi::metadata::codes::NAMESPACE) - .concat_str(#module_path) - .concat_str(#namespace); + const #namespace_const_ident: ::uniffi::MetadataBuffer = + ::uniffi::MetadataBuffer::from_code(::uniffi::metadata::codes::NAMESPACE) + .concat_str(#module_path) + .concat_str(#namespace); #[doc(hidden)] #[no_mangle] - pub static #namespace_static_ident: [u8; #namespace_const_ident.size] = #namespace_const_ident.into_array(); + pub static #namespace_static_ident: [::std::primitive::u8; #namespace_const_ident.size] = + #namespace_const_ident.into_array(); // Everybody gets basic buffer support, since it's needed for passing complex types over the FFI. // @@ -60,29 +62,42 @@ pub fn setup_scaffolding(namespace: String) -> Result { #[allow(clippy::missing_safety_doc, missing_docs)] #[doc(hidden)] #[no_mangle] - pub extern "C" fn #ffi_rustbuffer_alloc_ident(size: u64, call_status: &mut uniffi::RustCallStatus) -> uniffi::RustBuffer { - uniffi::ffi::uniffi_rustbuffer_alloc(size, call_status) + pub extern "C" fn #ffi_rustbuffer_alloc_ident( + size: ::std::primitive::u64, + call_status: &mut ::uniffi::RustCallStatus, + ) -> ::uniffi::RustBuffer { + ::uniffi::ffi::uniffi_rustbuffer_alloc(size, call_status) } #[allow(clippy::missing_safety_doc, missing_docs)] #[doc(hidden)] #[no_mangle] - pub unsafe extern "C" fn #ffi_rustbuffer_from_bytes_ident(bytes: uniffi::ForeignBytes, call_status: &mut uniffi::RustCallStatus) -> uniffi::RustBuffer { - uniffi::ffi::uniffi_rustbuffer_from_bytes(bytes, call_status) + pub unsafe extern "C" fn #ffi_rustbuffer_from_bytes_ident( + bytes: ::uniffi::ForeignBytes, + call_status: &mut ::uniffi::RustCallStatus, + ) -> ::uniffi::RustBuffer { + ::uniffi::ffi::uniffi_rustbuffer_from_bytes(bytes, call_status) } #[allow(clippy::missing_safety_doc, missing_docs)] #[doc(hidden)] #[no_mangle] - pub unsafe extern "C" fn #ffi_rustbuffer_free_ident(buf: uniffi::RustBuffer, call_status: &mut uniffi::RustCallStatus) { - uniffi::ffi::uniffi_rustbuffer_free(buf, call_status); + pub unsafe extern "C" fn #ffi_rustbuffer_free_ident( + buf: ::uniffi::RustBuffer, + call_status: &mut ::uniffi::RustCallStatus, + ) { + ::uniffi::ffi::uniffi_rustbuffer_free(buf, call_status); } #[allow(clippy::missing_safety_doc, missing_docs)] #[doc(hidden)] #[no_mangle] - pub unsafe extern "C" fn #ffi_rustbuffer_reserve_ident(buf: uniffi::RustBuffer, additional: u64, call_status: &mut uniffi::RustCallStatus) -> uniffi::RustBuffer { - uniffi::ffi::uniffi_rustbuffer_reserve(buf, additional, call_status) + pub unsafe extern "C" fn #ffi_rustbuffer_reserve_ident( + buf: ::uniffi::RustBuffer, + additional: ::std::primitive::u64, + call_status: &mut ::uniffi::RustCallStatus, + ) -> ::uniffi::RustBuffer { + ::uniffi::ffi::uniffi_rustbuffer_reserve(buf, additional, call_status) } #ffi_rust_future_scaffolding_fns @@ -121,7 +136,9 @@ pub fn setup_scaffolding(namespace: String) -> Result { #[doc(hidden)] pub trait UniffiCustomTypeConverter { type Builtin; - fn into_custom(val: Self::Builtin) -> uniffi::Result where Self: Sized; + fn into_custom(val: Self::Builtin) -> ::uniffi::Result + where + Self: ::std::marker::Sized; fn from_custom(obj: Self) -> Self::Builtin; } }) diff --git a/uniffi_macros/src/test.rs b/uniffi_macros/src/test.rs index da7a343dbc..95acfbc1e6 100644 --- a/uniffi_macros/src/test.rs +++ b/uniffi_macros/src/test.rs @@ -30,16 +30,16 @@ pub(crate) fn build_foreign_language_testcases(tokens: TokenStream) -> TokenStre ); let run_test = match test_file_pathbuf.extension() { Some("kts") => quote! { - uniffi::kotlin_run_test + ::uniffi::kotlin_run_test }, Some("swift") => quote! { - uniffi::swift_run_test + ::uniffi::swift_run_test }, Some("py") => quote! { - uniffi::python_run_test + ::uniffi::python_run_test }, Some("rb") => quote! { - uniffi::ruby_run_test + ::uniffi::ruby_run_test }, _ => panic!("Unexpected extension for test script: {test_file_name}"), }; @@ -51,7 +51,7 @@ pub(crate) fn build_foreign_language_testcases(tokens: TokenStream) -> TokenStre quote! { #maybe_ignore #[test] - fn #test_name () -> uniffi::deps::anyhow::Result<()> { + fn #test_name () -> ::uniffi::deps::anyhow::Result<()> { #run_test( std::env!("CARGO_TARGET_TMPDIR"), std::env!("CARGO_PKG_NAME"), From c26330edec4fe692a57de90aed887476e4b0d225 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Mon, 8 Jul 2024 17:36:24 +0200 Subject: [PATCH 11/62] Kotlin: use `Enum.entries` instead of `Enum.values()` --- uniffi_bindgen/src/bindings/kotlin/templates/EnumTemplate.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/EnumTemplate.kt b/uniffi_bindgen/src/bindings/kotlin/templates/EnumTemplate.kt index 8d1c2235ec..653c7d7dc8 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/EnumTemplate.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/EnumTemplate.kt @@ -29,7 +29,7 @@ enum class {{ type_name }}(val value: {{ variant_discr_type|type_name(ci) }}) { public object {{ e|ffi_converter_name }}: FfiConverterRustBuffer<{{ type_name }}> { override fun read(buf: ByteBuffer) = try { - {{ type_name }}.values()[buf.getInt() - 1] + {{ type_name }}.entries[buf.getInt() - 1] } catch (e: IndexOutOfBoundsException) { throw RuntimeException("invalid enum value, something is very wrong!!", e) } From 3c6521a7891ed7a38024d8f2856a9a1be7ed6ccb Mon Sep 17 00:00:00 2001 From: Mark Hammond Date: Thu, 11 Jul 2024 20:48:55 -0400 Subject: [PATCH 12/62] Add a cargo_metadata feature so using cargo_metadata can be avoided in some cases (#2175) --- uniffi_bindgen/Cargo.toml | 6 +- uniffi_bindgen/src/bindings/swift/test.rs | 15 +-- uniffi_bindgen/src/lib.rs | 13 +-- uniffi_bindgen/src/library_mode.rs | 114 +++++++++++++--------- uniffi_testing/src/lib.rs | 27 ++--- 5 files changed, 98 insertions(+), 77 deletions(-) diff --git a/uniffi_bindgen/Cargo.toml b/uniffi_bindgen/Cargo.toml index 75c0a4c47f..0f9457e612 100644 --- a/uniffi_bindgen/Cargo.toml +++ b/uniffi_bindgen/Cargo.toml @@ -11,11 +11,15 @@ edition = "2021" keywords = ["ffi", "bindgen"] readme = "../README.md" +[features] +default = ["cargo_metadata"] +cargo_metadata = ["dep:cargo_metadata"] + [dependencies] anyhow = "1" askama = { version = "0.12", default-features = false, features = ["config"] } camino = "1.0.8" -cargo_metadata = "0.15" +cargo_metadata = { version = "0.15", optional = true } fs-err = "2.7.0" glob = "0.3" goblin = "0.8" diff --git a/uniffi_bindgen/src/bindings/swift/test.rs b/uniffi_bindgen/src/bindings/swift/test.rs index 7417775295..2bc98b6163 100644 --- a/uniffi_bindgen/src/bindings/swift/test.rs +++ b/uniffi_bindgen/src/bindings/swift/test.rs @@ -15,10 +15,10 @@ use std::process::{Command, Stdio}; use uniffi_testing::UniFFITestHelper; /// Run Swift tests for a UniFFI test fixture -pub fn run_test(tmp_dir: &str, fixture_name: &str, script_file: &str) -> Result<()> { +pub fn run_test(tmp_dir: &str, package_name: &str, script_file: &str) -> Result<()> { run_script( tmp_dir, - fixture_name, + package_name, script_file, vec![], &RunScriptOptions::default(), @@ -30,16 +30,17 @@ pub fn run_test(tmp_dir: &str, fixture_name: &str, script_file: &str) -> Result< /// This function will set things up so that the script can import the UniFFI bindings for a crate pub fn run_script( tmp_dir: &str, - crate_name: &str, + package_name: &str, script_file: &str, args: Vec, options: &RunScriptOptions, ) -> Result<()> { let script_path = Utf8Path::new(script_file).canonicalize_utf8()?; - let test_helper = UniFFITestHelper::new(crate_name)?; + let test_helper = UniFFITestHelper::new(package_name)?; let out_dir = test_helper.create_out_dir(tmp_dir, &script_path)?; let cdylib_path = test_helper.copy_cdylib_to_out_dir(&out_dir)?; - let generated_sources = GeneratedSources::new(crate_name, &cdylib_path, &out_dir)?; + let generated_sources = + GeneratedSources::new(test_helper.crate_name(), &cdylib_path, &out_dir)?; // Compile the generated sources together to create a single swift module compile_swift_module( @@ -124,7 +125,7 @@ struct GeneratedSources { } impl GeneratedSources { - fn new(package_name: &str, cdylib_path: &Utf8Path, out_dir: &Utf8Path) -> Result { + fn new(crate_name: &str, cdylib_path: &Utf8Path, out_dir: &Utf8Path) -> Result { let sources = generate_bindings( cdylib_path, None, @@ -135,7 +136,7 @@ impl GeneratedSources { )?; let main_source = sources .iter() - .find(|s| s.package_name.as_deref() == Some(package_name)) + .find(|s| s.ci.crate_name() == crate_name) .unwrap(); let main_module = main_source.config.module_name(); let modulemap_glob = glob(&out_dir.join("*.modulemap"))?; diff --git a/uniffi_bindgen/src/lib.rs b/uniffi_bindgen/src/lib.rs index 18f20f3ad6..9e7d121c45 100644 --- a/uniffi_bindgen/src/lib.rs +++ b/uniffi_bindgen/src/lib.rs @@ -162,7 +162,6 @@ pub trait BindingGenerator: Sized { pub struct Component { pub ci: ComponentInterface, pub config: Config, - pub package_name: Option, } /// A convenience function for the CLI to help avoid using static libs @@ -207,7 +206,7 @@ pub fn generate_external_bindings( let config_file_override = config_file_override.as_ref().map(|p| p.as_ref()); let config = { - let toml = load_initial_config(crate_root, config_file_override)?; + let toml = load_initial_config(Some(crate_root), config_file_override)?; binding_generator.new_config(&toml)? }; @@ -225,11 +224,7 @@ pub fn generate_external_bindings( try_format_code, }; - let mut components = vec![Component { - ci, - config, - package_name: None, - }]; + let mut components = vec![Component { ci, config }]; binding_generator.update_component_configs(&settings, &mut components)?; binding_generator.write_bindings(&settings, &components) } @@ -411,10 +406,10 @@ fn load_toml_file(source: Option<&Utf8Path>) -> Result, config_file_override: Option<&Utf8Path>, ) -> Result { - let mut config = load_toml_file(Some(crate_root.join("uniffi.toml").as_path())) + let mut config = load_toml_file(crate_root.map(|p| p.join("uniffi.toml")).as_deref()) .context("default config")? .unwrap_or(toml::value::Table::default()); diff --git a/uniffi_bindgen/src/library_mode.rs b/uniffi_bindgen/src/library_mode.rs index 47dfc75504..5fcef6f806 100644 --- a/uniffi_bindgen/src/library_mode.rs +++ b/uniffi_bindgen/src/library_mode.rs @@ -20,8 +20,7 @@ use crate::{ GenerationSettings, Result, }; use anyhow::{bail, Context}; -use camino::Utf8Path; -use cargo_metadata::{MetadataCommand, Package}; +use camino::{Utf8Path, Utf8PathBuf}; use std::{collections::HashMap, fs}; use uniffi_meta::{ create_metadata_groups, fixup_external_type, group_metadata, Metadata, MetadataGroup, @@ -42,24 +41,14 @@ pub fn generate_bindings( out_dir: &Utf8Path, try_format_code: bool, ) -> Result>> { - let cargo_metadata = MetadataCommand::new() - .exec() - .context("error running cargo metadata")?; - - let mut components = find_components(&cargo_metadata, library_path)? + let path_map = CratePathMap::new()?; + let mut components = find_components(&path_map, library_path)? .into_iter() - .map(|(ci, package)| { - let crate_root = package - .manifest_path - .parent() - .context("manifest path has no parent")?; + .map(|ci| { + let crate_root = path_map.get_path(ci.crate_name()); let config = binding_generator .new_config(&load_initial_config(crate_root, config_file_override)?)?; - Ok(Component { - ci, - config, - package_name: Some(package.name), - }) + Ok(Component { ci, config }) }) .collect::>>()?; @@ -102,9 +91,9 @@ pub fn calc_cdylib_name(library_path: &Utf8Path) -> Option<&str> { } fn find_components( - cargo_metadata: &cargo_metadata::Metadata, + crates: &CratePathMap, library_path: &Utf8Path, -) -> Result> { +) -> Result> { let items = macro_metadata::extract_from_library(library_path)?; let mut metadata_groups = create_metadata_groups(&items); group_metadata(&mut metadata_groups, items)?; @@ -114,13 +103,10 @@ fn find_components( let mut udl_items: HashMap = HashMap::new(); for group in metadata_groups.values() { - let package = find_package_by_crate_name(cargo_metadata, &group.namespace.crate_name)?; - let crate_root = package - .manifest_path - .parent() - .context("manifest path has no parent")?; let crate_name = group.namespace.crate_name.clone(); - if let Some(mut metadata_group) = load_udl_metadata(group, crate_root, &crate_name)? { + let path = crates.get_path(&crate_name); + + if let Some(mut metadata_group) = load_udl_metadata(group, path, &crate_name)? { // fixup the items. metadata_group.items = metadata_group .items @@ -138,39 +124,68 @@ fn find_components( metadata_groups .into_values() .map(|group| { - let package = find_package_by_crate_name(cargo_metadata, &group.namespace.crate_name)?; - let mut ci = ComponentInterface::new(&group.namespace.crate_name); - if let Some(metadata) = udl_items.remove(&group.namespace.crate_name) { + let crate_name = &group.namespace.crate_name; + let mut ci = ComponentInterface::new(crate_name); + if let Some(metadata) = udl_items.remove(crate_name) { ci.add_metadata(metadata)?; }; ci.add_metadata(group)?; - Ok((ci, package)) + Ok(ci) }) .collect() } -fn find_package_by_crate_name( - metadata: &cargo_metadata::Metadata, - crate_name: &str, -) -> Result { - let matching: Vec<&Package> = metadata - .packages - .iter() - .filter(|p| { - p.targets - .iter() - .any(|t| t.name.replace('-', "_") == crate_name) - }) - .collect(); - match matching.len() { - 1 => Ok(matching[0].clone()), - n => bail!("cargo metadata returned {n} packages for crate name {crate_name}"), +#[derive(Debug, Clone, Default)] +struct CratePathMap { + // path on disk where we will find the crate's source. + paths: HashMap, +} + +impl CratePathMap { + // TODO: we probably need external overrides passed in (maybe via + // `config_file_override` - what are the semantics of that?) + // Anyway: for now we just do this. + #[cfg(feature = "cargo_metadata")] + fn new() -> Result { + Ok(Self::from( + cargo_metadata::MetadataCommand::new() + .exec() + .context("error running cargo metadata")?, + )) + } + + #[cfg(not(feature = "cargo_metadata"))] + fn new() -> Result { + Ok(Self::default()) + } + + fn get_path(&self, crate_name: &str) -> Option<&Utf8Path> { + self.paths.get(crate_name).map(|p| p.as_path()) + } +} + +// all this to get a few paths!? +#[cfg(feature = "cargo_metadata")] +impl From for CratePathMap { + fn from(metadata: cargo_metadata::Metadata) -> Self { + let paths: HashMap = metadata + .packages + .iter() + .flat_map(|p| { + p.targets.iter().filter(|t| t.is_lib()).filter_map(|t| { + p.manifest_path + .parent() + .map(|p| (t.name.replace('-', "_"), p.to_owned())) + }) + }) + .collect(); + Self { paths } } } fn load_udl_metadata( group: &MetadataGroup, - crate_root: &Utf8Path, + crate_root: Option<&Utf8Path>, crate_name: &str, ) -> Result> { let udl_items = group @@ -194,7 +209,10 @@ fn load_udl_metadata( ); } let ci_name = &udl_items[0].file_stub; - let ci_path = crate_root.join("src").join(format!("{ci_name}.udl")); + let ci_path = crate_root + .context(format!("No path known to '{ci_name}.udl'"))? + .join("src") + .join(format!("{ci_name}.udl")); if ci_path.exists() { let udl = fs::read_to_string(ci_path)?; let udl_group = uniffi_udl::parse_udl(&udl, crate_name)?; @@ -203,7 +221,7 @@ fn load_udl_metadata( bail!("{ci_path} not found"); } } - n => bail!("{n} UDL files found for {crate_root}"), + n => bail!("{n} UDL files found for {crate_name}"), } } diff --git a/uniffi_testing/src/lib.rs b/uniffi_testing/src/lib.rs index f8a0d8499a..54dc247d71 100644 --- a/uniffi_testing/src/lib.rs +++ b/uniffi_testing/src/lib.rs @@ -37,17 +37,15 @@ static CARGO_BUILD_MESSAGES: Lazy> = Lazy::new(get_cargo_build_mess /// - The fixture crate produces a cdylib library /// - The fixture crate, and any external-crates, has 1 UDL file in it's src/ directory pub struct UniFFITestHelper { - name: String, - package: Package, + crate_name: String, + cdylib: Utf8PathBuf, } impl UniFFITestHelper { - pub fn new(name: &str) -> Result { - let package = Self::find_package(name)?; - Ok(Self { - name: name.to_string(), - package, - }) + pub fn new(package_name: &str) -> Result { + let package = Self::find_package(package_name)?; + let (crate_name, cdylib) = Self::find_name_and_cdylib_path(&package)?; + Ok(Self { crate_name, cdylib }) } fn find_package(name: &str) -> Result { @@ -62,7 +60,7 @@ impl UniFFITestHelper { } } - fn find_cdylib_path(package: &Package) -> Result { + fn find_name_and_cdylib_path(package: &Package) -> Result<(String, Utf8PathBuf)> { let cdylib_targets: Vec<&Target> = package .targets .iter() @@ -72,6 +70,7 @@ impl UniFFITestHelper { 1 => cdylib_targets[0], n => bail!("Found {n} cdylib targets for {}", package.name), }; + let target_name = target.name.replace('-', "_"); let artifacts = CARGO_BUILD_MESSAGES .iter() @@ -97,7 +96,7 @@ impl UniFFITestHelper { .collect(); match cdylib_files.len() { - 1 => Ok(cdylib_files[0].to_owned()), + 1 => Ok((target_name, cdylib_files[0].to_owned())), n => bail!("Found {n} cdylib files for {}", package.name), } } @@ -119,7 +118,7 @@ impl UniFFITestHelper { temp_dir: impl AsRef, script_path: impl AsRef, ) -> Result { - let dirname = format!("{}-{}", self.name, hash_path(script_path.as_ref())); + let dirname = format!("{}-{}", self.crate_name, hash_path(script_path.as_ref())); let out_dir = temp_dir.as_ref().join(dirname); if out_dir.exists() { // Clean out any files from previous runs @@ -144,7 +143,11 @@ impl UniFFITestHelper { /// Get the path to the cdylib file for this package pub fn cdylib_path(&self) -> Result { - Self::find_cdylib_path(&self.package) + Ok(self.cdylib.clone()) + } + + pub fn crate_name(&self) -> &str { + &self.crate_name } } From bd83dbe0852724936c5d8a11e91ceb65f5aa6804 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Fri, 12 Jul 2024 18:54:27 +0200 Subject: [PATCH 13/62] Add `use_enum_entries` configuration option to enable the `EnumClass.entries` usage on `Kotlin >= 1.9.0` and keep backwards compatibility with previous versions --- docs/manual/src/kotlin/configuration.md | 17 +++++++++-------- .../src/bindings/kotlin/gen_kotlin/mod.rs | 6 ++++++ .../bindings/kotlin/templates/EnumTemplate.kt | 4 ++++ 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/docs/manual/src/kotlin/configuration.md b/docs/manual/src/kotlin/configuration.md index 19ae2a745b..029baba415 100644 --- a/docs/manual/src/kotlin/configuration.md +++ b/docs/manual/src/kotlin/configuration.md @@ -4,15 +4,16 @@ The generated Kotlin modules can be configured using a `uniffi.toml` configurati ## Available options -| Configuration name | Default | Description | -| ------------------ | ------- |------------ | -| `package_name` | `uniffi` | The Kotlin package name - ie, the value used in the `package` statement at the top of generated files. | -| `cdylib_name` | `uniffi_{namespace}`[^1] | The name of the compiled Rust library containing the FFI implementation (not needed when using `generate --library`). | +| Configuration name | Default | Description | +|------------------------------| ------- |------------ | +| `package_name` | `uniffi` | The Kotlin package name - ie, the value used in the `package` statement at the top of generated files. | +| `cdylib_name` | `uniffi_{namespace}`[^1] | The name of the compiled Rust library containing the FFI implementation (not needed when using `generate --library`). | | `generate_immutable_records` | `false` | Whether to generate records with immutable fields (`val` instead of `var`). | -| `custom_types` | | A map which controls how custom types are exposed to Kotlin. See the [custom types section of the manual](../udl/custom_types.md#custom-types-in-the-bindings-code)| -| `external_packages` | | A map of packages to be used for the specified external crates. The key is the Rust crate name, the value is the Kotlin package which will be used referring to types in that crate. See the [external types section of the manual](../udl/ext_types_external.md#kotlin) -| `android` | `false` | Used to toggle on Android specific optimizations -| `android_cleaner` | `android` | Use the [`android.system.SystemCleaner`](https://developer.android.com/reference/android/system/SystemCleaner) instead of [`java.lang.ref.Cleaner`](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/ref/Cleaner.html). Fallback in both instances is the one shipped with JNA. +| `custom_types` | | A map which controls how custom types are exposed to Kotlin. See the [custom types section of the manual](../udl/custom_types.md#custom-types-in-the-bindings-code)| +| `external_packages` | | A map of packages to be used for the specified external crates. The key is the Rust crate name, the value is the Kotlin package which will be used referring to types in that crate. See the [external types section of the manual](../udl/ext_types_external.md#kotlin) +| `android` | `false` | Used to toggle on Android specific optimizations +| `android_cleaner` | `android` | Use the [`android.system.SystemCleaner`](https://developer.android.com/reference/android/system/SystemCleaner) instead of [`java.lang.ref.Cleaner`](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/ref/Cleaner.html). Fallback in both instances is the one shipped with JNA. +| `use_enum_entries` | `false` | Uses `EnumClass.entries` instead of `EnumClass.values()` in the bindings, which is more efficient as it reuses the same list instance every time. Note this is only available since Kotlin 1.9.0 ## Example diff --git a/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/mod.rs b/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/mod.rs index e6faf8ddfa..d979f9ece7 100644 --- a/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/mod.rs +++ b/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/mod.rs @@ -82,12 +82,18 @@ pub struct Config { android: bool, #[serde(default)] android_cleaner: Option, + #[serde(default)] + use_enum_entries: bool, } impl Config { pub(crate) fn android_cleaner(&self) -> bool { self.android_cleaner.unwrap_or(self.android) } + + pub(crate) fn use_enum_entries(&self) -> bool { + self.use_enum_entries + } } #[derive(Debug, Default, Clone, Serialize, Deserialize)] diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/EnumTemplate.kt b/uniffi_bindgen/src/bindings/kotlin/templates/EnumTemplate.kt index 653c7d7dc8..1c17799633 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/EnumTemplate.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/EnumTemplate.kt @@ -29,7 +29,11 @@ enum class {{ type_name }}(val value: {{ variant_discr_type|type_name(ci) }}) { public object {{ e|ffi_converter_name }}: FfiConverterRustBuffer<{{ type_name }}> { override fun read(buf: ByteBuffer) = try { + {% if config.use_enum_entries() %} {{ type_name }}.entries[buf.getInt() - 1] + {% else -%} + {{ type_name }}.values()[buf.getInt() - 1] + {%- endif %} } catch (e: IndexOutOfBoundsException) { throw RuntimeException("invalid enum value, something is very wrong!!", e) } From 75a06ea851b72b44f8d20ba6ce22c630e895dc2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Fri, 12 Jul 2024 21:33:27 +0200 Subject: [PATCH 14/62] Use `kotlin_target_version` instead of `use_enum_entries` --- docs/manual/src/kotlin/configuration.md | 18 +++++++-------- .../src/bindings/kotlin/gen_kotlin/mod.rs | 22 ++++++++++++++++--- 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/docs/manual/src/kotlin/configuration.md b/docs/manual/src/kotlin/configuration.md index 029baba415..3473873516 100644 --- a/docs/manual/src/kotlin/configuration.md +++ b/docs/manual/src/kotlin/configuration.md @@ -4,16 +4,16 @@ The generated Kotlin modules can be configured using a `uniffi.toml` configurati ## Available options -| Configuration name | Default | Description | -|------------------------------| ------- |------------ | -| `package_name` | `uniffi` | The Kotlin package name - ie, the value used in the `package` statement at the top of generated files. | +| Configuration name | Default | Description | +|------------------------------|--------------------------|------------ | +| `package_name` | `uniffi` | The Kotlin package name - ie, the value used in the `package` statement at the top of generated files. | | `cdylib_name` | `uniffi_{namespace}`[^1] | The name of the compiled Rust library containing the FFI implementation (not needed when using `generate --library`). | -| `generate_immutable_records` | `false` | Whether to generate records with immutable fields (`val` instead of `var`). | -| `custom_types` | | A map which controls how custom types are exposed to Kotlin. See the [custom types section of the manual](../udl/custom_types.md#custom-types-in-the-bindings-code)| -| `external_packages` | | A map of packages to be used for the specified external crates. The key is the Rust crate name, the value is the Kotlin package which will be used referring to types in that crate. See the [external types section of the manual](../udl/ext_types_external.md#kotlin) -| `android` | `false` | Used to toggle on Android specific optimizations -| `android_cleaner` | `android` | Use the [`android.system.SystemCleaner`](https://developer.android.com/reference/android/system/SystemCleaner) instead of [`java.lang.ref.Cleaner`](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/ref/Cleaner.html). Fallback in both instances is the one shipped with JNA. -| `use_enum_entries` | `false` | Uses `EnumClass.entries` instead of `EnumClass.values()` in the bindings, which is more efficient as it reuses the same list instance every time. Note this is only available since Kotlin 1.9.0 +| `generate_immutable_records` | `false` | Whether to generate records with immutable fields (`val` instead of `var`). | +| `custom_types` | | A map which controls how custom types are exposed to Kotlin. See the [custom types section of the manual](../udl/custom_types.md#custom-types-in-the-bindings-code)| +| `external_packages` | | A map of packages to be used for the specified external crates. The key is the Rust crate name, the value is the Kotlin package which will be used referring to types in that crate. See the [external types section of the manual](../udl/ext_types_external.md#kotlin) +| `android` | `false` | Used to toggle on Android specific optimizations +| `android_cleaner` | `android` | Use the [`android.system.SystemCleaner`](https://developer.android.com/reference/android/system/SystemCleaner) instead of [`java.lang.ref.Cleaner`](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/ref/Cleaner.html). Fallback in both instances is the one shipped with JNA. +| `kotlin_target_version` | `"x.y.z"` | When provided, it will enable features in the bindings supported for this version. The build process will fail if an invalid format is used. ## Example diff --git a/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/mod.rs b/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/mod.rs index d979f9ece7..968e07a0c2 100644 --- a/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/mod.rs +++ b/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/mod.rs @@ -9,7 +9,8 @@ use std::fmt::Debug; use anyhow::{Context, Result}; use askama::Template; - +use cargo_metadata::semver::Version; +use glob::Pattern; use heck::{ToLowerCamelCase, ToShoutySnakeCase, ToUpperCamelCase}; use serde::{Deserialize, Serialize}; @@ -83,7 +84,7 @@ pub struct Config { #[serde(default)] android_cleaner: Option, #[serde(default)] - use_enum_entries: bool, + kotlin_target_version: Option, } impl Config { @@ -92,7 +93,22 @@ impl Config { } pub(crate) fn use_enum_entries(&self) -> bool { - self.use_enum_entries + self.get_kotlin_version() >= Version::new(1, 9, 0) + } + + /// Returns a `Version` with the contents of `kotlin_target_version`. + /// If `kotlin_target_version` is not defined, version `0.0.0` will be used as a fallback. + /// If it's not valid, this function will panic. + fn get_kotlin_version(&self) -> Version { + self.kotlin_target_version + .clone() + .map(|v| { + Version::parse(&v).expect(&format!( + "Provided Kotlin target version is not valid: {}", + v + )) + }) + .unwrap_or(Version::new(0, 0, 0)) } } From 85f861451a689ebca5242f2d538e96d0458fa2b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Fri, 12 Jul 2024 22:15:43 +0200 Subject: [PATCH 15/62] Remove unused import --- uniffi_bindgen/src/bindings/kotlin/gen_kotlin/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/mod.rs b/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/mod.rs index 968e07a0c2..6c094689e1 100644 --- a/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/mod.rs +++ b/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/mod.rs @@ -10,7 +10,6 @@ use std::fmt::Debug; use anyhow::{Context, Result}; use askama::Template; use cargo_metadata::semver::Version; -use glob::Pattern; use heck::{ToLowerCamelCase, ToShoutySnakeCase, ToUpperCamelCase}; use serde::{Deserialize, Serialize}; From 1dfd0a5abe67a86e2bd64900b26ca3535d97938a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Fri, 12 Jul 2024 22:50:51 +0200 Subject: [PATCH 16/62] Fix clippy issue --- uniffi_bindgen/src/bindings/kotlin/gen_kotlin/mod.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/mod.rs b/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/mod.rs index 6c094689e1..de62dd7d9a 100644 --- a/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/mod.rs +++ b/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/mod.rs @@ -102,10 +102,9 @@ impl Config { self.kotlin_target_version .clone() .map(|v| { - Version::parse(&v).expect(&format!( - "Provided Kotlin target version is not valid: {}", - v - )) + Version::parse(&v).unwrap_or_else(|_| { + panic!("Provided Kotlin target version is not valid: {}", v) + }) }) .unwrap_or(Version::new(0, 0, 0)) } From 4803eb4d6ff32fd51456be084b4797080860d24a Mon Sep 17 00:00:00 2001 From: Mark Hammond Date: Fri, 12 Jul 2024 23:40:00 -0400 Subject: [PATCH 17/62] Only invoke cargo metadata for desired targets (fixes #2183) (#2185) --- uniffi_bindgen/src/library_mode.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/uniffi_bindgen/src/library_mode.rs b/uniffi_bindgen/src/library_mode.rs index 5fcef6f806..0c110dc722 100644 --- a/uniffi_bindgen/src/library_mode.rs +++ b/uniffi_bindgen/src/library_mode.rs @@ -149,6 +149,7 @@ impl CratePathMap { fn new() -> Result { Ok(Self::from( cargo_metadata::MetadataCommand::new() + .no_deps() .exec() .context("error running cargo metadata")?, )) From 6a07d0a050878934eacb98e074b40c201a2933e1 Mon Sep 17 00:00:00 2001 From: Ben Dean-Kawamura Date: Fri, 12 Jul 2024 19:47:37 -0400 Subject: [PATCH 18/62] Don't use cargo_metadata to parse version numbers We're moving towards making this an optional dependency. --- .../src/bindings/kotlin/gen_kotlin/mod.rs | 66 +++++++++++++++++-- 1 file changed, 60 insertions(+), 6 deletions(-) diff --git a/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/mod.rs b/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/mod.rs index de62dd7d9a..2b99d8be3b 100644 --- a/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/mod.rs +++ b/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/mod.rs @@ -7,9 +7,8 @@ use std::cell::RefCell; use std::collections::{BTreeSet, HashMap, HashSet}; use std::fmt::Debug; -use anyhow::{Context, Result}; +use anyhow::{anyhow, Context, Result}; use askama::Template; -use cargo_metadata::semver::Version; use heck::{ToLowerCamelCase, ToShoutySnakeCase, ToUpperCamelCase}; use serde::{Deserialize, Serialize}; @@ -92,21 +91,49 @@ impl Config { } pub(crate) fn use_enum_entries(&self) -> bool { - self.get_kotlin_version() >= Version::new(1, 9, 0) + self.get_kotlin_version() >= KotlinVersion::new(1, 9, 0) } /// Returns a `Version` with the contents of `kotlin_target_version`. /// If `kotlin_target_version` is not defined, version `0.0.0` will be used as a fallback. /// If it's not valid, this function will panic. - fn get_kotlin_version(&self) -> Version { + fn get_kotlin_version(&self) -> KotlinVersion { self.kotlin_target_version .clone() .map(|v| { - Version::parse(&v).unwrap_or_else(|_| { + KotlinVersion::parse(&v).unwrap_or_else(|_| { panic!("Provided Kotlin target version is not valid: {}", v) }) }) - .unwrap_or(Version::new(0, 0, 0)) + .unwrap_or(KotlinVersion::new(0, 0, 0)) + } +} + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] +struct KotlinVersion((u16, u16, u16)); + +impl KotlinVersion { + fn new(major: u16, minor: u16, patch: u16) -> Self { + Self((major, minor, patch)) + } + + fn parse(version: &str) -> Result { + let components = version + .split('.') + .map(|n| { + n.parse::() + .map_err(|_| anyhow!("Invalid version string ({n} is not an integer)")) + }) + .collect::>>()?; + + match components.as_slice() { + [major, minor, patch] => Ok(Self((*major, *minor, *patch))), + [major, minor] => Ok(Self((*major, *minor, 0))), + [major] => Ok(Self((*major, 0, 0))), + _ => Err(anyhow!( + "Invalid version string (expected 1-3 components): {version}" + )), + } } } @@ -694,3 +721,30 @@ mod filters { Ok(textwrap::indent(&wrapped, &" ".repeat(spaces))) } } + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_kotlin_version() { + assert_eq!( + KotlinVersion::parse("1.2.3").unwrap(), + KotlinVersion::new(1, 2, 3) + ); + assert_eq!( + KotlinVersion::parse("2.3").unwrap(), + KotlinVersion::new(2, 3, 0), + ); + assert_eq!( + KotlinVersion::parse("2").unwrap(), + KotlinVersion::new(2, 0, 0), + ); + assert!(KotlinVersion::parse("2.").is_err()); + assert!(KotlinVersion::parse("").is_err()); + assert!(KotlinVersion::parse("A.B.C").is_err()); + assert!(KotlinVersion::new(1, 2, 3) > KotlinVersion::new(0, 1, 2)); + assert!(KotlinVersion::new(1, 2, 3) > KotlinVersion::new(0, 100, 0)); + assert!(KotlinVersion::new(10, 0, 0) > KotlinVersion::new(1, 10, 0)); + } +} From cf594980d690a9aa98d92e14d314963f2045c4cf Mon Sep 17 00:00:00 2001 From: Ben Dean-Kawamura Date: Sat, 13 Jul 2024 15:20:16 -0400 Subject: [PATCH 19/62] Don't panic for try_lift errors This avoids crashing for consumers that set panic=abort (Firefox). - Updated the Err type for `rust_call`. The closure can now return an internal error. The only way to signal an internal error before was to panic. - Reworked the `LowerReturn` trait and the future code to work with the new system. - Added a type for argument lift errors, this makes the rustfuture signatures nicer. --- uniffi_core/src/ffi/rustcalls.rs | 72 +++++++++++++++++++----- uniffi_core/src/ffi/rustfuture/future.rs | 19 ++++--- uniffi_core/src/ffi/rustfuture/mod.rs | 4 +- uniffi_core/src/ffi/rustfuture/tests.rs | 54 +++++++++++++++--- uniffi_core/src/ffi_converter_impls.rs | 22 +++++--- uniffi_core/src/ffi_converter_traits.rs | 41 ++++++++------ uniffi_macros/src/export/scaffolding.rs | 37 ++++++------ uniffi_macros/src/object.rs | 2 +- 8 files changed, 171 insertions(+), 80 deletions(-) diff --git a/uniffi_core/src/ffi/rustcalls.rs b/uniffi_core/src/ffi/rustcalls.rs index cdc02b91d0..c4cd1ac03a 100644 --- a/uniffi_core/src/ffi/rustcalls.rs +++ b/uniffi_core/src/ffi/rustcalls.rs @@ -111,6 +111,29 @@ impl TryFrom for RustCallStatusCode { } } +/// Error type for Rust scaffolding calls +/// +/// This enum represents the fact that there are two ways for a scaffolding call to fail: +/// - Expected errors (the Rust function returned an `Err` value). +/// - Unexpected errors (there was a failure calling the Rust function, for example the failure to +/// lift the arguments). +pub enum RustCallError { + /// The Rust function returned an `Err` value. + /// + /// The associated value is the serialized `Err` value. + Error(RustBuffer), + /// There was a failure to call the Rust function, for example a failure to lift the arguments. + /// + /// The associated value is a message string for the error. + InternalError(String), +} + +/// Error when trying to lift arguments to pass to the scaffolding call +pub struct LiftArgsError { + pub arg_name: &'static str, + pub error: anyhow::Error, +} + /// Handle a scaffolding calls /// /// `callback` is responsible for making the actual Rust call and returning a special result type: @@ -132,7 +155,7 @@ impl TryFrom for RustCallStatusCode { /// - `FfiDefault::ffi_default()` is returned, although foreign code should ignore this value pub fn rust_call(out_status: &mut RustCallStatus, callback: F) -> R where - F: panic::UnwindSafe + FnOnce() -> Result, + F: panic::UnwindSafe + FnOnce() -> Result, R: FfiDefault, { rust_call_with_out_status(out_status, callback).unwrap_or_else(R::ffi_default) @@ -149,7 +172,7 @@ pub(crate) fn rust_call_with_out_status( callback: F, ) -> Option where - F: panic::UnwindSafe + FnOnce() -> Result, + F: panic::UnwindSafe + FnOnce() -> Result, { let result = panic::catch_unwind(|| { crate::panichook::ensure_setup(); @@ -160,11 +183,16 @@ where // initializes it to [RustCallStatusCode::Success] Ok(Ok(v)) => Some(v), // Callback returned an Err. - Ok(Err(buf)) => { + Ok(Err(RustCallError::Error(buf))) => { out_status.code = RustCallStatusCode::Error; *out_status.error_buf = buf; None } + Ok(Err(RustCallError::InternalError(msg))) => { + out_status.code = RustCallStatusCode::UnexpectedError; + *out_status.error_buf = >::lower(msg); + None + } // Callback panicked Err(cause) => { out_status.code = RustCallStatusCode::UnexpectedError; @@ -199,27 +227,25 @@ where mod test { use super::*; use crate::{test_util::TestError, Lift, LowerReturn}; - - fn test_callback(a: u8) -> Result { - match a { - 0 => Ok(100), - 1 => Err(TestError("Error".to_owned())), - x => panic!("Unexpected value: {x}"), - } - } + use anyhow::anyhow; #[test] fn test_rust_call() { + // Successful call let mut status = RustCallStatus::default(); let return_value = rust_call(&mut status, || { - as LowerReturn>::lower_return(test_callback(0)) + as LowerReturn>::lower_return(Ok(100)) }); assert_eq!(status.code, RustCallStatusCode::Success); assert_eq!(return_value, 100); + // Successful call that returns an Err value + let mut status = RustCallStatus::default(); rust_call(&mut status, || { - as LowerReturn>::lower_return(test_callback(1)) + as LowerReturn>::lower_return(Err(TestError( + "Error".into(), + ))) }); assert_eq!(status.code, RustCallStatusCode::Error); assert_eq!( @@ -228,15 +254,31 @@ mod test { TestError("Error".to_owned()) ); + // Internal error while trying to make the call let mut status = RustCallStatus::default(); rust_call(&mut status, || { - as LowerReturn>::lower_return(test_callback(2)) + as LowerReturn>::handle_failed_lift(LiftArgsError { + arg_name: "foo", + error: anyhow!("invalid handle"), + }) + }); + assert_eq!(status.code, RustCallStatusCode::UnexpectedError); + assert_eq!( + >::try_lift(ManuallyDrop::into_inner(status.error_buf)) + .unwrap(), + "Failed to convert arg 'foo': invalid handle" + ); + + // Panic inside the call + let mut status = RustCallStatus::default(); + rust_call(&mut status, || -> Result { + panic!("I crashed") }); assert_eq!(status.code, RustCallStatusCode::UnexpectedError); assert_eq!( >::try_lift(ManuallyDrop::into_inner(status.error_buf)) .unwrap(), - "Unexpected value: 2" + "I crashed" ); } } diff --git a/uniffi_core/src/ffi/rustfuture/future.rs b/uniffi_core/src/ffi/rustfuture/future.rs index 93c34e7543..ee468ffb97 100644 --- a/uniffi_core/src/ffi/rustfuture/future.rs +++ b/uniffi_core/src/ffi/rustfuture/future.rs @@ -86,13 +86,13 @@ use std::{ }; use super::{RustFutureContinuationCallback, RustFuturePoll, Scheduler}; -use crate::{rust_call_with_out_status, FfiDefault, LowerReturn, RustCallStatus}; +use crate::{rust_call_with_out_status, FfiDefault, LiftArgsError, LowerReturn, RustCallStatus}; /// Wraps the actual future we're polling struct WrappedFuture where // See rust_future_new for an explanation of these trait bounds - F: Future + Send + 'static, + F: Future> + Send + 'static, T: LowerReturn + Send + 'static, UT: Send + 'static, { @@ -106,7 +106,7 @@ where impl WrappedFuture where // See rust_future_new for an explanation of these trait bounds - F: Future + Send + 'static, + F: Future> + Send + 'static, T: LowerReturn + Send + 'static, UT: Send + 'static, { @@ -139,7 +139,8 @@ where // case below and we will never poll the future again. panic::AssertUnwindSafe(|| match pinned.poll(context) { Poll::Pending => Ok(Poll::Pending), - Poll::Ready(v) => T::lower_return(v).map(Poll::Ready), + Poll::Ready(Ok(v)) => T::lower_return(v).map(Poll::Ready), + Poll::Ready(Err(e)) => T::handle_failed_lift(e).map(Poll::Ready), }), ); match result { @@ -185,7 +186,7 @@ where unsafe impl Send for WrappedFuture where // See rust_future_new for an explanation of these trait bounds - F: Future + Send + 'static, + F: Future> + Send + 'static, T: LowerReturn + Send + 'static, UT: Send + 'static, { @@ -195,7 +196,7 @@ where pub(super) struct RustFuture where // See rust_future_new for an explanation of these trait bounds - F: Future + Send + 'static, + F: Future> + Send + 'static, T: LowerReturn + Send + 'static, UT: Send + 'static, { @@ -211,7 +212,7 @@ where impl RustFuture where // See rust_future_new for an explanation of these trait bounds - F: Future + Send + 'static, + F: Future> + Send + 'static, T: LowerReturn + Send + 'static, UT: Send + 'static, { @@ -263,7 +264,7 @@ where impl Wake for RustFuture where // See rust_future_new for an explanation of these trait bounds - F: Future + Send + 'static, + F: Future> + Send + 'static, T: LowerReturn + Send + 'static, UT: Send + 'static, { @@ -298,7 +299,7 @@ pub trait RustFutureFfi: Send + Sync { impl RustFutureFfi for RustFuture where // See rust_future_new for an explanation of these trait bounds - F: Future + Send + 'static, + F: Future> + Send + 'static, T: LowerReturn + Send + 'static, UT: Send + 'static, { diff --git a/uniffi_core/src/ffi/rustfuture/mod.rs b/uniffi_core/src/ffi/rustfuture/mod.rs index 3d3505e5ef..01202c5752 100644 --- a/uniffi_core/src/ffi/rustfuture/mod.rs +++ b/uniffi_core/src/ffi/rustfuture/mod.rs @@ -12,7 +12,7 @@ use scheduler::*; #[cfg(test)] mod tests; -use crate::{derive_ffi_traits, Handle, HandleAlloc, LowerReturn, RustCallStatus}; +use crate::{derive_ffi_traits, Handle, HandleAlloc, LiftArgsError, LowerReturn, RustCallStatus}; /// Result code for [rust_future_poll]. This is passed to the continuation function. #[repr(i8)] @@ -42,7 +42,7 @@ where // since it will move between threads for an indeterminate amount of time as the foreign // executor calls polls it and the Rust executor wakes it. It does not need to by `Sync`, // since we synchronize all access to the values. - F: Future + Send + 'static, + F: Future> + Send + 'static, // T is the output of the Future. It needs to implement [LowerReturn]. Also it must be Send + // 'static for the same reason as F. T: LowerReturn + Send + 'static, diff --git a/uniffi_core/src/ffi/rustfuture/tests.rs b/uniffi_core/src/ffi/rustfuture/tests.rs index 67dd47525b..6bd9b6c93e 100644 --- a/uniffi_core/src/ffi/rustfuture/tests.rs +++ b/uniffi_core/src/ffi/rustfuture/tests.rs @@ -13,7 +13,7 @@ use crate::{test_util::TestError, Lift, RustBuffer, RustCallStatusCode}; // Sender/Receiver pair that we use for testing struct Channel { - result: Option>, + result: Option, LiftArgsError>>, waker: Option, } @@ -29,7 +29,21 @@ impl Sender { fn send(&self, value: Result) { let mut inner = self.0.lock().unwrap(); - if inner.result.replace(value).is_some() { + if inner.result.replace(Ok(value)).is_some() { + panic!("value already sent"); + } + if let Some(waker) = &inner.waker { + waker.wake_by_ref(); + } + } + + fn send_lift_args_error(&self, arg_name: &'static str, error: anyhow::Error) { + let mut inner = self.0.lock().unwrap(); + if inner + .result + .replace(Err(LiftArgsError { arg_name, error })) + .is_some() + { panic!("value already sent"); } if let Some(waker) = &inner.waker { @@ -41,12 +55,15 @@ impl Sender { struct Receiver(Arc>); impl Future for Receiver { - type Output = Result; + type Output = Result, LiftArgsError>; - fn poll(self: Pin<&mut Self>, context: &mut Context<'_>) -> Poll> { + fn poll( + self: Pin<&mut Self>, + context: &mut Context<'_>, + ) -> Poll, LiftArgsError>> { let mut inner = self.0.lock().unwrap(); - match &inner.result { - Some(v) => Poll::Ready(v.clone()), + match inner.result.take() { + Some(v) => Poll::Ready(v), None => { inner.waker = Some(context.waker().clone()); Poll::Pending @@ -136,6 +153,29 @@ fn test_error() { ) } +#[test] +fn test_lift_args_error() { + let (sender, rust_future) = channel(); + + let continuation_result = poll(&rust_future); + assert_eq!(continuation_result.get(), None); + sender.send_lift_args_error("arg0", anyhow::anyhow!("Invalid handle")); + assert_eq!(continuation_result.get(), Some(&RustFuturePoll::MaybeReady)); + + let continuation_result = poll(&rust_future); + assert_eq!(continuation_result.get(), Some(&RustFuturePoll::Ready)); + + let (_, call_status) = complete(rust_future); + assert_eq!(call_status.code, RustCallStatusCode::UnexpectedError); + assert_eq!( + >::try_lift(ManuallyDrop::into_inner( + call_status.error_buf + )) + .unwrap(), + "Failed to convert arg 'arg0': Invalid handle", + ) +} + // Once `complete` is called, the inner future should be released, even if wakers still hold a // reference to the RustFuture #[test] @@ -203,7 +243,7 @@ fn test_wake_during_poll() { Poll::Pending } else { // The second time we're polled, we're ready - Poll::Ready("All done".to_owned()) + Poll::Ready(Ok("All done".to_owned())) } }); let rust_future: Arc> = RustFuture::new(future, crate::UniFfiTag); diff --git a/uniffi_core/src/ffi_converter_impls.rs b/uniffi_core/src/ffi_converter_impls.rs index 34ef5c6074..714fce16ba 100644 --- a/uniffi_core/src/ffi_converter_impls.rs +++ b/uniffi_core/src/ffi_converter_impls.rs @@ -23,8 +23,9 @@ /// "UT" means an arbitrary `UniFfiTag` type. use crate::{ check_remaining, derive_ffi_traits, ffi_converter_rust_buffer_lift_and_lower, metadata, - ConvertError, FfiConverter, Lift, LiftRef, LiftReturn, Lower, LowerError, LowerReturn, - MetadataBuffer, Result, RustBuffer, TypeId, UnexpectedUniFFICallbackError, + ConvertError, FfiConverter, Lift, LiftArgsError, LiftRef, LiftReturn, Lower, LowerError, + LowerReturn, MetadataBuffer, Result, RustBuffer, RustCallError, TypeId, + UnexpectedUniFFICallbackError, }; use anyhow::bail; use bytes::buf::{Buf, BufMut}; @@ -453,7 +454,7 @@ derive_ffi_traits!(impl TypeId for Arc where Arc: FfiConverter< unsafe impl LowerReturn for () { type ReturnType = (); - fn lower_return(_: ()) -> Result { + fn lower_return(_: ()) -> Result { Ok(()) } } @@ -480,17 +481,20 @@ where { type ReturnType = R::ReturnType; - fn lower_return(v: Self) -> Result { + fn lower_return(v: Self) -> Result { match v { Ok(r) => R::lower_return(r), - Err(e) => Err(E::lower_error(e)), + Err(e) => Err(RustCallError::Error(E::lower_error(e))), } } - fn handle_failed_lift(arg_name: &str, err: anyhow::Error) -> Self { - match err.downcast::() { - Ok(actual_error) => Err(actual_error), - Err(ohno) => panic!("Failed to convert arg '{arg_name}': {ohno}"), + fn handle_failed_lift(error: LiftArgsError) -> Result { + match error.error.downcast::() { + Ok(downcast) => Err(RustCallError::Error(E::lower_error(downcast))), + Err(e) => { + let msg = format!("Failed to convert arg '{}': {e}", error.arg_name); + Err(RustCallError::InternalError(msg)) + } } } } diff --git a/uniffi_core/src/ffi_converter_traits.rs b/uniffi_core/src/ffi_converter_traits.rs index 86e29e7f50..4bfe9a360d 100644 --- a/uniffi_core/src/ffi_converter_traits.rs +++ b/uniffi_core/src/ffi_converter_traits.rs @@ -56,8 +56,8 @@ use anyhow::bail; use bytes::Buf; use crate::{ - FfiDefault, Handle, MetadataBuffer, Result, RustBuffer, RustCallStatus, RustCallStatusCode, - UnexpectedUniFFICallbackError, + FfiDefault, Handle, LiftArgsError, MetadataBuffer, Result, RustBuffer, RustCallError, + RustCallStatus, RustCallStatusCode, UnexpectedUniFFICallbackError, }; /// Generalized FFI conversions @@ -269,22 +269,29 @@ pub unsafe trait LowerReturn: Sized { /// When derived, it's the same as `FfiType`. type ReturnType: FfiDefault; - /// Lower this value for scaffolding function return + /// Lower the return value from an scaffolding call + /// + /// Returns values that [rust_call] expects: /// - /// This method converts values into the `Result<>` type that [rust_call] expects. For - /// successful calls, return `Ok(lower_return)`. For errors that should be translated into - /// thrown exceptions on the foreign code, serialize the error into a RustBuffer and return - /// `Err(buf)` - fn lower_return(obj: Self) -> Result; + /// - Ok(v) for `Ok` returns and non-result returns, where v is the lowered return value + /// - `Err(RustCallError::Error(buf))` for `Err` returns where `buf` is serialized error value. + fn lower_return(v: Self) -> Result; - /// If possible, get a serialized error for failed argument lifts + /// Lower the return value for failed argument lifts + /// + /// This is called when we fail to make a scaffolding call, because of an error lifting an + /// argument. It should return a value that [rust_call] expects: /// - /// By default, we just panic and let `rust_call` handle things. However, for `Result<_, E>` - /// returns, if the anyhow error can be downcast to `E`, then serialize that and return it. - /// This results in the foreign code throwing a "normal" exception, rather than an unexpected - /// exception. - fn handle_failed_lift(arg_name: &str, e: anyhow::Error) -> Self { - panic!("Failed to convert arg '{arg_name}': {e}") + /// - By default, this is `Err(RustCallError::InternalError(msg))` where `msg` is message + /// describing the failed lift. + /// - For Result types, if we can downcast the error to the `Err` value, then return + /// `Err(RustCallError::Error(buf))`. This results in better exception throws on the foreign + /// side. + fn handle_failed_lift(error: LiftArgsError) -> Result { + let LiftArgsError { arg_name, error } = error; + Err(RustCallError::InternalError(format!( + "Failed to convert arg '{arg_name}': {error}" + ))) } } @@ -540,8 +547,8 @@ macro_rules! derive_ffi_traits { { type ReturnType = >::FfiType; - fn lower_return(obj: Self) -> $crate::deps::anyhow::Result { - ::std::result::Result::Ok(>::lower(obj)) + fn lower_return(v: Self) -> $crate::deps::anyhow::Result { + ::std::result::Result::Ok(>::lower(v)) } } }; diff --git a/uniffi_macros/src/export/scaffolding.rs b/uniffi_macros/src/export/scaffolding.rs index 7f4b15a3b0..bc45648723 100644 --- a/uniffi_macros/src/export/scaffolding.rs +++ b/uniffi_macros/src/export/scaffolding.rs @@ -265,15 +265,15 @@ pub(super) fn gen_ffi_function( ::uniffi::deps::log::debug!(#name); let uniffi_lift_args = #lift_closure; ::uniffi::rust_call(call_status, || { - #lower_return(match uniffi_lift_args() { + match uniffi_lift_args() { ::std::result::Result::Ok(uniffi_args) => { let uniffi_result = #rust_fn_call; - #convert_result + #lower_return(#convert_result) } - ::std::result::Result::Err((arg_name, anyhow_error)) => { - #handle_failed_lift(arg_name, anyhow_error) + ::std::result::Result::Err((arg_name, error)) => { + #handle_failed_lift(::uniffi::LiftArgsError { arg_name, error} ) }, - }) + } }) } @@ -292,24 +292,21 @@ pub(super) fn gen_ffi_function( #[no_mangle] pub extern "C" fn #ffi_ident(#(#param_names: #param_types,)*) -> ::uniffi::Handle { ::uniffi::deps::log::debug!(#name); - let uniffi_lift_args = #lift_closure; - match uniffi_lift_args() { - ::std::result::Result::Ok(uniffi_args) => { - ::uniffi::rust_future_new::<_, #return_ty, _>( - async move { + let uniffi_lifted_args = (#lift_closure)(); + ::uniffi::rust_future_new::<_, #return_ty, _>( + async move { + match uniffi_lifted_args { + ::std::result::Result::Ok(uniffi_args) => { let uniffi_result = #future_expr.await; - #convert_result + Ok(#convert_result) + } + ::std::result::Result::Err((arg_name, error)) => { + Err(::uniffi::LiftArgsError { arg_name, error }) }, - crate::UniFfiTag - ) - }, - ::std::result::Result::Err((arg_name, anyhow_error)) => { - ::uniffi::rust_future_new::<_, #return_ty, _>( - async move { #handle_failed_lift(arg_name, anyhow_error) }, - crate::UniFfiTag, - ) + } }, - } + crate::UniFfiTag + ) } #scaffolding_fn_ffi_buffer_version diff --git a/uniffi_macros/src/object.rs b/uniffi_macros/src/object.rs index 65b17229b0..0907bbfd3b 100644 --- a/uniffi_macros/src/object.rs +++ b/uniffi_macros/src/object.rs @@ -185,7 +185,7 @@ fn interface_impl(object: &ObjectItem, options: &DeriveOptions) -> TokenStream { unsafe #lower_return_impl_spec { type ReturnType = #lower_return_type_arc; - fn lower_return(obj: Self) -> ::std::result::Result { + fn lower_return(obj: Self) -> ::std::result::Result { #lower_return_arc(::std::sync::Arc::new(obj)) } } From 194649024f34abfd277c844d5381d6a904907fd0 Mon Sep 17 00:00:00 2001 From: Mark Hammond Date: Mon, 22 Jul 2024 10:34:30 -0400 Subject: [PATCH 20/62] Handle::from_raw() and HandleAlloc::clone_handle()/consume_handle() are all unsafe. (#2192) Fixes #2164 --- uniffi_core/src/ffi/ffiserialize.rs | 2 +- uniffi_core/src/ffi/handle.rs | 4 ++- uniffi_core/src/ffi_converter_traits.rs | 38 +++++++++++++------------ 3 files changed, 24 insertions(+), 20 deletions(-) diff --git a/uniffi_core/src/ffi/ffiserialize.rs b/uniffi_core/src/ffi/ffiserialize.rs index 50ae78840a..edbeb51229 100644 --- a/uniffi_core/src/ffi/ffiserialize.rs +++ b/uniffi_core/src/ffi/ffiserialize.rs @@ -275,7 +275,7 @@ mod test { rust_buffer.len(), rust_buffer.capacity(), ); - let handle = Handle::from_raw(101).unwrap(); + let handle = unsafe { Handle::from_raw(101).unwrap() }; let rust_call_status = RustCallStatus::default(); let rust_call_status_error_buf = &rust_call_status.error_buf; let orig_rust_call_status_buffer_data = ( diff --git a/uniffi_core/src/ffi/handle.rs b/uniffi_core/src/ffi/handle.rs index b56c16d538..8ca66a41de 100644 --- a/uniffi_core/src/ffi/handle.rs +++ b/uniffi_core/src/ffi/handle.rs @@ -28,7 +28,9 @@ impl Handle { self.0 as *const T } - pub fn from_raw(raw: u64) -> Option { + /// # Safety + /// The raw value must be a valid handle as described above. + pub unsafe fn from_raw(raw: u64) -> Option { if raw == 0 { None } else { diff --git a/uniffi_core/src/ffi_converter_traits.rs b/uniffi_core/src/ffi_converter_traits.rs index 86e29e7f50..d796648899 100644 --- a/uniffi_core/src/ffi_converter_traits.rs +++ b/uniffi_core/src/ffi_converter_traits.rs @@ -449,18 +449,24 @@ pub unsafe trait HandleAlloc: Send + Sync { /// This creates a new handle from an existing one. /// It's used when the foreign code wants to pass back an owned handle and still keep a copy /// for themselves. - fn clone_handle(handle: Handle) -> Handle; + /// # Safety + /// The handle must be valid. + unsafe fn clone_handle(handle: Handle) -> Handle; /// Get a clone of the `Arc<>` using a "borrowed" handle. /// - /// Take care that the handle can not be destroyed between when it's passed and when + /// # Safety + /// The handle must be valid. Take care that the handle can + /// not be destroyed between when it's passed and when /// `get_arc()` is called. #1797 is a cautionary tale. - fn get_arc(handle: Handle) -> Arc { + unsafe fn get_arc(handle: Handle) -> Arc { Self::consume_handle(Self::clone_handle(handle)) } /// Consume a handle, getting back the initial `Arc<>` - fn consume_handle(handle: Handle) -> Arc; + /// # Safety + /// The handle must be valid. + unsafe fn consume_handle(handle: Handle) -> Arc; } /// Derive FFI traits @@ -596,19 +602,15 @@ macro_rules! derive_ffi_traits { $crate::Handle::from_pointer(::std::sync::Arc::into_raw(::std::sync::Arc::new(value))) } - fn clone_handle(handle: $crate::Handle) -> $crate::Handle { - unsafe { - ::std::sync::Arc::<::std::sync::Arc>::increment_strong_count(handle.as_pointer::<::std::sync::Arc>()); - } + unsafe fn clone_handle(handle: $crate::Handle) -> $crate::Handle { + ::std::sync::Arc::<::std::sync::Arc>::increment_strong_count(handle.as_pointer::<::std::sync::Arc>()); handle } - fn consume_handle(handle: $crate::Handle) -> ::std::sync::Arc { - unsafe { - ::std::sync::Arc::::clone( - &std::sync::Arc::<::std::sync::Arc::>::from_raw(handle.as_pointer::<::std::sync::Arc>()) - ) - } + unsafe fn consume_handle(handle: $crate::Handle) -> ::std::sync::Arc { + ::std::sync::Arc::::clone( + &std::sync::Arc::<::std::sync::Arc::>::from_raw(handle.as_pointer::<::std::sync::Arc>()) + ) } } }; @@ -626,12 +628,12 @@ unsafe impl HandleAlloc for T { Handle::from_pointer(Arc::into_raw(value)) } - fn clone_handle(handle: Handle) -> Handle { - unsafe { Arc::increment_strong_count(handle.as_pointer::()) }; + unsafe fn clone_handle(handle: Handle) -> Handle { + Arc::increment_strong_count(handle.as_pointer::()); handle } - fn consume_handle(handle: Handle) -> Arc { - unsafe { Arc::from_raw(handle.as_pointer()) } + unsafe fn consume_handle(handle: Handle) -> Arc { + Arc::from_raw(handle.as_pointer()) } } From ee5b8401ce46fccb9b8407863db48c0601bd131d Mon Sep 17 00:00:00 2001 From: ckaznable Date: Wed, 24 Jul 2024 18:51:55 +0800 Subject: [PATCH 21/62] Remove extra character in document (#2193) --- docs/manual/src/udl/errors.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/manual/src/udl/errors.md b/docs/manual/src/udl/errors.md index 5ee67cc660..d96cc36948 100644 --- a/docs/manual/src/udl/errors.md +++ b/docs/manual/src/udl/errors.md @@ -71,7 +71,7 @@ pub struct MyError { } impl MyError { - fn message(&self) -> String> { self.to_string() } + fn message(&self) -> String { self.to_string() } } impl From for MyError { From e585dc07e9a269f225c7a2bcfe94866351445aaa Mon Sep 17 00:00:00 2001 From: Mark Hammond Date: Tue, 30 Jul 2024 09:26:59 -0400 Subject: [PATCH 22/62] Fix so cargo_metadata isn't a dependency without the cargo_metadata feature. (#2200) The `uniffi_test` crate unconditionally depends on `cargo_metadata`, and this will be difficult to change. This means that because `uniffi_test` is an unconditional dependency of `uniffi_bindgen`, the `cargo_metadata` feature doesn't actually prevent `uniffi_bindgen` from depending on `cargo_metadata`. The top level `uniffi` create already has a feature `bindgen-tests`. This patch introduces a feature of the same name to `uniffi_bindgen`. Enabling that feature also enables the metadata feature. This means that if you do not enable either of these features you should be able to avoid `cargo_metadata`. This patch also renames the new `cargo_metadata` feature to be `cargo-metadata` to be more consistent with our existing features and Rust feature naming in general. --- fixtures/benchmarks/Cargo.toml | 2 +- fixtures/benchmarks/benches/benchmarks.rs | 3 ++- uniffi/Cargo.toml | 2 +- uniffi/src/lib.rs | 9 ++------- uniffi_bindgen/Cargo.toml | 7 ++++--- uniffi_bindgen/src/bindings/kotlin/mod.rs | 4 ++-- uniffi_bindgen/src/bindings/mod.rs | 20 +++++++++++--------- uniffi_bindgen/src/bindings/python/mod.rs | 4 ++-- uniffi_bindgen/src/bindings/ruby/mod.rs | 4 ++-- uniffi_bindgen/src/bindings/swift/mod.rs | 5 +++-- uniffi_bindgen/src/library_mode.rs | 6 +++--- uniffi_macros/src/test.rs | 8 ++++---- 12 files changed, 37 insertions(+), 37 deletions(-) diff --git a/fixtures/benchmarks/Cargo.toml b/fixtures/benchmarks/Cargo.toml index 74bbcc961d..f7fc07dde1 100644 --- a/fixtures/benchmarks/Cargo.toml +++ b/fixtures/benchmarks/Cargo.toml @@ -20,7 +20,7 @@ criterion = "0.5.1" uniffi = { workspace = true, features = ["build"] } [dev-dependencies] -uniffi_bindgen = {path = "../../uniffi_bindgen"} +uniffi_bindgen = {path = "../../uniffi_bindgen", features = ["bindgen-tests"]} [[bench]] name = "benchmarks" diff --git a/fixtures/benchmarks/benches/benchmarks.rs b/fixtures/benchmarks/benches/benchmarks.rs index 891d9821e4..627e42e69b 100644 --- a/fixtures/benchmarks/benches/benchmarks.rs +++ b/fixtures/benchmarks/benches/benchmarks.rs @@ -6,7 +6,8 @@ use clap::Parser; use std::env; use uniffi_benchmarks::Args; use uniffi_bindgen::bindings::{ - kotlin_run_script, python_run_script, swift_run_script, RunScriptOptions, + kotlin_test::run_script as kotlin_run_script, python_test::run_script as python_run_script, + swift_test::run_script as swift_run_script, RunScriptOptions, }; fn main() { diff --git a/uniffi/Cargo.toml b/uniffi/Cargo.toml index 6396757233..b50521d7b0 100644 --- a/uniffi/Cargo.toml +++ b/uniffi/Cargo.toml @@ -39,7 +39,7 @@ bindgen = ["dep:uniffi_bindgen"] cli = [ "bindgen", "dep:clap", "dep:camino" ] # Support for running example/fixture tests for `uniffi-bindgen`. You probably # don't need to enable this. -bindgen-tests = [ "dep:uniffi_bindgen" ] +bindgen-tests = [ "dep:uniffi_bindgen", "uniffi_bindgen/bindgen-tests" ] # Enable support for Tokio's futures. # This must still be opted into on a per-function basis using `#[uniffi::export(async_runtime = "tokio")]`. tokio = ["uniffi_core/tokio"] diff --git a/uniffi/src/lib.rs b/uniffi/src/lib.rs index 55d2acd9ae..d49e89e584 100644 --- a/uniffi/src/lib.rs +++ b/uniffi/src/lib.rs @@ -8,13 +8,8 @@ pub use uniffi_macros::*; #[cfg(feature = "cli")] mod cli; #[cfg(feature = "bindgen-tests")] -pub use uniffi_bindgen::bindings::kotlin_run_test; -#[cfg(feature = "bindgen-tests")] -pub use uniffi_bindgen::bindings::python_run_test; -#[cfg(feature = "bindgen-tests")] -pub use uniffi_bindgen::bindings::ruby_run_test; -#[cfg(feature = "bindgen-tests")] -pub use uniffi_bindgen::bindings::swift_run_test; +pub use uniffi_bindgen::bindings::{kotlin_test, python_test, ruby_test, swift_test}; + #[cfg(feature = "bindgen")] pub use uniffi_bindgen::{ bindings::{ diff --git a/uniffi_bindgen/Cargo.toml b/uniffi_bindgen/Cargo.toml index 0f9457e612..640e5f0eba 100644 --- a/uniffi_bindgen/Cargo.toml +++ b/uniffi_bindgen/Cargo.toml @@ -12,8 +12,9 @@ keywords = ["ffi", "bindgen"] readme = "../README.md" [features] -default = ["cargo_metadata"] -cargo_metadata = ["dep:cargo_metadata"] +default = ["cargo-metadata"] +cargo-metadata = ["dep:cargo_metadata"] +bindgen-tests = ["cargo-metadata", "dep:uniffi_testing"] [dependencies] anyhow = "1" @@ -29,7 +30,7 @@ paste = "1.0" serde = { version = "1", features = ["derive"] } toml = "0.5" uniffi_meta = { path = "../uniffi_meta", version = "=0.28.0" } -uniffi_testing = { path = "../uniffi_testing", version = "=0.28.0" } +uniffi_testing = { path = "../uniffi_testing", version = "=0.28.0", optional = true } uniffi_udl = { path = "../uniffi_udl", version = "=0.28.0" } # Don't include the `unicode-linebreak` or `unicode-width` since that functionality isn't needed for # docstrings. diff --git a/uniffi_bindgen/src/bindings/kotlin/mod.rs b/uniffi_bindgen/src/bindings/kotlin/mod.rs index 4a447d13d9..501f449da0 100644 --- a/uniffi_bindgen/src/bindings/kotlin/mod.rs +++ b/uniffi_bindgen/src/bindings/kotlin/mod.rs @@ -11,8 +11,8 @@ use std::process::Command; mod gen_kotlin; use gen_kotlin::{generate_bindings, Config}; -mod test; -pub use test::{run_script, run_test}; +#[cfg(feature = "bindgen-tests")] +pub mod test; pub struct KotlinBindingGenerator; impl BindingGenerator for KotlinBindingGenerator { diff --git a/uniffi_bindgen/src/bindings/mod.rs b/uniffi_bindgen/src/bindings/mod.rs index c984d07aff..2d6cdc07fb 100644 --- a/uniffi_bindgen/src/bindings/mod.rs +++ b/uniffi_bindgen/src/bindings/mod.rs @@ -8,26 +8,28 @@ //! along with some helpers for executing foreign language scripts or tests. mod kotlin; -pub use kotlin::{ - run_script as kotlin_run_script, run_test as kotlin_run_test, KotlinBindingGenerator, -}; +pub use kotlin::KotlinBindingGenerator; mod python; -pub use python::{ - run_script as python_run_script, run_test as python_run_test, PythonBindingGenerator, -}; +pub use python::PythonBindingGenerator; mod ruby; -pub use ruby::{run_test as ruby_run_test, RubyBindingGenerator}; +pub use ruby::RubyBindingGenerator; mod swift; -pub use swift::{ - run_script as swift_run_script, run_test as swift_run_test, SwiftBindingGenerator, +pub use swift::SwiftBindingGenerator; + +#[cfg(feature = "bindgen-tests")] +pub use self::{ + kotlin::test as kotlin_test, python::test as python_test, ruby::test as ruby_test, + swift::test as swift_test, }; +#[cfg(feature = "bindgen-tests")] /// Mode for the `run_script` function defined for each language #[derive(Clone, Debug)] pub struct RunScriptOptions { pub show_compiler_messages: bool, } +#[cfg(feature = "bindgen-tests")] impl Default for RunScriptOptions { fn default() -> Self { Self { diff --git a/uniffi_bindgen/src/bindings/python/mod.rs b/uniffi_bindgen/src/bindings/python/mod.rs index 04c8f7f76c..723e167340 100644 --- a/uniffi_bindgen/src/bindings/python/mod.rs +++ b/uniffi_bindgen/src/bindings/python/mod.rs @@ -8,10 +8,10 @@ use anyhow::Result; use fs_err as fs; mod gen_python; -mod test; +#[cfg(feature = "bindgen-tests")] +pub mod test; use crate::{Component, GenerationSettings}; use gen_python::{generate_python_bindings, Config}; -pub use test::{run_script, run_test}; pub struct PythonBindingGenerator; diff --git a/uniffi_bindgen/src/bindings/ruby/mod.rs b/uniffi_bindgen/src/bindings/ruby/mod.rs index f970b50fc1..e8f3c5f77c 100644 --- a/uniffi_bindgen/src/bindings/ruby/mod.rs +++ b/uniffi_bindgen/src/bindings/ruby/mod.rs @@ -9,9 +9,9 @@ use anyhow::{Context, Result}; use fs_err as fs; mod gen_ruby; -mod test; +#[cfg(feature = "bindgen-tests")] +pub mod test; use gen_ruby::{Config, RubyWrapper}; -pub use test::run_test; pub struct RubyBindingGenerator; impl BindingGenerator for RubyBindingGenerator { diff --git a/uniffi_bindgen/src/bindings/swift/mod.rs b/uniffi_bindgen/src/bindings/swift/mod.rs index 8421b93f9e..27d2bcf0a3 100644 --- a/uniffi_bindgen/src/bindings/swift/mod.rs +++ b/uniffi_bindgen/src/bindings/swift/mod.rs @@ -36,8 +36,9 @@ use std::process::Command; mod gen_swift; use gen_swift::{generate_bindings, Config}; -mod test; -pub use test::{run_script, run_test}; + +#[cfg(feature = "bindgen-tests")] +pub mod test; /// The Swift bindings generated from a [`crate::ComponentInterface`]. /// diff --git a/uniffi_bindgen/src/library_mode.rs b/uniffi_bindgen/src/library_mode.rs index 0c110dc722..eac8312a2b 100644 --- a/uniffi_bindgen/src/library_mode.rs +++ b/uniffi_bindgen/src/library_mode.rs @@ -145,7 +145,7 @@ impl CratePathMap { // TODO: we probably need external overrides passed in (maybe via // `config_file_override` - what are the semantics of that?) // Anyway: for now we just do this. - #[cfg(feature = "cargo_metadata")] + #[cfg(feature = "cargo-metadata")] fn new() -> Result { Ok(Self::from( cargo_metadata::MetadataCommand::new() @@ -155,7 +155,7 @@ impl CratePathMap { )) } - #[cfg(not(feature = "cargo_metadata"))] + #[cfg(not(feature = "cargo-metadata"))] fn new() -> Result { Ok(Self::default()) } @@ -166,7 +166,7 @@ impl CratePathMap { } // all this to get a few paths!? -#[cfg(feature = "cargo_metadata")] +#[cfg(feature = "cargo-metadata")] impl From for CratePathMap { fn from(metadata: cargo_metadata::Metadata) -> Self { let paths: HashMap = metadata diff --git a/uniffi_macros/src/test.rs b/uniffi_macros/src/test.rs index 95acfbc1e6..e6ae142ce1 100644 --- a/uniffi_macros/src/test.rs +++ b/uniffi_macros/src/test.rs @@ -30,16 +30,16 @@ pub(crate) fn build_foreign_language_testcases(tokens: TokenStream) -> TokenStre ); let run_test = match test_file_pathbuf.extension() { Some("kts") => quote! { - ::uniffi::kotlin_run_test + ::uniffi::kotlin_test::run_test }, Some("swift") => quote! { - ::uniffi::swift_run_test + ::uniffi::swift_test::run_test }, Some("py") => quote! { - ::uniffi::python_run_test + ::uniffi::python_test::run_test }, Some("rb") => quote! { - ::uniffi::ruby_run_test + ::uniffi::ruby_test::run_test }, _ => panic!("Unexpected extension for test script: {test_file_name}"), }; From f1c846776fde3ea45e4e9a8763a8a307ad28eba6 Mon Sep 17 00:00:00 2001 From: Mark Hammond Date: Wed, 31 Jul 2024 17:09:41 -0400 Subject: [PATCH 23/62] Move cargo_metadata execution out to the edges (ie, to the cli) (#2201) This continues the cargo_metadata feature work but making the execution of cargo_metadata the resonsibility of the uniffi_bindgen callers rather that executing it implicitly. This means the CLI, which in-turn means the top-level uniffi crate also gets a `cargo-metadata` feature. This reverts what we did to fix #2183 - by making `no_deps` the default, it means we will be unable to support reuse of UniFFI components, because it means we only support all components being in the same workspace. While this is a common use-case, it's not the only use-case we want to support. So grabbing all dependencies from cargo_metadata is again the default, but there's a new command-line option to avoid it. It also replaces some of #2195. --- CHANGELOG.md | 3 + Cargo.lock | 2 + fixtures/docstring-proc-macro/Cargo.toml | 1 + .../tests/test_generated_bindings.rs | 9 ++ uniffi/Cargo.toml | 5 +- uniffi/src/cli.rs | 34 ++++++- uniffi_bindgen/src/bindings/kotlin/test.rs | 3 + uniffi_bindgen/src/bindings/python/test.rs | 2 + uniffi_bindgen/src/bindings/ruby/test.rs | 2 + uniffi_bindgen/src/bindings/swift/test.rs | 18 +++- uniffi_bindgen/src/cargo_metadata.rs | 56 +++++++++++ uniffi_bindgen/src/lib.rs | 39 ++++++-- uniffi_bindgen/src/library_mode.rs | 93 ++++--------------- uniffi_testing/src/lib.rs | 4 + 14 files changed, 180 insertions(+), 91 deletions(-) create mode 100644 uniffi_bindgen/src/cargo_metadata.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a1b47eed4..73097deae3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,9 @@ ### What's new? +- A new bindgen command line option `--metadata-no-deps` is available to avoid processing + cargo_metadata for all dependencies. + - Objects error types can now be as `Result<>` error type without wrapping them in `Arc<>`. - Swift errors now provide `localizedDescription` ([#2116](https://github.com/mozilla/uniffi-rs/pull/2116)) diff --git a/Cargo.lock b/Cargo.lock index c0f83c5727..f135e335a8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1475,6 +1475,7 @@ version = "0.28.0" dependencies = [ "anyhow", "camino", + "cargo_metadata", "clap", "trybuild", "uniffi_bindgen", @@ -1626,6 +1627,7 @@ dependencies = [ name = "uniffi-fixture-docstring-proc-macro" version = "0.22.0" dependencies = [ + "cargo_metadata", "glob", "thiserror", "uniffi", diff --git a/fixtures/docstring-proc-macro/Cargo.toml b/fixtures/docstring-proc-macro/Cargo.toml index f67dece8db..45142fd21b 100644 --- a/fixtures/docstring-proc-macro/Cargo.toml +++ b/fixtures/docstring-proc-macro/Cargo.toml @@ -11,6 +11,7 @@ name = "uniffi_fixture_docstring_proc_macro" crate-type = ["lib", "cdylib"] [dependencies] +cargo_metadata = { version = "0.15" } thiserror = "1.0" uniffi = { path = "../../uniffi" } diff --git a/fixtures/docstring-proc-macro/tests/test_generated_bindings.rs b/fixtures/docstring-proc-macro/tests/test_generated_bindings.rs index 61c3d6e89d..e06bfdc6b3 100644 --- a/fixtures/docstring-proc-macro/tests/test_generated_bindings.rs +++ b/fixtures/docstring-proc-macro/tests/test_generated_bindings.rs @@ -50,10 +50,19 @@ mod tests { let cdylib_path = test_helper.copy_cdylib_to_out_dir(&out_dir).unwrap(); + let config_supplier = { + use uniffi_bindgen::cargo_metadata::CrateConfigSupplier; + let metadata = cargo_metadata::MetadataCommand::new() + .exec() + .expect("error running cargo metadata"); + CrateConfigSupplier::from(metadata) + }; + uniffi_bindgen::library_mode::generate_bindings( &cdylib_path, None, &gen, + &config_supplier, None, &out_dir, false, diff --git a/uniffi/Cargo.toml b/uniffi/Cargo.toml index b50521d7b0..72f4918ccd 100644 --- a/uniffi/Cargo.toml +++ b/uniffi/Cargo.toml @@ -21,19 +21,22 @@ uniffi_core = { path = "../uniffi_core", version = "=0.28.0" } uniffi_macros = { path = "../uniffi_macros", version = "=0.28.0" } anyhow = "1" camino = { version = "1.0.8", optional = true } +cargo_metadata = { version = "0.15", optional = true } clap = { version = "4", features = ["cargo", "std", "derive"], optional = true } [dev-dependencies] trybuild = "1" [features] -default = [] +default = ["cargo-metadata"] # Support for features needed by the `build.rs` script. Enable this in your # `build-dependencies`. build = [ "dep:uniffi_build" ] # Support for `uniffi_bindgen::{generate_bindings, generate_component_scaffolding}`. # Enable this feature for your `uniffi-bindgen` binaries if you don't need the full CLI. bindgen = ["dep:uniffi_bindgen"] +cargo-metadata = ["dep:cargo_metadata", "uniffi_bindgen?/cargo-metadata"] + # Support for `uniffi_bindgen_main()`. Enable this feature for your # `uniffi-bindgen` binaries. cli = [ "bindgen", "dep:clap", "dep:camino" ] diff --git a/uniffi/src/cli.rs b/uniffi/src/cli.rs index 09f408d628..e3d5cf41e9 100644 --- a/uniffi/src/cli.rs +++ b/uniffi/src/cli.rs @@ -2,7 +2,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/. */ -use anyhow::{bail, Result}; +use anyhow::{bail, Context, Result}; use camino::Utf8PathBuf; use clap::{Parser, Subcommand}; use std::fmt; @@ -108,6 +108,14 @@ enum Commands { /// Path to the UDL file, or cdylib if `library-mode` is specified source: Utf8PathBuf, + + /// Whether we should exclude dependencies when running "cargo metadata". + /// This will mean external types may not be resolved if they are implemented in crates + /// outside of this workspace. + /// This can be used in environments when all types are in the namespace and fetching + /// all sub-dependencies causes obscure platform specific problems. + #[clap(long)] + metadata_no_deps: bool, }, /// Generate Rust scaffolding code @@ -138,8 +146,23 @@ fn gen_library_mode( cfo: Option<&camino::Utf8Path>, out_dir: &camino::Utf8Path, fmt: bool, + metadata_no_deps: bool, ) -> anyhow::Result<()> { use uniffi_bindgen::library_mode::generate_bindings; + + #[cfg(feature = "cargo-metadata")] + let config_supplier = { + use uniffi_bindgen::cargo_metadata::CrateConfigSupplier; + let mut cmd = cargo_metadata::MetadataCommand::new(); + if metadata_no_deps { + cmd.no_deps(); + } + let metadata = cmd.exec().context("error running cargo metadata")?; + CrateConfigSupplier::from(metadata) + }; + #[cfg(not(feature = "cargo-metadata"))] + let config_supplier = uniffi_bindgen::EmptyCrateConfigSupplier; + for language in languages { // to help avoid mistakes we check the library is actually a cdylib, except // for swift where static libs are often used to extract the metadata. @@ -155,6 +178,7 @@ fn gen_library_mode( library_path, crate_name.clone(), &KotlinBindingGenerator, + &config_supplier, cfo, out_dir, fmt, @@ -164,6 +188,7 @@ fn gen_library_mode( library_path, crate_name.clone(), &PythonBindingGenerator, + &config_supplier, cfo, out_dir, fmt, @@ -173,6 +198,7 @@ fn gen_library_mode( library_path, crate_name.clone(), &RubyBindingGenerator, + &config_supplier, cfo, out_dir, fmt, @@ -182,6 +208,7 @@ fn gen_library_mode( library_path, crate_name.clone(), &SwiftBindingGenerator, + &config_supplier, cfo, out_dir, fmt, @@ -257,6 +284,7 @@ pub fn run_main() -> anyhow::Result<()> { source, crate_name, library_mode, + metadata_no_deps, } => { if library_mode { if lib_file.is_some() { @@ -273,8 +301,12 @@ pub fn run_main() -> anyhow::Result<()> { config.as_deref(), &out_dir, !no_format, + metadata_no_deps, )?; } else { + if metadata_no_deps { + panic!("--metadata-no-deps makes no sense when not in library mode") + } gen_bindings( &source, config.as_deref(), diff --git a/uniffi_bindgen/src/bindings/kotlin/test.rs b/uniffi_bindgen/src/bindings/kotlin/test.rs index 48c3d06cf1..5c7f0c9943 100644 --- a/uniffi_bindgen/src/bindings/kotlin/test.rs +++ b/uniffi_bindgen/src/bindings/kotlin/test.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/. */ use crate::bindings::RunScriptOptions; +use crate::cargo_metadata::CrateConfigSupplier; use crate::library_mode::generate_bindings; use anyhow::{bail, Context, Result}; use camino::{Utf8Path, Utf8PathBuf}; @@ -35,10 +36,12 @@ pub fn run_script( let test_helper = UniFFITestHelper::new(crate_name)?; let out_dir = test_helper.create_out_dir(tmp_dir, script_path)?; let cdylib_path = test_helper.copy_cdylib_to_out_dir(&out_dir)?; + generate_bindings( &cdylib_path, None, &super::KotlinBindingGenerator, + &CrateConfigSupplier::from(test_helper.cargo_metadata()), None, &out_dir, false, diff --git a/uniffi_bindgen/src/bindings/python/test.rs b/uniffi_bindgen/src/bindings/python/test.rs index 0392c324d4..39dbc9c136 100644 --- a/uniffi_bindgen/src/bindings/python/test.rs +++ b/uniffi_bindgen/src/bindings/python/test.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/. */ use crate::bindings::RunScriptOptions; +use crate::cargo_metadata::CrateConfigSupplier; use crate::library_mode::generate_bindings; use anyhow::{Context, Result}; use camino::Utf8Path; @@ -40,6 +41,7 @@ pub fn run_script( &cdylib_path, None, &super::PythonBindingGenerator, + &CrateConfigSupplier::from(test_helper.cargo_metadata()), None, &out_dir, false, diff --git a/uniffi_bindgen/src/bindings/ruby/test.rs b/uniffi_bindgen/src/bindings/ruby/test.rs index 86965fe2ff..47a3b68401 100644 --- a/uniffi_bindgen/src/bindings/ruby/test.rs +++ b/uniffi_bindgen/src/bindings/ruby/test.rs @@ -2,6 +2,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/. */ +use crate::cargo_metadata::CrateConfigSupplier; use crate::library_mode::generate_bindings; use anyhow::{bail, Context, Result}; use camino::Utf8Path; @@ -37,6 +38,7 @@ pub fn test_script_command( &cdylib_path, None, &super::RubyBindingGenerator, + &CrateConfigSupplier::from(test_helper.cargo_metadata()), None, &out_dir, false, diff --git a/uniffi_bindgen/src/bindings/swift/test.rs b/uniffi_bindgen/src/bindings/swift/test.rs index 2bc98b6163..9a2a5a7de9 100644 --- a/uniffi_bindgen/src/bindings/swift/test.rs +++ b/uniffi_bindgen/src/bindings/swift/test.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 crate::bindings::RunScriptOptions; +use crate::cargo_metadata::CrateConfigSupplier; use crate::library_mode::generate_bindings; use anyhow::{bail, Context, Result}; use camino::{Utf8Path, Utf8PathBuf}; +use cargo_metadata::Metadata; use std::env::consts::{DLL_PREFIX, DLL_SUFFIX}; use std::ffi::OsStr; use std::fs::{read_to_string, File}; @@ -39,8 +41,12 @@ pub fn run_script( let test_helper = UniFFITestHelper::new(package_name)?; let out_dir = test_helper.create_out_dir(tmp_dir, &script_path)?; let cdylib_path = test_helper.copy_cdylib_to_out_dir(&out_dir)?; - let generated_sources = - GeneratedSources::new(test_helper.crate_name(), &cdylib_path, &out_dir)?; + let generated_sources = GeneratedSources::new( + test_helper.crate_name(), + &cdylib_path, + test_helper.cargo_metadata(), + &out_dir, + )?; // Compile the generated sources together to create a single swift module compile_swift_module( @@ -125,11 +131,17 @@ struct GeneratedSources { } impl GeneratedSources { - fn new(crate_name: &str, cdylib_path: &Utf8Path, out_dir: &Utf8Path) -> Result { + fn new( + crate_name: &str, + cdylib_path: &Utf8Path, + cargo_metadata: Metadata, + out_dir: &Utf8Path, + ) -> Result { let sources = generate_bindings( cdylib_path, None, &super::SwiftBindingGenerator, + &CrateConfigSupplier::from(cargo_metadata), None, out_dir, false, diff --git a/uniffi_bindgen/src/cargo_metadata.rs b/uniffi_bindgen/src/cargo_metadata.rs new file mode 100644 index 0000000000..e304025f8f --- /dev/null +++ b/uniffi_bindgen/src/cargo_metadata.rs @@ -0,0 +1,56 @@ +/* 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/. */ + +//! Helpers for data returned by cargo_metadata. Note that this doesn't +//! execute cargo_metadata, just parses its output. + +use anyhow::{bail, Context}; +use camino::Utf8PathBuf; +use cargo_metadata::Metadata; +use std::{collections::HashMap, fs}; + +use crate::BindgenCrateConfigSupplier; + +#[derive(Debug, Clone, Default)] +pub struct CrateConfigSupplier { + paths: HashMap, +} + +impl BindgenCrateConfigSupplier for CrateConfigSupplier { + fn get_toml(&self, crate_name: &str) -> anyhow::Result> { + let toml = self.paths.get(crate_name).map(|p| p.join("uniffi.toml")); + crate::load_toml_file(toml.as_deref()) + } + + fn get_udl(&self, crate_name: &str, udl_name: &str) -> anyhow::Result { + let path = self + .paths + .get(crate_name) + .context(format!("No path known to UDL files for '{crate_name}'"))? + .join("src") + .join(format!("{udl_name}.udl")); + if path.exists() { + Ok(fs::read_to_string(path)?) + } else { + bail!(format!("No UDL file found at '{path}'")); + } + } +} + +impl From for CrateConfigSupplier { + fn from(metadata: Metadata) -> Self { + let paths: HashMap = metadata + .packages + .iter() + .flat_map(|p| { + p.targets.iter().filter(|t| t.is_lib()).filter_map(|t| { + p.manifest_path + .parent() + .map(|p| (t.name.replace('-', "_"), p.to_owned())) + }) + }) + .collect(); + Self { paths } + } +} diff --git a/uniffi_bindgen/src/lib.rs b/uniffi_bindgen/src/lib.rs index 9e7d121c45..7ff692361a 100644 --- a/uniffi_bindgen/src/lib.rs +++ b/uniffi_bindgen/src/lib.rs @@ -107,6 +107,9 @@ pub mod library_mode; pub mod macro_metadata; pub mod scaffolding; +#[cfg(feature = "cargo-metadata")] +pub mod cargo_metadata; + pub use interface::ComponentInterface; use scaffolding::RustScaffolding; @@ -164,6 +167,26 @@ pub struct Component { pub config: Config, } +/// A trait used by the bindgen to obtain config information about a source crate +/// which was found in the metadata for the library. +/// +/// This is an abstraction around needing the source directory for a crate. +/// In most cases `cargo_metadata` can be used, but this should be able to work in +/// more environments. +pub trait BindgenCrateConfigSupplier { + /// Get the toml for the crate. Probably came from uniffi.toml in the root of the crate source. + fn get_toml(&self, _crate_name: &str) -> Result> { + Ok(None) + } + /// Obtains the contents of the named UDL file which was referenced by the type metadata. + fn get_udl(&self, crate_name: &str, udl_name: &str) -> Result { + bail!("Crate {crate_name} has no UDL {udl_name}") + } +} + +pub struct EmptyCrateConfigSupplier; +impl BindgenCrateConfigSupplier for EmptyCrateConfigSupplier {} + /// A convenience function for the CLI to help avoid using static libs /// in places cdylibs are required. pub fn is_cdylib(library_file: impl AsRef) -> bool { @@ -206,8 +229,10 @@ pub fn generate_external_bindings( let config_file_override = config_file_override.as_ref().map(|p| p.as_ref()); let config = { - let toml = load_initial_config(Some(crate_root), config_file_override)?; - binding_generator.new_config(&toml)? + let crate_config = load_toml_file(Some(&crate_root.join("uniffi.toml"))) + .context("failed to load {crate_root}/uniffi.toml")?; + let toml_value = overridden_config_value(crate_config, config_file_override)?; + binding_generator.new_config(&toml_value)? }; let settings = GenerationSettings { @@ -405,19 +430,15 @@ fn load_toml_file(source: Option<&Utf8Path>) -> Result, +fn overridden_config_value( + config: Option, config_file_override: Option<&Utf8Path>, ) -> Result { - let mut config = load_toml_file(crate_root.map(|p| p.join("uniffi.toml")).as_deref()) - .context("default config")? - .unwrap_or(toml::value::Table::default()); - + let mut config = config.unwrap_or_default(); let override_config = load_toml_file(config_file_override).context("override config")?; if let Some(override_config) = override_config { merge_toml(&mut config, override_config); } - Ok(toml::Value::from(config)) } diff --git a/uniffi_bindgen/src/library_mode.rs b/uniffi_bindgen/src/library_mode.rs index eac8312a2b..b8e919bfbf 100644 --- a/uniffi_bindgen/src/library_mode.rs +++ b/uniffi_bindgen/src/library_mode.rs @@ -16,11 +16,11 @@ /// - UniFFI can figure out the package/module names for each crate, eliminating the external /// package maps. use crate::{ - load_initial_config, macro_metadata, BindingGenerator, Component, ComponentInterface, - GenerationSettings, Result, + macro_metadata, overridden_config_value, BindgenCrateConfigSupplier, BindingGenerator, + Component, ComponentInterface, GenerationSettings, Result, }; -use anyhow::{bail, Context}; -use camino::{Utf8Path, Utf8PathBuf}; +use anyhow::bail; +use camino::Utf8Path; use std::{collections::HashMap, fs}; use uniffi_meta::{ create_metadata_groups, fixup_external_type, group_metadata, Metadata, MetadataGroup, @@ -37,17 +37,17 @@ pub fn generate_bindings( library_path: &Utf8Path, crate_name: Option, binding_generator: &T, + config_supplier: &dyn BindgenCrateConfigSupplier, config_file_override: Option<&Utf8Path>, out_dir: &Utf8Path, try_format_code: bool, ) -> Result>> { - let path_map = CratePathMap::new()?; - let mut components = find_components(&path_map, library_path)? + let mut components = find_components(config_supplier, library_path)? .into_iter() .map(|ci| { - let crate_root = path_map.get_path(ci.crate_name()); - let config = binding_generator - .new_config(&load_initial_config(crate_root, config_file_override)?)?; + let crate_toml = config_supplier.get_toml(ci.crate_name())?; + let toml_value = overridden_config_value(crate_toml, config_file_override)?; + let config = binding_generator.new_config(&toml_value)?; Ok(Component { ci, config }) }) .collect::>>()?; @@ -91,7 +91,7 @@ pub fn calc_cdylib_name(library_path: &Utf8Path) -> Option<&str> { } fn find_components( - crates: &CratePathMap, + config_supplier: &dyn BindgenCrateConfigSupplier, library_path: &Utf8Path, ) -> Result> { let items = macro_metadata::extract_from_library(library_path)?; @@ -104,9 +104,7 @@ fn find_components( for group in metadata_groups.values() { let crate_name = group.namespace.crate_name.clone(); - let path = crates.get_path(&crate_name); - - if let Some(mut metadata_group) = load_udl_metadata(group, path, &crate_name)? { + if let Some(mut metadata_group) = load_udl_metadata(group, &crate_name, config_supplier)? { // fixup the items. metadata_group.items = metadata_group .items @@ -135,59 +133,10 @@ fn find_components( .collect() } -#[derive(Debug, Clone, Default)] -struct CratePathMap { - // path on disk where we will find the crate's source. - paths: HashMap, -} - -impl CratePathMap { - // TODO: we probably need external overrides passed in (maybe via - // `config_file_override` - what are the semantics of that?) - // Anyway: for now we just do this. - #[cfg(feature = "cargo-metadata")] - fn new() -> Result { - Ok(Self::from( - cargo_metadata::MetadataCommand::new() - .no_deps() - .exec() - .context("error running cargo metadata")?, - )) - } - - #[cfg(not(feature = "cargo-metadata"))] - fn new() -> Result { - Ok(Self::default()) - } - - fn get_path(&self, crate_name: &str) -> Option<&Utf8Path> { - self.paths.get(crate_name).map(|p| p.as_path()) - } -} - -// all this to get a few paths!? -#[cfg(feature = "cargo-metadata")] -impl From for CratePathMap { - fn from(metadata: cargo_metadata::Metadata) -> Self { - let paths: HashMap = metadata - .packages - .iter() - .flat_map(|p| { - p.targets.iter().filter(|t| t.is_lib()).filter_map(|t| { - p.manifest_path - .parent() - .map(|p| (t.name.replace('-', "_"), p.to_owned())) - }) - }) - .collect(); - Self { paths } - } -} - fn load_udl_metadata( group: &MetadataGroup, - crate_root: Option<&Utf8Path>, crate_name: &str, + config_supplier: &dyn BindgenCrateConfigSupplier, ) -> Result> { let udl_items = group .items @@ -197,10 +146,9 @@ fn load_udl_metadata( _ => None, }) .collect::>(); + // We only support 1 UDL file per crate, for no good reason! match udl_items.len() { - // No UDL files, load directly from the group 0 => Ok(None), - // Found a UDL file, use it to load the CI, then add the MetadataGroup 1 => { if udl_items[0].module_path != crate_name { bail!( @@ -209,18 +157,9 @@ fn load_udl_metadata( crate_name ); } - let ci_name = &udl_items[0].file_stub; - let ci_path = crate_root - .context(format!("No path known to '{ci_name}.udl'"))? - .join("src") - .join(format!("{ci_name}.udl")); - if ci_path.exists() { - let udl = fs::read_to_string(ci_path)?; - let udl_group = uniffi_udl::parse_udl(&udl, crate_name)?; - Ok(Some(udl_group)) - } else { - bail!("{ci_path} not found"); - } + let udl = config_supplier.get_udl(crate_name, &udl_items[0].file_stub)?; + let udl_group = uniffi_udl::parse_udl(&udl, crate_name)?; + Ok(Some(udl_group)) } n => bail!("{n} UDL files found for {crate_name}"), } diff --git a/uniffi_testing/src/lib.rs b/uniffi_testing/src/lib.rs index 54dc247d71..79c95bb023 100644 --- a/uniffi_testing/src/lib.rs +++ b/uniffi_testing/src/lib.rs @@ -149,6 +149,10 @@ impl UniFFITestHelper { pub fn crate_name(&self) -> &str { &self.crate_name } + + pub fn cargo_metadata(&self) -> Metadata { + CARGO_METADATA.clone() + } } fn get_cargo_metadata() -> Metadata { From fc7d250ac4ff3a5d7977edaf99f5aebb436372d2 Mon Sep 17 00:00:00 2001 From: Bastian Gruber Date: Fri, 19 Jul 2024 12:49:20 -0300 Subject: [PATCH 24/62] feat: rename python bindings before they hit the template --- .../python/gen_python/callback_interface.rs | 1 + .../src/bindings/python/gen_python/custom.rs | 1 + .../src/bindings/python/gen_python/enum_.rs | 1 + .../bindings/python/gen_python/external.rs | 1 + .../src/bindings/python/gen_python/mod.rs | 16 +++++-- .../src/bindings/python/gen_python/object.rs | 1 + .../src/bindings/python/gen_python/record.rs | 1 + uniffi_bindgen/src/bindings/python/mod.rs | 2 +- .../python/templates/ErrorTemplate.py | 8 ++-- .../python/templates/ExternalTemplate.py | 2 +- .../src/bindings/python/templates/wrapper.py | 2 +- uniffi_bindgen/src/interface/callbacks.rs | 17 +++++++ uniffi_bindgen/src/interface/conventions.rs | 25 ++++++++++ uniffi_bindgen/src/interface/enum_.rs | 19 ++++++++ uniffi_bindgen/src/interface/function.rs | 15 ++++++ uniffi_bindgen/src/interface/mod.rs | 4 +- uniffi_bindgen/src/interface/object.rs | 17 +++++++ uniffi_bindgen/src/interface/record.rs | 15 ++++++ uniffi_bindgen/src/interface/universe.rs | 2 +- uniffi_bindgen/src/lib.rs | 46 +++++++++++++++++++ 20 files changed, 182 insertions(+), 14 deletions(-) create mode 100644 uniffi_bindgen/src/interface/conventions.rs diff --git a/uniffi_bindgen/src/bindings/python/gen_python/callback_interface.rs b/uniffi_bindgen/src/bindings/python/gen_python/callback_interface.rs index 9c93965e35..55a2096a4a 100644 --- a/uniffi_bindgen/src/bindings/python/gen_python/callback_interface.rs +++ b/uniffi_bindgen/src/bindings/python/gen_python/callback_interface.rs @@ -4,6 +4,7 @@ use super::CodeType; use crate::backend::Literal; +use crate::CodeOracle; #[derive(Debug)] pub struct CallbackInterfaceCodeType { diff --git a/uniffi_bindgen/src/bindings/python/gen_python/custom.rs b/uniffi_bindgen/src/bindings/python/gen_python/custom.rs index 0818e7198b..5e05832cc7 100644 --- a/uniffi_bindgen/src/bindings/python/gen_python/custom.rs +++ b/uniffi_bindgen/src/bindings/python/gen_python/custom.rs @@ -3,6 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use super::CodeType; +use crate::CodeOracle; #[derive(Debug)] pub struct CustomCodeType { diff --git a/uniffi_bindgen/src/bindings/python/gen_python/enum_.rs b/uniffi_bindgen/src/bindings/python/gen_python/enum_.rs index 83ce177e07..7ba7150404 100644 --- a/uniffi_bindgen/src/bindings/python/gen_python/enum_.rs +++ b/uniffi_bindgen/src/bindings/python/gen_python/enum_.rs @@ -4,6 +4,7 @@ use super::CodeType; use crate::backend::Literal; +use crate::CodeOracle; #[derive(Debug)] pub struct EnumCodeType { diff --git a/uniffi_bindgen/src/bindings/python/gen_python/external.rs b/uniffi_bindgen/src/bindings/python/gen_python/external.rs index 0a46251d6d..519fb16d4f 100644 --- a/uniffi_bindgen/src/bindings/python/gen_python/external.rs +++ b/uniffi_bindgen/src/bindings/python/gen_python/external.rs @@ -3,6 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use super::CodeType; +use crate::CodeOracle; #[derive(Debug)] pub struct ExternalCodeType { diff --git a/uniffi_bindgen/src/bindings/python/gen_python/mod.rs b/uniffi_bindgen/src/bindings/python/gen_python/mod.rs index 61ab04166e..bcde25ab47 100644 --- a/uniffi_bindgen/src/bindings/python/gen_python/mod.rs +++ b/uniffi_bindgen/src/bindings/python/gen_python/mod.rs @@ -14,6 +14,7 @@ use std::collections::{BTreeSet, HashMap, HashSet}; use std::fmt::Debug; use crate::backend::TemplateExpression; +use crate::CodeOracle; use crate::interface::*; @@ -148,7 +149,7 @@ impl Config { } // Generate python bindings for the given ComponentInterface, as a string. -pub fn generate_python_bindings(config: &Config, ci: &ComponentInterface) -> Result { +pub fn generate_python_bindings(config: &Config, ci: &mut ComponentInterface) -> Result { PythonWrapper::new(config.clone(), ci) .render() .context("failed to render python bindings") @@ -298,10 +299,13 @@ pub struct PythonWrapper<'a> { type_imports: BTreeSet, } impl<'a> PythonWrapper<'a> { - pub fn new(config: Config, ci: &'a ComponentInterface) -> Self { + pub fn new(config: Config, ci: &'a mut ComponentInterface) -> Self { let type_renderer = TypeRenderer::new(&config, ci); let type_helper_code = type_renderer.render().unwrap(); let type_imports = type_renderer.imports.into_inner(); + + ci.apply_naming_conventions(PythonCodeOracle::default()); + Self { config, ci, @@ -330,7 +334,9 @@ impl PythonCodeOracle { fn find(&self, type_: &Type) -> Box { type_.clone().as_type().as_codetype() } +} +impl CodeOracle for PythonCodeOracle { /// Get the idiomatic Python rendering of a class name (for enums, records, errors, etc). fn class_name(&self, nm: &str) -> String { fixup_keyword(nm.to_string().to_upper_camel_case()) @@ -548,9 +554,9 @@ pub mod filters { } /// Get the idiomatic Python rendering of a class name (for enums, records, errors, etc). - pub fn class_name(nm: &str) -> Result { - Ok(PythonCodeOracle.class_name(nm)) - } + // pub fn class_name(nm: &str) -> Result { + // Ok(PythonCodeOracle.class_name(nm)) + // } /// Get the idiomatic Python rendering of a function name. pub fn fn_name(nm: &str) -> Result { diff --git a/uniffi_bindgen/src/bindings/python/gen_python/object.rs b/uniffi_bindgen/src/bindings/python/gen_python/object.rs index 1165bb0e54..31667ad31d 100644 --- a/uniffi_bindgen/src/bindings/python/gen_python/object.rs +++ b/uniffi_bindgen/src/bindings/python/gen_python/object.rs @@ -4,6 +4,7 @@ use super::CodeType; use crate::backend::Literal; +use crate::CodeOracle; #[derive(Debug)] pub struct ObjectCodeType { diff --git a/uniffi_bindgen/src/bindings/python/gen_python/record.rs b/uniffi_bindgen/src/bindings/python/gen_python/record.rs index df00f98e8b..a395442b73 100644 --- a/uniffi_bindgen/src/bindings/python/gen_python/record.rs +++ b/uniffi_bindgen/src/bindings/python/gen_python/record.rs @@ -4,6 +4,7 @@ use super::CodeType; use crate::backend::Literal; +use crate::CodeOracle; #[derive(Debug)] pub struct RecordCodeType { diff --git a/uniffi_bindgen/src/bindings/python/mod.rs b/uniffi_bindgen/src/bindings/python/mod.rs index 723e167340..66c3965f68 100644 --- a/uniffi_bindgen/src/bindings/python/mod.rs +++ b/uniffi_bindgen/src/bindings/python/mod.rs @@ -50,7 +50,7 @@ impl crate::BindingGenerator for PythonBindingGenerator { ) -> Result<()> { for Component { ci, config, .. } in components { let py_file = settings.out_dir.join(format!("{}.py", ci.namespace())); - fs::write(&py_file, generate_python_bindings(config, ci)?)?; + fs::write(&py_file, generate_python_bindings(config, &mut ci.clone())?)?; if settings.try_format_code { if let Err(e) = Command::new("yapf").arg(&py_file).output() { diff --git a/uniffi_bindgen/src/bindings/python/templates/ErrorTemplate.py b/uniffi_bindgen/src/bindings/python/templates/ErrorTemplate.py index 53818b1806..8037155d1e 100644 --- a/uniffi_bindgen/src/bindings/python/templates/ErrorTemplate.py +++ b/uniffi_bindgen/src/bindings/python/templates/ErrorTemplate.py @@ -13,7 +13,7 @@ class {{ type_name }}(Exception): class {{ type_name }}: # type: ignore {%- call py::docstring(e, 4) %} {%- for variant in e.variants() -%} - {%- let variant_type_name = variant.name()|class_name -%} + {%- let variant_type_name = variant.name() -%} {%- if e.is_flat() %} class {{ variant_type_name }}(_UniffiTemp{{ type_name }}): {%- call py::docstring(variant, 8) %} @@ -70,7 +70,7 @@ def read(buf): variant = buf.read_i32() {%- for variant in e.variants() %} if variant == {{ loop.index }}: - return {{ type_name }}.{{ variant.name()|class_name }}( + return {{ type_name }}.{{ variant.name() }}( {%- if e.is_flat() %} {{ Type::String.borrow()|read_fn }}(buf), {%- else %} @@ -88,7 +88,7 @@ def check_lower(value): pass {%- else %} {%- for variant in e.variants() %} - if isinstance(value, {{ type_name }}.{{ variant.name()|class_name }}): + if isinstance(value, {{ type_name }}.{{ variant.name() }}): {%- for field in variant.fields() %} {%- if variant.has_nameless_fields() %} {{ field|check_lower_fn }}(value._values[{{ loop.index0 }}]) @@ -103,7 +103,7 @@ def check_lower(value): @staticmethod def write(value, buf): {%- for variant in e.variants() %} - if isinstance(value, {{ type_name }}.{{ variant.name()|class_name }}): + if isinstance(value, {{ type_name }}.{{ variant.name() }}): buf.write_i32({{ loop.index }}) {%- for field in variant.fields() %} {%- if variant.has_nameless_fields() %} diff --git a/uniffi_bindgen/src/bindings/python/templates/ExternalTemplate.py b/uniffi_bindgen/src/bindings/python/templates/ExternalTemplate.py index 6c0cee85ef..26e4639e19 100644 --- a/uniffi_bindgen/src/bindings/python/templates/ExternalTemplate.py +++ b/uniffi_bindgen/src/bindings/python/templates/ExternalTemplate.py @@ -3,7 +3,7 @@ # External type {{name}} is in namespace "{{namespace}}", crate {{module_path}} {%- let ffi_converter_name = "_UniffiConverterType{}"|format(name) %} {{ self.add_import_of(module, ffi_converter_name) }} -{{ self.add_import_of(module, name|class_name) }} {#- import the type alias itself -#} +{{ self.add_import_of(module, name) }} {#- import the type alias itself -#} {%- let rustbuffer_local_name = "_UniffiRustBuffer{}"|format(name) %} {{ self.add_import_of_as(module, "_UniffiRustBuffer", rustbuffer_local_name) }} diff --git a/uniffi_bindgen/src/bindings/python/templates/wrapper.py b/uniffi_bindgen/src/bindings/python/templates/wrapper.py index 8a932406de..d1eb00b285 100644 --- a/uniffi_bindgen/src/bindings/python/templates/wrapper.py +++ b/uniffi_bindgen/src/bindings/python/templates/wrapper.py @@ -73,7 +73,7 @@ "{{ obj|type_name }}", {%- endfor %} {%- for c in ci.callback_interface_definitions() %} - "{{ c.name()|class_name }}", + "{{ c.name() }}", {%- endfor %} ] diff --git a/uniffi_bindgen/src/interface/callbacks.rs b/uniffi_bindgen/src/interface/callbacks.rs index f176a7a684..c020f49301 100644 --- a/uniffi_bindgen/src/interface/callbacks.rs +++ b/uniffi_bindgen/src/interface/callbacks.rs @@ -37,6 +37,7 @@ use std::iter; use heck::ToUpperCamelCase; use uniffi_meta::Checksum; +use crate::Renameable; use super::ffi::{FfiArgument, FfiCallbackFunction, FfiField, FfiFunction, FfiStruct, FfiType}; use super::object::Method; @@ -114,6 +115,22 @@ impl CallbackInterface { } } +impl Renameable for CallbackInterface { + fn name(&self) -> &str { + &self.name + } + + fn rename(&mut self, name: String) { + println!("FOUND NAME"); + self.name = name; + println!("ENUM {self:?}"); + } + + fn rename_nested(&mut self, _new_name: String) { + // no nested variables + } +} + impl AsType for CallbackInterface { fn as_type(&self) -> Type { Type::CallbackInterface { diff --git a/uniffi_bindgen/src/interface/conventions.rs b/uniffi_bindgen/src/interface/conventions.rs new file mode 100644 index 0000000000..3b81c368c7 --- /dev/null +++ b/uniffi_bindgen/src/interface/conventions.rs @@ -0,0 +1,25 @@ +use crate::{CodeOracle, ComponentInterface, Renameable}; + +impl ComponentInterface { + pub fn apply_naming_conventions(&mut self, oracle: O) { + for enum_item in self.enums.values_mut() { + enum_item.rename(oracle.class_name(enum_item.name())); + } + + for record_item in self.records.values_mut() { + record_item.rename(oracle.class_name(record_item.name())); + } + + for function_item in self.functions.iter_mut() { + function_item.rename(oracle.fn_name(function_item.name())); + } + + for object_item in self.objects.iter_mut() { + object_item.rename(oracle.class_name(object_item.name())); + } + + for callback_interface in self.callback_interfaces.iter_mut() { + callback_interface.rename(oracle.class_name(callback_interface.name())); + } + } +} \ No newline at end of file diff --git a/uniffi_bindgen/src/interface/enum_.rs b/uniffi_bindgen/src/interface/enum_.rs index 2418978b22..82e4cf8ba7 100644 --- a/uniffi_bindgen/src/interface/enum_.rs +++ b/uniffi_bindgen/src/interface/enum_.rs @@ -161,6 +161,7 @@ use anyhow::Result; use uniffi_meta::{Checksum, EnumShape}; +use crate::Renameable; use super::record::Field; use super::{AsType, Literal, Type, TypeIterator}; @@ -250,6 +251,24 @@ impl Enum { } } +impl Renameable for Enum { + fn name(&self) -> &str { + &self.name + } + + fn rename(&mut self, name: String) { + println!("FOUND NAME"); + self.name = name; + println!("ENUM {self:?}"); + } + + fn rename_nested(&mut self, new_name: String) { + for variant in &mut self.variants { + variant.name = new_name.clone(); + } + } +} + impl TryFrom for Enum { type Error = anyhow::Error; diff --git a/uniffi_bindgen/src/interface/function.rs b/uniffi_bindgen/src/interface/function.rs index 8effc4c876..1b14a21ed7 100644 --- a/uniffi_bindgen/src/interface/function.rs +++ b/uniffi_bindgen/src/interface/function.rs @@ -37,6 +37,7 @@ use anyhow::Result; use super::ffi::{FfiArgument, FfiFunction, FfiType}; use super::{AsType, ComponentInterface, Literal, ObjectImpl, Type, TypeIterator}; use uniffi_meta::Checksum; +use crate::Renameable; /// Represents a standalone function. /// @@ -136,6 +137,20 @@ impl Function { } } +impl Renameable for Function { + fn name(&self) -> &str { + &self.name + } + + fn rename(&mut self, name: String) { + self.name = name; + } + + fn rename_nested(&mut self, _new_name: String) { + // Functions do not contain other nested elements + } +} + impl From for Argument { fn from(meta: uniffi_meta::FnParamMetadata) -> Self { Argument { diff --git a/uniffi_bindgen/src/interface/mod.rs b/uniffi_bindgen/src/interface/mod.rs index 19c2152672..5f08131480 100644 --- a/uniffi_bindgen/src/interface/mod.rs +++ b/uniffi_bindgen/src/interface/mod.rs @@ -67,6 +67,8 @@ mod record; pub use record::{Field, Record}; pub mod ffi; +mod conventions; + pub use ffi::{ FfiArgument, FfiCallbackFunction, FfiDefinition, FfiField, FfiFunction, FfiStruct, FfiType, }; @@ -79,7 +81,7 @@ pub type Literal = LiteralMetadata; /// The main public interface for this module, representing the complete details of an interface exposed /// by a rust component and the details of consuming it via an extern-C FFI layer. -#[derive(Debug, Default)] +#[derive(Clone, Debug, Default)] pub struct ComponentInterface { /// All of the types used in the interface. // We can't checksum `self.types`, but its contents are implied by the other fields diff --git a/uniffi_bindgen/src/interface/object.rs b/uniffi_bindgen/src/interface/object.rs index 2b86e54a45..4bd718b386 100644 --- a/uniffi_bindgen/src/interface/object.rs +++ b/uniffi_bindgen/src/interface/object.rs @@ -59,6 +59,7 @@ use anyhow::Result; use uniffi_meta::Checksum; +use crate::Renameable; use super::callbacks; use super::ffi::{FfiArgument, FfiCallbackFunction, FfiFunction, FfiStruct, FfiType}; @@ -291,6 +292,22 @@ impl Object { } } +impl Renameable for Object { + fn name(&self) -> &str { + &self.name + } + + fn rename(&mut self, new_name: String) { + self.name = new_name; + } + + fn rename_nested(&mut self, new_name: String) { + for method in &mut self.methods { + method.name = new_name.clone(); + } + } +} + impl AsType for Object { fn as_type(&self) -> Type { Type::Object { diff --git a/uniffi_bindgen/src/interface/record.rs b/uniffi_bindgen/src/interface/record.rs index e9a6004189..258ab45dd9 100644 --- a/uniffi_bindgen/src/interface/record.rs +++ b/uniffi_bindgen/src/interface/record.rs @@ -46,6 +46,7 @@ use anyhow::Result; use uniffi_meta::Checksum; +use crate::Renameable; use super::Literal; use super::{AsType, Type, TypeIterator}; @@ -86,6 +87,20 @@ impl Record { } } +impl Renameable for Record { + fn name(&self) -> &str { + &self.name + } + fn rename(&mut self, name: String) { + self.name = name; + } + fn rename_nested(&mut self, new_name: String) { + for field in &mut self.fields { + field.name = new_name.clone(); + } + } +} + impl AsType for Record { fn as_type(&self) -> Type { Type::Record { diff --git a/uniffi_bindgen/src/interface/universe.rs b/uniffi_bindgen/src/interface/universe.rs index 70bc61f8a9..a1cd2cab71 100644 --- a/uniffi_bindgen/src/interface/universe.rs +++ b/uniffi_bindgen/src/interface/universe.rs @@ -21,7 +21,7 @@ pub use uniffi_meta::{AsType, ExternalKind, NamespaceMetadata, ObjectImpl, Type, /// You could imagine this struct doing some clever interning of names and so-on in future, /// to reduce the overhead of passing around [Type] instances. For now we just do a whole /// lot of cloning. -#[derive(Debug, Default)] +#[derive(Clone, Debug, Default)] pub(crate) struct TypeUniverse { /// The unique prefixes that we'll use for namespacing when exposing this component's API. pub namespace: NamespaceMetadata, diff --git a/uniffi_bindgen/src/lib.rs b/uniffi_bindgen/src/lib.rs index 7ff692361a..08f2111613 100644 --- a/uniffi_bindgen/src/lib.rs +++ b/uniffi_bindgen/src/lib.rs @@ -112,6 +112,7 @@ pub mod cargo_metadata; pub use interface::ComponentInterface; use scaffolding::RustScaffolding; +use crate::interface::{FfiType, Object}; /// The options used when creating bindings. Named such /// it doesn't cause confusion that it's settings specific to @@ -160,6 +161,51 @@ pub trait BindingGenerator: Sized { ) -> Result<()>; } +pub trait CodeOracle { + /// Get the idiomatic Python rendering of a class name (for enums, records, errors, etc). + fn class_name(&self, nm: &str) -> String; + + /// Get the idiomatic Python rendering of a function name. + fn fn_name(&self, nm: &str) -> String; + + /// Get the idiomatic Python rendering of a variable name. + fn var_name(&self, nm: &str) -> String; + + /// Get the idiomatic Python rendering of an individual enum variant. + fn enum_variant_name(&self, nm: &str) -> String; + + /// Get the idiomatic Python rendering of an FFI callback function name + fn ffi_callback_name(&self, nm: &str) -> String; + + /// Get the idiomatic Python rendering of an FFI struct name + fn ffi_struct_name(&self, nm: &str) -> String; + + fn ffi_type_label(&self, ffi_type: &FfiType) -> String; + + /// Default values for FFI types + /// + /// Used to set a default return value when returning an error + fn ffi_default_value(&self, return_type: Option<&FfiType>) -> String; + + /// Get the name of the protocol and class name for an object. + /// + /// If we support callback interfaces, the protocol name is the object name, and the class name is derived from that. + /// Otherwise, the class name is the object name and the protocol name is derived from that. + /// + /// This split determines what types `FfiConverter.lower()` inputs. If we support callback + /// interfaces, `lower` must lower anything that implements the protocol. If not, then lower + /// only lowers the concrete class. + fn object_names(&self, obj: &Object) -> (String, String); +} + +pub trait Renameable { + fn name(&self) -> &str; + + fn rename(&mut self, new_name: String); + + fn rename_nested(&mut self, new_name: String); +} + /// Everything needed to generate a ComponentInterface. #[derive(Debug)] pub struct Component { From 09fdc91bcdbf9459e67637a22d2dd8bbe0e8edde Mon Sep 17 00:00:00 2001 From: Bastian Gruber Date: Fri, 19 Jul 2024 12:57:01 -0300 Subject: [PATCH 25/62] feat: remove filter functions --- .../src/bindings/python/gen_python/mod.rs | 24 +++++------ .../python/templates/CallbackInterfaceImpl.py | 10 ++--- .../bindings/python/templates/EnumTemplate.py | 42 +++++++++---------- .../python/templates/ErrorTemplate.py | 10 ++--- .../templates/NamespaceLibraryTemplate.py | 2 +- .../python/templates/ObjectTemplate.py | 6 +-- .../src/bindings/python/templates/Protocol.py | 2 +- .../python/templates/RecordTemplate.py | 16 +++---- .../templates/TopLevelFunctionTemplate.py | 8 ++-- .../src/bindings/python/templates/macros.py | 18 ++++---- .../src/bindings/python/templates/wrapper.py | 2 +- 11 files changed, 70 insertions(+), 70 deletions(-) diff --git a/uniffi_bindgen/src/bindings/python/gen_python/mod.rs b/uniffi_bindgen/src/bindings/python/gen_python/mod.rs index bcde25ab47..d7415c2d49 100644 --- a/uniffi_bindgen/src/bindings/python/gen_python/mod.rs +++ b/uniffi_bindgen/src/bindings/python/gen_python/mod.rs @@ -558,20 +558,20 @@ pub mod filters { // Ok(PythonCodeOracle.class_name(nm)) // } - /// Get the idiomatic Python rendering of a function name. - pub fn fn_name(nm: &str) -> Result { - Ok(PythonCodeOracle.fn_name(nm)) - } + // /// Get the idiomatic Python rendering of a function name. + // pub fn fn_name(nm: &str) -> Result { + // Ok(PythonCodeOracle.fn_name(nm)) + // } - /// Get the idiomatic Python rendering of a variable name. - pub fn var_name(nm: &str) -> Result { - Ok(PythonCodeOracle.var_name(nm)) - } + // /// Get the idiomatic Python rendering of a variable name. + // pub fn var_name(nm: &str) -> Result { + // Ok(PythonCodeOracle.var_name(nm)) + // } - /// Get the idiomatic Python rendering of an individual enum variant. - pub fn enum_variant_py(nm: &str) -> Result { - Ok(PythonCodeOracle.enum_variant_name(nm)) - } + // /// Get the idiomatic Python rendering of an individual enum variant. + // pub fn enum_variant_py(nm: &str) -> Result { + // Ok(PythonCodeOracle.enum_variant_name(nm)) + // } /// Get the idiomatic Python rendering of an FFI callback function name pub fn ffi_callback_name(nm: &str) -> Result { diff --git a/uniffi_bindgen/src/bindings/python/templates/CallbackInterfaceImpl.py b/uniffi_bindgen/src/bindings/python/templates/CallbackInterfaceImpl.py index d3d67a1a97..1009505d63 100644 --- a/uniffi_bindgen/src/bindings/python/templates/CallbackInterfaceImpl.py +++ b/uniffi_bindgen/src/bindings/python/templates/CallbackInterfaceImpl.py @@ -7,9 +7,9 @@ class {{ trait_impl }}: {%- for (ffi_callback, meth) in vtable_methods.iter() %} @{{ ffi_callback.name()|ffi_callback_name }} - def {{ meth.name()|fn_name }}( + def {{ meth.name() }}( {%- for arg in ffi_callback.arguments() %} - {{ arg.name()|var_name }}, + {{ arg.name() }}, {%- endfor -%} {%- if ffi_callback.has_rust_call_status_arg() %} uniffi_call_status_ptr, @@ -17,8 +17,8 @@ def {{ meth.name()|fn_name }}( ): uniffi_obj = {{ ffi_converter_name }}._handle_map.get(uniffi_handle) def make_call(): - args = ({% for arg in meth.arguments() %}{{ arg|lift_fn }}({{ arg.name()|var_name }}), {% endfor %}) - method = uniffi_obj.{{ meth.name()|fn_name }} + args = ({% for arg in meth.arguments() %}{{ arg|lift_fn }}({{ arg.name() }}), {% endfor %}) + method = uniffi_obj.{{ meth.name() }} return method(*args) {% if !meth.is_async() %} @@ -89,7 +89,7 @@ def _uniffi_free(uniffi_handle): # Generate the FFI VTable. This has a field for each callback interface method. _uniffi_vtable = {{ vtable|ffi_type_name }}( {%- for (_, meth) in vtable_methods.iter() %} - {{ meth.name()|fn_name }}, + {{ meth.name() }}, {%- endfor %} _uniffi_free ) diff --git a/uniffi_bindgen/src/bindings/python/templates/EnumTemplate.py b/uniffi_bindgen/src/bindings/python/templates/EnumTemplate.py index c308edea39..99a9ecaba2 100644 --- a/uniffi_bindgen/src/bindings/python/templates/EnumTemplate.py +++ b/uniffi_bindgen/src/bindings/python/templates/EnumTemplate.py @@ -9,7 +9,7 @@ class {{ type_name }}(enum.Enum): {%- call py::docstring(e, 4) %} {%- for variant in e.variants() %} - {{ variant.name()|enum_variant_py }} = {{ e|variant_discr_literal(loop.index0) }} + {{ variant.name() }} = {{ e|variant_discr_literal(loop.index0) }} {%- call py::docstring(variant, 4) %} {% endfor %} {% else %} @@ -21,7 +21,7 @@ def __init__(self): # Each enum variant is a nested class of the enum itself. {% for variant in e.variants() -%} - class {{ variant.name()|enum_variant_py }}: + class {{ variant.name() }}: {%- call py::docstring(variant, 8) %} {%- if variant.has_nameless_fields() %} @@ -38,36 +38,36 @@ def __getitem__(self, index): return self._values[index] def __str__(self): - return f"{{ type_name }}.{{ variant.name()|enum_variant_py }}{self._values!r}" + return f"{{ type_name }}.{{ variant.name() }}{self._values!r}" def __eq__(self, other): - if not other.is_{{ variant.name()|var_name }}(): + if not other.is_{{ variant.name() }}(): return False return self._values == other._values {%- else -%} {%- for field in variant.fields() %} - {{ field.name()|var_name }}: "{{ field|type_name }}" + {{ field.name() }}: "{{ field|type_name }}" {%- call py::docstring(field, 8) %} {%- endfor %} - def __init__(self,{% for field in variant.fields() %}{{ field.name()|var_name }}: "{{- field|type_name }}"{% if loop.last %}{% else %}, {% endif %}{% endfor %}): + def __init__(self,{% for field in variant.fields() %}{{ field.name() }}: "{{- field|type_name }}"{% if loop.last %}{% else %}, {% endif %}{% endfor %}): {%- if variant.has_fields() %} {%- for field in variant.fields() %} - self.{{ field.name()|var_name }} = {{ field.name()|var_name }} + self.{{ field.name() }} = {{ field.name() }} {%- endfor %} {%- else %} pass {%- endif %} def __str__(self): - return "{{ type_name }}.{{ variant.name()|enum_variant_py }}({% for field in variant.fields() %}{{ field.name()|var_name }}={}{% if loop.last %}{% else %}, {% endif %}{% endfor %})".format({% for field in variant.fields() %}self.{{ field.name()|var_name }}{% if loop.last %}{% else %}, {% endif %}{% endfor %}) + return "{{ type_name }}.{{ variant.name() }}({% for field in variant.fields() %}{{ field.name() }}={}{% if loop.last %}{% else %}, {% endif %}{% endfor %})".format({% for field in variant.fields() %}self.{{ field.name() }}{% if loop.last %}{% else %}, {% endif %}{% endfor %}) def __eq__(self, other): - if not other.is_{{ variant.name()|var_name }}(): + if not other.is_{{ variant.name() }}(): return False {%- for field in variant.fields() %} - if self.{{ field.name()|var_name }} != other.{{ field.name()|var_name }}: + if self.{{ field.name() }} != other.{{ field.name() }}: return False {%- endfor %} return True @@ -77,15 +77,15 @@ def __eq__(self, other): # For each variant, we have an `is_NAME` method for easily checking # whether an instance is that variant. {% for variant in e.variants() -%} - def is_{{ variant.name()|var_name }}(self) -> bool: - return isinstance(self, {{ type_name }}.{{ variant.name()|enum_variant_py }}) + def is_{{ variant.name() }}(self) -> bool: + return isinstance(self, {{ type_name }}.{{ variant.name() }}) {% endfor %} # Now, a little trick - we make each nested variant class be a subclass of the main # enum class, so that method calls and instance checks etc will work intuitively. # We might be able to do this a little more neatly with a metaclass, but this'll do. {% for variant in e.variants() -%} -{{ type_name }}.{{ variant.name()|enum_variant_py }} = type("{{ type_name }}.{{ variant.name()|enum_variant_py }}", ({{ type_name }}.{{variant.name()|enum_variant_py}}, {{ type_name }},), {}) # type: ignore +{{ type_name }}.{{ variant.name() }} = type("{{ type_name }}.{{ variant.name() }}", ({{ type_name }}.{{variant.name()}}, {{ type_name }},), {}) # type: ignore {% endfor %} {% endif %} @@ -98,9 +98,9 @@ def read(buf): {%- for variant in e.variants() %} if variant == {{ loop.index }}: {%- if e.is_flat() %} - return {{ type_name }}.{{variant.name()|enum_variant_py}} + return {{ type_name }}.{{variant.name()}} {%- else %} - return {{ type_name }}.{{variant.name()|enum_variant_py}}( + return {{ type_name }}.{{variant.name()}}( {%- for field in variant.fields() %} {{ field|read_fn }}(buf), {%- endfor %} @@ -116,15 +116,15 @@ def check_lower(value): {%- else %} {%- for variant in e.variants() %} {%- if e.is_flat() %} - if value == {{ type_name }}.{{ variant.name()|enum_variant_py }}: + if value == {{ type_name }}.{{ variant.name() }}: {%- else %} - if value.is_{{ variant.name()|var_name }}(): + if value.is_{{ variant.name() }}(): {%- endif %} {%- for field in variant.fields() %} {%- if variant.has_nameless_fields() %} {{ field|check_lower_fn }}(value._values[{{ loop.index0 }}]) {%- else %} - {{ field|check_lower_fn }}(value.{{ field.name()|var_name }}) + {{ field|check_lower_fn }}(value.{{ field.name() }}) {%- endif %} {%- endfor %} return @@ -136,16 +136,16 @@ def check_lower(value): def write(value, buf): {%- for variant in e.variants() %} {%- if e.is_flat() %} - if value == {{ type_name }}.{{ variant.name()|enum_variant_py }}: + if value == {{ type_name }}.{{ variant.name() }}: buf.write_i32({{ loop.index }}) {%- else %} - if value.is_{{ variant.name()|var_name }}(): + if value.is_{{ variant.name() }}(): buf.write_i32({{ loop.index }}) {%- for field in variant.fields() %} {%- if variant.has_nameless_fields() %} {{ field|write_fn }}(value._values[{{ loop.index0 }}], buf) {%- else %} - {{ field|write_fn }}(value.{{ field.name()|var_name }}, buf) + {{ field|write_fn }}(value.{{ field.name() }}, buf) {%- endif %} {%- endfor %} {%- endif %} diff --git a/uniffi_bindgen/src/bindings/python/templates/ErrorTemplate.py b/uniffi_bindgen/src/bindings/python/templates/ErrorTemplate.py index 8037155d1e..131e7efa01 100644 --- a/uniffi_bindgen/src/bindings/python/templates/ErrorTemplate.py +++ b/uniffi_bindgen/src/bindings/python/templates/ErrorTemplate.py @@ -39,15 +39,15 @@ def __getitem__(self, index): return self._values[index] {%- else %} - def __init__(self{% for field in variant.fields() %}, {{ field.name()|var_name }}{% endfor %}): + def __init__(self{% for field in variant.fields() %}, {{ field.name() }}{% endfor %}): {%- if variant.has_fields() %} super().__init__(", ".join([ {%- for field in variant.fields() %} - "{{ field.name()|var_name }}={!r}".format({{ field.name()|var_name }}), + "{{ field.name() }}={!r}".format({{ field.name() }}), {%- endfor %} ])) {%- for field in variant.fields() %} - self.{{ field.name()|var_name }} = {{ field.name()|var_name }} + self.{{ field.name() }} = {{ field.name() }} {%- endfor %} {%- else %} pass @@ -93,7 +93,7 @@ def check_lower(value): {%- if variant.has_nameless_fields() %} {{ field|check_lower_fn }}(value._values[{{ loop.index0 }}]) {%- else %} - {{ field|check_lower_fn }}(value.{{ field.name()|var_name }}) + {{ field|check_lower_fn }}(value.{{ field.name() }}) {%- endif %} {%- endfor %} return @@ -109,7 +109,7 @@ def write(value, buf): {%- if variant.has_nameless_fields() %} {{ field|write_fn }}(value._values[{{ loop.index0 }}], buf) {%- else %} - {{ field|write_fn }}(value.{{ field.name()|var_name }}, buf) + {{ field|write_fn }}(value.{{ field.name() }}, buf) {%- endif %} {%- endfor %} {%- endfor %} diff --git a/uniffi_bindgen/src/bindings/python/templates/NamespaceLibraryTemplate.py b/uniffi_bindgen/src/bindings/python/templates/NamespaceLibraryTemplate.py index 1929f9aad6..174ea2d353 100644 --- a/uniffi_bindgen/src/bindings/python/templates/NamespaceLibraryTemplate.py +++ b/uniffi_bindgen/src/bindings/python/templates/NamespaceLibraryTemplate.py @@ -76,7 +76,7 @@ def _uniffi_check_api_checksums(lib): class {{ ffi_struct.name()|ffi_struct_name }}(ctypes.Structure): _fields_ = [ {%- for field in ffi_struct.fields() %} - ("{{ field.name()|var_name }}", {{ field.type_().borrow()|ffi_type_name }}), + ("{{ field.name() }}", {{ field.type_().borrow()|ffi_type_name }}), {%- endfor %} ] {%- when FfiDefinition::Function(func) %} diff --git a/uniffi_bindgen/src/bindings/python/templates/ObjectTemplate.py b/uniffi_bindgen/src/bindings/python/templates/ObjectTemplate.py index 33a914195e..5772ad24db 100644 --- a/uniffi_bindgen/src/bindings/python/templates/ObjectTemplate.py +++ b/uniffi_bindgen/src/bindings/python/templates/ObjectTemplate.py @@ -52,7 +52,7 @@ def _make_instance_(cls, pointer): @classmethod {%- if cons.is_async() %} - async def {{ cons.name()|fn_name }}(cls, {% call py::arg_list_decl(cons) %}): + async def {{ cons.name() }}(cls, {% call py::arg_list_decl(cons) %}): {%- call py::docstring(cons, 8) %} {%- call py::setup_args_extra_indent(cons) %} @@ -65,7 +65,7 @@ async def {{ cons.name()|fn_name }}(cls, {% call py::arg_list_decl(cons) %}): {% call py::error_ffi_converter(cons) %} ) {%- else %} - def {{ cons.name()|fn_name }}(cls, {% call py::arg_list_decl(cons) %}): + def {{ cons.name() }}(cls, {% call py::arg_list_decl(cons) %}): {%- call py::docstring(cons, 8) %} {%- call py::setup_args_extra_indent(cons) %} # Call the (fallible) function before creating any half-baked object instances. @@ -75,7 +75,7 @@ def {{ cons.name()|fn_name }}(cls, {% call py::arg_list_decl(cons) %}): {% endfor %} {%- for meth in obj.methods() -%} - {%- call py::method_decl(meth.name()|fn_name, meth) %} + {%- call py::method_decl(meth.name(), meth) %} {%- endfor %} {%- for tm in obj.uniffi_traits() -%} {%- match tm %} diff --git a/uniffi_bindgen/src/bindings/python/templates/Protocol.py b/uniffi_bindgen/src/bindings/python/templates/Protocol.py index 3b7e93596a..8e5012ddd4 100644 --- a/uniffi_bindgen/src/bindings/python/templates/Protocol.py +++ b/uniffi_bindgen/src/bindings/python/templates/Protocol.py @@ -1,7 +1,7 @@ class {{ protocol_name }}(typing.Protocol): {%- call py::docstring_value(protocol_docstring, 4) %} {%- for meth in methods.iter() %} - def {{ meth.name()|fn_name }}(self, {% call py::arg_list_decl(meth) %}): + def {{ meth.name() }}(self, {% call py::arg_list_decl(meth) %}): {%- call py::docstring(meth, 8) %} raise NotImplementedError {%- else %} diff --git a/uniffi_bindgen/src/bindings/python/templates/RecordTemplate.py b/uniffi_bindgen/src/bindings/python/templates/RecordTemplate.py index cf0d0653d7..5028542fce 100644 --- a/uniffi_bindgen/src/bindings/python/templates/RecordTemplate.py +++ b/uniffi_bindgen/src/bindings/python/templates/RecordTemplate.py @@ -2,18 +2,18 @@ class {{ type_name }}: {%- call py::docstring(rec, 4) %} {%- for field in rec.fields() %} - {{ field.name()|var_name }}: "{{ field|type_name }}" + {{ field.name() }}: "{{ field|type_name }}" {%- call py::docstring(field, 4) %} {%- endfor %} {%- if rec.has_fields() %} def __init__(self, *, {% for field in rec.fields() %} - {{- field.name()|var_name }}: "{{- field|type_name }}" + {{- field.name() }}: "{{- field|type_name }}" {%- if field.default_value().is_some() %} = _DEFAULT{% endif %} {%- if !loop.last %}, {% endif %} {%- endfor %}): {%- for field in rec.fields() %} - {%- let field_name = field.name()|var_name %} + {%- let field_name = field.name() %} {%- match field.default_value() %} {%- when None %} self.{{ field_name }} = {{ field_name }} @@ -27,11 +27,11 @@ def __init__(self, *, {% for field in rec.fields() %} {%- endif %} def __str__(self): - return "{{ type_name }}({% for field in rec.fields() %}{{ field.name()|var_name }}={}{% if loop.last %}{% else %}, {% endif %}{% endfor %})".format({% for field in rec.fields() %}self.{{ field.name()|var_name }}{% if loop.last %}{% else %}, {% endif %}{% endfor %}) + return "{{ type_name }}({% for field in rec.fields() %}{{ field.name() }}={}{% if loop.last %}{% else %}, {% endif %}{% endfor %})".format({% for field in rec.fields() %}self.{{ field.name() }}{% if loop.last %}{% else %}, {% endif %}{% endfor %}) def __eq__(self, other): {%- for field in rec.fields() %} - if self.{{ field.name()|var_name }} != other.{{ field.name()|var_name }}: + if self.{{ field.name() }} != other.{{ field.name() }}: return False {%- endfor %} return True @@ -41,7 +41,7 @@ class {{ ffi_converter_name }}(_UniffiConverterRustBuffer): def read(buf): return {{ type_name }}( {%- for field in rec.fields() %} - {{ field.name()|var_name }}={{ field|read_fn }}(buf), + {{ field.name() }}={{ field|read_fn }}(buf), {%- endfor %} ) @@ -51,7 +51,7 @@ def check_lower(value): pass {%- else %} {%- for field in rec.fields() %} - {{ field|check_lower_fn }}(value.{{ field.name()|var_name }}) + {{ field|check_lower_fn }}(value.{{ field.name() }}) {%- endfor %} {%- endif %} @@ -59,7 +59,7 @@ def check_lower(value): def write(value, buf): {%- if rec.has_fields() %} {%- for field in rec.fields() %} - {{ field|write_fn }}(value.{{ field.name()|var_name }}, buf) + {{ field|write_fn }}(value.{{ field.name() }}, buf) {%- endfor %} {%- else %} pass diff --git a/uniffi_bindgen/src/bindings/python/templates/TopLevelFunctionTemplate.py b/uniffi_bindgen/src/bindings/python/templates/TopLevelFunctionTemplate.py index 230b9e853f..7a6cb6d7c2 100644 --- a/uniffi_bindgen/src/bindings/python/templates/TopLevelFunctionTemplate.py +++ b/uniffi_bindgen/src/bindings/python/templates/TopLevelFunctionTemplate.py @@ -2,9 +2,9 @@ {%- match func.return_type() -%} {%- when Some with (return_type) %} -async def {{ func.name()|fn_name }}({%- call py::arg_list_decl(func) -%}) -> "{{ return_type|type_name }}": +async def {{ func.name() }}({%- call py::arg_list_decl(func) -%}) -> "{{ return_type|type_name }}": {% when None %} -async def {{ func.name()|fn_name }}({%- call py::arg_list_decl(func) -%}) -> None: +async def {{ func.name() }}({%- call py::arg_list_decl(func) -%}) -> None: {% endmatch %} {%- call py::docstring(func, 4) %} @@ -28,13 +28,13 @@ async def {{ func.name()|fn_name }}({%- call py::arg_list_decl(func) -%}) -> Non {%- match func.return_type() -%} {%- when Some with (return_type) %} -def {{ func.name()|fn_name }}({%- call py::arg_list_decl(func) -%}) -> "{{ return_type|type_name }}": +def {{ func.name() }}({%- call py::arg_list_decl(func) -%}) -> "{{ return_type|type_name }}": {%- call py::docstring(func, 4) %} {%- call py::setup_args(func) %} return {{ return_type|lift_fn }}({% call py::to_ffi_call(func) %}) {% when None %} -def {{ func.name()|fn_name }}({%- call py::arg_list_decl(func) -%}) -> None: +def {{ func.name() }}({%- call py::arg_list_decl(func) -%}) -> None: {%- call py::docstring(func, 4) %} {%- call py::setup_args(func) %} {% call py::to_ffi_call(func) %} diff --git a/uniffi_bindgen/src/bindings/python/templates/macros.py b/uniffi_bindgen/src/bindings/python/templates/macros.py index 7fdf66e02b..8d84ccc251 100644 --- a/uniffi_bindgen/src/bindings/python/templates/macros.py +++ b/uniffi_bindgen/src/bindings/python/templates/macros.py @@ -34,7 +34,7 @@ {%- macro arg_list_lowered(func) %} {%- for arg in func.arguments() %} - {{ arg|lower_fn }}({{ arg.name()|var_name }}) + {{ arg|lower_fn }}({{ arg.name() }}) {%- if !loop.last %},{% endif %} {%- endfor %} {%- endmacro -%} @@ -54,12 +54,12 @@ {#- // Arglist as used in Python declarations of methods, functions and constructors. -// Note the var_name and type_name filters. +// Note the and type_name filters. -#} {% macro arg_list_decl(func) %} {%- for arg in func.arguments() -%} - {{ arg.name()|var_name }} + {{ arg.name() }} {%- match arg.default_value() %} {%- when Some with(literal) %}: "typing.Union[object, {{ arg|type_name -}}]" = _DEFAULT {%- else %}: "{{ arg|type_name -}}" @@ -88,10 +88,10 @@ {%- match arg.default_value() %} {%- when None %} {%- when Some with(literal) %} - if {{ arg.name()|var_name }} is _DEFAULT: - {{ arg.name()|var_name }} = {{ literal|literal_py(arg.as_type().borrow()) }} + if {{ arg.name() }} is _DEFAULT: + {{ arg.name() }} = {{ literal|literal_py(arg.as_type().borrow()) }} {%- endmatch %} - {{ arg|check_lower_fn }}({{ arg.name()|var_name }}) + {{ arg|check_lower_fn }}({{ arg.name() }}) {% endfor -%} {%- endmacro -%} @@ -104,10 +104,10 @@ {%- match arg.default_value() %} {%- when None %} {%- when Some with(literal) %} - if {{ arg.name()|var_name }} is _DEFAULT: - {{ arg.name()|var_name }} = {{ literal|literal_py(arg.as_type().borrow()) }} + if {{ arg.name() }} is _DEFAULT: + {{ arg.name() }} = {{ literal|literal_py(arg.as_type().borrow()) }} {%- endmatch %} - {{ arg|check_lower_fn }}({{ arg.name()|var_name }}) + {{ arg|check_lower_fn }}({{ arg.name() }}) {% endfor -%} {%- endmacro -%} diff --git a/uniffi_bindgen/src/bindings/python/templates/wrapper.py b/uniffi_bindgen/src/bindings/python/templates/wrapper.py index d1eb00b285..c81052e4cc 100644 --- a/uniffi_bindgen/src/bindings/python/templates/wrapper.py +++ b/uniffi_bindgen/src/bindings/python/templates/wrapper.py @@ -67,7 +67,7 @@ "{{ record|type_name }}", {%- endfor %} {%- for func in ci.function_definitions() %} - "{{ func.name()|fn_name }}", + "{{ func.name() }}", {%- endfor %} {%- for obj in ci.object_definitions() %} "{{ obj|type_name }}", From a645721a8d5e53f805039b4111d1626651dc025c Mon Sep 17 00:00:00 2001 From: Bastian Gruber Date: Wed, 31 Jul 2024 21:18:01 -0300 Subject: [PATCH 26/62] fix: cargo --- .../bindings/python/templates/RecordTemplate.py | 16 ++++++++-------- uniffi_bindgen/src/interface/callbacks.rs | 4 +--- uniffi_bindgen/src/interface/conventions.rs | 2 +- uniffi_bindgen/src/interface/enum_.rs | 4 +--- uniffi_bindgen/src/interface/function.rs | 3 ++- uniffi_bindgen/src/interface/mod.rs | 3 +-- uniffi_bindgen/src/interface/object.rs | 2 +- uniffi_bindgen/src/interface/record.rs | 2 +- uniffi_bindgen/src/lib.rs | 2 +- 9 files changed, 17 insertions(+), 21 deletions(-) diff --git a/uniffi_bindgen/src/bindings/python/templates/RecordTemplate.py b/uniffi_bindgen/src/bindings/python/templates/RecordTemplate.py index 5028542fce..d778b90ebd 100644 --- a/uniffi_bindgen/src/bindings/python/templates/RecordTemplate.py +++ b/uniffi_bindgen/src/bindings/python/templates/RecordTemplate.py @@ -2,18 +2,18 @@ class {{ type_name }}: {%- call py::docstring(rec, 4) %} {%- for field in rec.fields() %} - {{ field.name() }}: "{{ field|type_name }}" + {{ field.name() }}: "{{ field|type_name }}" {%- call py::docstring(field, 4) %} {%- endfor %} {%- if rec.has_fields() %} def __init__(self, *, {% for field in rec.fields() %} - {{- field.name() }}: "{{- field|type_name }}" + {{- field.name() }}: "{{- field|type_name }}" {%- if field.default_value().is_some() %} = _DEFAULT{% endif %} {%- if !loop.last %}, {% endif %} {%- endfor %}): {%- for field in rec.fields() %} - {%- let field_name = field.name() %} + {%- let field_name = field.name() %} {%- match field.default_value() %} {%- when None %} self.{{ field_name }} = {{ field_name }} @@ -27,11 +27,11 @@ def __init__(self, *, {% for field in rec.fields() %} {%- endif %} def __str__(self): - return "{{ type_name }}({% for field in rec.fields() %}{{ field.name() }}={}{% if loop.last %}{% else %}, {% endif %}{% endfor %})".format({% for field in rec.fields() %}self.{{ field.name() }}{% if loop.last %}{% else %}, {% endif %}{% endfor %}) + return "{{ type_name }}({% for field in rec.fields() %}{{ field.name() }}={}{% if loop.last %}{% else %}, {% endif %}{% endfor %})".format({% for field in rec.fields() %}self.{{ field.name() }}{% if loop.last %}{% else %}, {% endif %}{% endfor %}) def __eq__(self, other): {%- for field in rec.fields() %} - if self.{{ field.name() }} != other.{{ field.name() }}: + if self.{{ field.name() }} != other.{{ field.name() }}: return False {%- endfor %} return True @@ -41,7 +41,7 @@ class {{ ffi_converter_name }}(_UniffiConverterRustBuffer): def read(buf): return {{ type_name }}( {%- for field in rec.fields() %} - {{ field.name() }}={{ field|read_fn }}(buf), + {{ field.name() }}={{ field|read_fn }}(buf), {%- endfor %} ) @@ -51,7 +51,7 @@ def check_lower(value): pass {%- else %} {%- for field in rec.fields() %} - {{ field|check_lower_fn }}(value.{{ field.name() }}) + {{ field|check_lower_fn }}(value.{{ field.name() }}) {%- endfor %} {%- endif %} @@ -59,7 +59,7 @@ def check_lower(value): def write(value, buf): {%- if rec.has_fields() %} {%- for field in rec.fields() %} - {{ field|write_fn }}(value.{{ field.name() }}, buf) + {{ field|write_fn }}(value.{{ field.name() }}, buf) {%- endfor %} {%- else %} pass diff --git a/uniffi_bindgen/src/interface/callbacks.rs b/uniffi_bindgen/src/interface/callbacks.rs index c020f49301..3300f8a94f 100644 --- a/uniffi_bindgen/src/interface/callbacks.rs +++ b/uniffi_bindgen/src/interface/callbacks.rs @@ -35,9 +35,9 @@ use std::iter; +use crate::Renameable; use heck::ToUpperCamelCase; use uniffi_meta::Checksum; -use crate::Renameable; use super::ffi::{FfiArgument, FfiCallbackFunction, FfiField, FfiFunction, FfiStruct, FfiType}; use super::object::Method; @@ -121,9 +121,7 @@ impl Renameable for CallbackInterface { } fn rename(&mut self, name: String) { - println!("FOUND NAME"); self.name = name; - println!("ENUM {self:?}"); } fn rename_nested(&mut self, _new_name: String) { diff --git a/uniffi_bindgen/src/interface/conventions.rs b/uniffi_bindgen/src/interface/conventions.rs index 3b81c368c7..56ab3e8c1f 100644 --- a/uniffi_bindgen/src/interface/conventions.rs +++ b/uniffi_bindgen/src/interface/conventions.rs @@ -22,4 +22,4 @@ impl ComponentInterface { callback_interface.rename(oracle.class_name(callback_interface.name())); } } -} \ No newline at end of file +} diff --git a/uniffi_bindgen/src/interface/enum_.rs b/uniffi_bindgen/src/interface/enum_.rs index 82e4cf8ba7..a6520d1189 100644 --- a/uniffi_bindgen/src/interface/enum_.rs +++ b/uniffi_bindgen/src/interface/enum_.rs @@ -159,9 +159,9 @@ //! # Ok::<(), anyhow::Error>(()) //! ``` +use crate::Renameable; use anyhow::Result; use uniffi_meta::{Checksum, EnumShape}; -use crate::Renameable; use super::record::Field; use super::{AsType, Literal, Type, TypeIterator}; @@ -257,9 +257,7 @@ impl Renameable for Enum { } fn rename(&mut self, name: String) { - println!("FOUND NAME"); self.name = name; - println!("ENUM {self:?}"); } fn rename_nested(&mut self, new_name: String) { diff --git a/uniffi_bindgen/src/interface/function.rs b/uniffi_bindgen/src/interface/function.rs index 1b14a21ed7..3093787f73 100644 --- a/uniffi_bindgen/src/interface/function.rs +++ b/uniffi_bindgen/src/interface/function.rs @@ -33,10 +33,11 @@ //! ``` use anyhow::Result; +use uniffi_meta::Checksum; use super::ffi::{FfiArgument, FfiFunction, FfiType}; use super::{AsType, ComponentInterface, Literal, ObjectImpl, Type, TypeIterator}; -use uniffi_meta::Checksum; + use crate::Renameable; /// Represents a standalone function. diff --git a/uniffi_bindgen/src/interface/mod.rs b/uniffi_bindgen/src/interface/mod.rs index 5f08131480..1e90b8a097 100644 --- a/uniffi_bindgen/src/interface/mod.rs +++ b/uniffi_bindgen/src/interface/mod.rs @@ -66,9 +66,8 @@ pub use object::{Constructor, Method, Object, UniffiTrait}; mod record; pub use record::{Field, Record}; -pub mod ffi; mod conventions; - +pub mod ffi; pub use ffi::{ FfiArgument, FfiCallbackFunction, FfiDefinition, FfiField, FfiFunction, FfiStruct, FfiType, }; diff --git a/uniffi_bindgen/src/interface/object.rs b/uniffi_bindgen/src/interface/object.rs index 4bd718b386..78e65c5e06 100644 --- a/uniffi_bindgen/src/interface/object.rs +++ b/uniffi_bindgen/src/interface/object.rs @@ -57,9 +57,9 @@ //! # Ok::<(), anyhow::Error>(()) //! ``` +use crate::Renameable; use anyhow::Result; use uniffi_meta::Checksum; -use crate::Renameable; use super::callbacks; use super::ffi::{FfiArgument, FfiCallbackFunction, FfiFunction, FfiStruct, FfiType}; diff --git a/uniffi_bindgen/src/interface/record.rs b/uniffi_bindgen/src/interface/record.rs index 258ab45dd9..fc7ec78f11 100644 --- a/uniffi_bindgen/src/interface/record.rs +++ b/uniffi_bindgen/src/interface/record.rs @@ -44,9 +44,9 @@ //! # Ok::<(), anyhow::Error>(()) //! ``` +use crate::Renameable; use anyhow::Result; use uniffi_meta::Checksum; -use crate::Renameable; use super::Literal; use super::{AsType, Type, TypeIterator}; diff --git a/uniffi_bindgen/src/lib.rs b/uniffi_bindgen/src/lib.rs index 08f2111613..b024b8b029 100644 --- a/uniffi_bindgen/src/lib.rs +++ b/uniffi_bindgen/src/lib.rs @@ -110,9 +110,9 @@ pub mod scaffolding; #[cfg(feature = "cargo-metadata")] pub mod cargo_metadata; +use crate::interface::{FfiType, Object}; pub use interface::ComponentInterface; use scaffolding::RustScaffolding; -use crate::interface::{FfiType, Object}; /// The options used when creating bindings. Named such /// it doesn't cause confusion that it's settings specific to From 425d727df17b6b4db47a2f31cae9d014646e239f Mon Sep 17 00:00:00 2001 From: Bastian Gruber Date: Fri, 19 Jul 2024 15:09:03 -0300 Subject: [PATCH 27/62] fix: spaces --- .../src/bindings/python/templates/EnumTemplate.py | 14 +++++++------- .../src/bindings/python/templates/StringHelper.py | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/uniffi_bindgen/src/bindings/python/templates/EnumTemplate.py b/uniffi_bindgen/src/bindings/python/templates/EnumTemplate.py index 99a9ecaba2..23ebef76de 100644 --- a/uniffi_bindgen/src/bindings/python/templates/EnumTemplate.py +++ b/uniffi_bindgen/src/bindings/python/templates/EnumTemplate.py @@ -64,10 +64,10 @@ def __str__(self): return "{{ type_name }}.{{ variant.name() }}({% for field in variant.fields() %}{{ field.name() }}={}{% if loop.last %}{% else %}, {% endif %}{% endfor %})".format({% for field in variant.fields() %}self.{{ field.name() }}{% if loop.last %}{% else %}, {% endif %}{% endfor %}) def __eq__(self, other): - if not other.is_{{ variant.name() }}(): + if not other.is_{{ variant.name() }}(): return False {%- for field in variant.fields() %} - if self.{{ field.name() }} != other.{{ field.name() }}: + if self.{{ field.name() }} != other.{{ field.name() }}: return False {%- endfor %} return True @@ -77,7 +77,7 @@ def __eq__(self, other): # For each variant, we have an `is_NAME` method for easily checking # whether an instance is that variant. {% for variant in e.variants() -%} - def is_{{ variant.name() }}(self) -> bool: + def is_{{ variant.name() }}(self) -> bool: return isinstance(self, {{ type_name }}.{{ variant.name() }}) {% endfor %} @@ -118,13 +118,13 @@ def check_lower(value): {%- if e.is_flat() %} if value == {{ type_name }}.{{ variant.name() }}: {%- else %} - if value.is_{{ variant.name() }}(): + if value.is_{{ variant.name() }}(): {%- endif %} {%- for field in variant.fields() %} {%- if variant.has_nameless_fields() %} {{ field|check_lower_fn }}(value._values[{{ loop.index0 }}]) {%- else %} - {{ field|check_lower_fn }}(value.{{ field.name() }}) + {{ field|check_lower_fn }}(value.{{ field.name() }}) {%- endif %} {%- endfor %} return @@ -139,13 +139,13 @@ def write(value, buf): if value == {{ type_name }}.{{ variant.name() }}: buf.write_i32({{ loop.index }}) {%- else %} - if value.is_{{ variant.name() }}(): + if value.is_{{ variant.name() }}(): buf.write_i32({{ loop.index }}) {%- for field in variant.fields() %} {%- if variant.has_nameless_fields() %} {{ field|write_fn }}(value._values[{{ loop.index0 }}], buf) {%- else %} - {{ field|write_fn }}(value.{{ field.name() }}, buf) + {{ field|write_fn }}(value.{{ field.name() }}, buf) {%- endif %} {%- endfor %} {%- endif %} diff --git a/uniffi_bindgen/src/bindings/python/templates/StringHelper.py b/uniffi_bindgen/src/bindings/python/templates/StringHelper.py index d574edd09f..3e8687b459 100644 --- a/uniffi_bindgen/src/bindings/python/templates/StringHelper.py +++ b/uniffi_bindgen/src/bindings/python/templates/StringHelper.py @@ -1,4 +1,4 @@ -class _UniffiConverterString: + class _UniffiConverterString: @staticmethod def check_lower(value): if not isinstance(value, str): From 73a1cec16c775950e43dcaa23884d1c0088d8c3b Mon Sep 17 00:00:00 2001 From: Bastian Gruber Date: Mon, 22 Jul 2024 11:04:15 -0300 Subject: [PATCH 28/62] feate: adapt python templating --- .../src/bindings/python/gen_python/mod.rs | 24 +---------- .../bindings/python/templates/EnumTemplate.py | 10 ++--- .../bindings/python/templates/StringHelper.py | 2 +- uniffi_bindgen/src/interface/callbacks.rs | 4 -- uniffi_bindgen/src/interface/conventions.rs | 43 ++++++++++++++++++- uniffi_bindgen/src/interface/enum_.rs | 26 ++++++++--- uniffi_bindgen/src/interface/function.rs | 14 ++++-- uniffi_bindgen/src/interface/object.rs | 26 ++++++++--- uniffi_bindgen/src/interface/record.rs | 14 +++--- uniffi_bindgen/src/lib.rs | 2 - 10 files changed, 108 insertions(+), 57 deletions(-) diff --git a/uniffi_bindgen/src/bindings/python/gen_python/mod.rs b/uniffi_bindgen/src/bindings/python/gen_python/mod.rs index d7415c2d49..52d21be6fc 100644 --- a/uniffi_bindgen/src/bindings/python/gen_python/mod.rs +++ b/uniffi_bindgen/src/bindings/python/gen_python/mod.rs @@ -300,12 +300,12 @@ pub struct PythonWrapper<'a> { } impl<'a> PythonWrapper<'a> { pub fn new(config: Config, ci: &'a mut ComponentInterface) -> Self { + ci.apply_naming_conventions(PythonCodeOracle::default()); + let type_renderer = TypeRenderer::new(&config, ci); let type_helper_code = type_renderer.render().unwrap(); let type_imports = type_renderer.imports.into_inner(); - ci.apply_naming_conventions(PythonCodeOracle::default()); - Self { config, ci, @@ -553,26 +553,6 @@ pub mod filters { Ok(PythonCodeOracle.ffi_default_value(return_type.as_ref())) } - /// Get the idiomatic Python rendering of a class name (for enums, records, errors, etc). - // pub fn class_name(nm: &str) -> Result { - // Ok(PythonCodeOracle.class_name(nm)) - // } - - // /// Get the idiomatic Python rendering of a function name. - // pub fn fn_name(nm: &str) -> Result { - // Ok(PythonCodeOracle.fn_name(nm)) - // } - - // /// Get the idiomatic Python rendering of a variable name. - // pub fn var_name(nm: &str) -> Result { - // Ok(PythonCodeOracle.var_name(nm)) - // } - - // /// Get the idiomatic Python rendering of an individual enum variant. - // pub fn enum_variant_py(nm: &str) -> Result { - // Ok(PythonCodeOracle.enum_variant_name(nm)) - // } - /// Get the idiomatic Python rendering of an FFI callback function name pub fn ffi_callback_name(nm: &str) -> Result { Ok(PythonCodeOracle.ffi_callback_name(nm)) diff --git a/uniffi_bindgen/src/bindings/python/templates/EnumTemplate.py b/uniffi_bindgen/src/bindings/python/templates/EnumTemplate.py index 23ebef76de..a94f910044 100644 --- a/uniffi_bindgen/src/bindings/python/templates/EnumTemplate.py +++ b/uniffi_bindgen/src/bindings/python/templates/EnumTemplate.py @@ -41,7 +41,7 @@ def __str__(self): return f"{{ type_name }}.{{ variant.name() }}{self._values!r}" def __eq__(self, other): - if not other.is_{{ variant.name() }}(): + if not other.is_{{ variant.is_name() }}(): return False return self._values == other._values @@ -64,7 +64,7 @@ def __str__(self): return "{{ type_name }}.{{ variant.name() }}({% for field in variant.fields() %}{{ field.name() }}={}{% if loop.last %}{% else %}, {% endif %}{% endfor %})".format({% for field in variant.fields() %}self.{{ field.name() }}{% if loop.last %}{% else %}, {% endif %}{% endfor %}) def __eq__(self, other): - if not other.is_{{ variant.name() }}(): + if not other.is_{{ variant.is_name() }}(): return False {%- for field in variant.fields() %} if self.{{ field.name() }} != other.{{ field.name() }}: @@ -77,7 +77,7 @@ def __eq__(self, other): # For each variant, we have an `is_NAME` method for easily checking # whether an instance is that variant. {% for variant in e.variants() -%} - def is_{{ variant.name() }}(self) -> bool: + def is_{{ variant.is_name() }}(self) -> bool: return isinstance(self, {{ type_name }}.{{ variant.name() }}) {% endfor %} @@ -118,7 +118,7 @@ def check_lower(value): {%- if e.is_flat() %} if value == {{ type_name }}.{{ variant.name() }}: {%- else %} - if value.is_{{ variant.name() }}(): + if value.is_{{ variant.is_name() }}(): {%- endif %} {%- for field in variant.fields() %} {%- if variant.has_nameless_fields() %} @@ -139,7 +139,7 @@ def write(value, buf): if value == {{ type_name }}.{{ variant.name() }}: buf.write_i32({{ loop.index }}) {%- else %} - if value.is_{{ variant.name() }}(): + if value.is_{{ variant.is_name() }}(): buf.write_i32({{ loop.index }}) {%- for field in variant.fields() %} {%- if variant.has_nameless_fields() %} diff --git a/uniffi_bindgen/src/bindings/python/templates/StringHelper.py b/uniffi_bindgen/src/bindings/python/templates/StringHelper.py index 3e8687b459..d574edd09f 100644 --- a/uniffi_bindgen/src/bindings/python/templates/StringHelper.py +++ b/uniffi_bindgen/src/bindings/python/templates/StringHelper.py @@ -1,4 +1,4 @@ - class _UniffiConverterString: +class _UniffiConverterString: @staticmethod def check_lower(value): if not isinstance(value, str): diff --git a/uniffi_bindgen/src/interface/callbacks.rs b/uniffi_bindgen/src/interface/callbacks.rs index 3300f8a94f..345673f84d 100644 --- a/uniffi_bindgen/src/interface/callbacks.rs +++ b/uniffi_bindgen/src/interface/callbacks.rs @@ -123,10 +123,6 @@ impl Renameable for CallbackInterface { fn rename(&mut self, name: String) { self.name = name; } - - fn rename_nested(&mut self, _new_name: String) { - // no nested variables - } } impl AsType for CallbackInterface { diff --git a/uniffi_bindgen/src/interface/conventions.rs b/uniffi_bindgen/src/interface/conventions.rs index 56ab3e8c1f..b43122258f 100644 --- a/uniffi_bindgen/src/interface/conventions.rs +++ b/uniffi_bindgen/src/interface/conventions.rs @@ -2,20 +2,61 @@ use crate::{CodeOracle, ComponentInterface, Renameable}; impl ComponentInterface { pub fn apply_naming_conventions(&mut self, oracle: O) { + let errors = self.errors.clone(); + for enum_item in self.enums.values_mut() { - enum_item.rename(oracle.class_name(enum_item.name())); + if errors.contains(enum_item.name()) { + enum_item.rename(oracle.class_name(enum_item.name())); + + for variant in &mut enum_item.variants { + variant.rename(oracle.class_name(variant.name())); + + for field in &mut variant.fields { + field.rename(oracle.var_name(field.name())); + } + } + } else { + enum_item.rename(oracle.enum_variant_name(enum_item.name())); + + for variant in &mut enum_item.variants { + variant.rename(oracle.enum_variant_name(variant.name())); + variant.set_is_name(oracle.var_name(variant.name())); + + for field in &mut variant.fields { + field.rename(oracle.var_name(field.name())); + } + } + } + + } for record_item in self.records.values_mut() { record_item.rename(oracle.class_name(record_item.name())); + + for field in &mut record_item.fields { + field.rename(oracle.var_name(field.name())); + } } for function_item in self.functions.iter_mut() { function_item.rename(oracle.fn_name(function_item.name())); + + for arg in &mut function_item.arguments { + arg.rename(oracle.var_name(arg.name())); + } } for object_item in self.objects.iter_mut() { object_item.rename(oracle.class_name(object_item.name())); + + for meth in &mut object_item.methods { + meth.rename(oracle.fn_name(meth.name())); + } + + for cons in &mut object_item.constructors { + cons.rename(oracle.fn_name(cons.name())); + } } for callback_interface in self.callback_interfaces.iter_mut() { diff --git a/uniffi_bindgen/src/interface/enum_.rs b/uniffi_bindgen/src/interface/enum_.rs index a6520d1189..760c9d0e5f 100644 --- a/uniffi_bindgen/src/interface/enum_.rs +++ b/uniffi_bindgen/src/interface/enum_.rs @@ -259,12 +259,6 @@ impl Renameable for Enum { fn rename(&mut self, name: String) { self.name = name; } - - fn rename_nested(&mut self, new_name: String) { - for variant in &mut self.variants { - variant.name = new_name.clone(); - } - } } impl TryFrom for Enum { @@ -302,6 +296,7 @@ impl AsType for Enum { #[derive(Debug, Clone, Default, PartialEq, Eq, Checksum)] pub struct Variant { pub(super) name: String, + pub(super) is_name: String, pub(super) discr: Option, pub(super) fields: Vec, #[checksum_ignore] @@ -313,6 +308,8 @@ impl Variant { &self.name } + pub fn is_name(&self) -> &str { &self.is_name } + pub fn fields(&self) -> &[Field] { &self.fields } @@ -332,6 +329,20 @@ impl Variant { pub fn iter_types(&self) -> TypeIterator<'_> { Box::new(self.fields.iter().flat_map(Field::iter_types)) } + + pub fn set_is_name(&mut self, name: String) { + self.is_name = name; + } +} + +impl Renameable for Variant { + fn name(&self) -> &str { + &self.name + } + + fn rename(&mut self, new_name: String) { + self.name = new_name; + } } impl TryFrom for Variant { @@ -339,7 +350,8 @@ impl TryFrom for Variant { fn try_from(meta: uniffi_meta::VariantMetadata) -> Result { Ok(Self { - name: meta.name, + name: meta.name.clone(), + is_name: meta.name, discr: meta.discr, fields: meta .fields diff --git a/uniffi_bindgen/src/interface/function.rs b/uniffi_bindgen/src/interface/function.rs index 3093787f73..b2810b1ade 100644 --- a/uniffi_bindgen/src/interface/function.rs +++ b/uniffi_bindgen/src/interface/function.rs @@ -146,10 +146,6 @@ impl Renameable for Function { fn rename(&mut self, name: String) { self.name = name; } - - fn rename_nested(&mut self, _new_name: String) { - // Functions do not contain other nested elements - } } impl From for Argument { @@ -227,6 +223,16 @@ impl Argument { } } +impl Renameable for Argument { + fn name(&self) -> &str { + &self.name + } + + fn rename(&mut self, new_name: String) { + self.name = new_name; + } +} + impl AsType for Argument { fn as_type(&self) -> Type { self.type_.clone() diff --git a/uniffi_bindgen/src/interface/object.rs b/uniffi_bindgen/src/interface/object.rs index 78e65c5e06..c2f91e5291 100644 --- a/uniffi_bindgen/src/interface/object.rs +++ b/uniffi_bindgen/src/interface/object.rs @@ -300,12 +300,6 @@ impl Renameable for Object { fn rename(&mut self, new_name: String) { self.name = new_name; } - - fn rename_nested(&mut self, new_name: String) { - for method in &mut self.methods { - method.name = new_name.clone(); - } - } } impl AsType for Object { @@ -449,6 +443,16 @@ impl Constructor { } } +impl Renameable for Constructor { + fn name(&self) -> &str { + &self.name + } + + fn rename(&mut self, new_name: String) { + self.name = new_name; + } +} + impl From for Constructor { fn from(meta: uniffi_meta::ConstructorMetadata) -> Self { let ffi_name = meta.ffi_symbol_name(); @@ -600,6 +604,16 @@ impl Method { } } +impl Renameable for Method { + fn name(&self) -> &str { + &self.name + } + + fn rename(&mut self, new_name: String) { + self.name = new_name; + } +} + impl From for Method { fn from(meta: uniffi_meta::MethodMetadata) -> Self { let ffi_name = meta.ffi_symbol_name(); diff --git a/uniffi_bindgen/src/interface/record.rs b/uniffi_bindgen/src/interface/record.rs index fc7ec78f11..794d1e49d7 100644 --- a/uniffi_bindgen/src/interface/record.rs +++ b/uniffi_bindgen/src/interface/record.rs @@ -94,11 +94,6 @@ impl Renameable for Record { fn rename(&mut self, name: String) { self.name = name; } - fn rename_nested(&mut self, new_name: String) { - for field in &mut self.fields { - field.name = new_name.clone(); - } - } } impl AsType for Record { @@ -155,6 +150,15 @@ impl Field { } } +impl Renameable for Field { + fn name(&self) -> &str { + &self.name + } + fn rename(&mut self, name: String) { + self.name = name; + } +} + impl AsType for Field { fn as_type(&self) -> Type { self.type_.clone() diff --git a/uniffi_bindgen/src/lib.rs b/uniffi_bindgen/src/lib.rs index b024b8b029..dca247bbbe 100644 --- a/uniffi_bindgen/src/lib.rs +++ b/uniffi_bindgen/src/lib.rs @@ -202,8 +202,6 @@ pub trait Renameable { fn name(&self) -> &str; fn rename(&mut self, new_name: String); - - fn rename_nested(&mut self, new_name: String); } /// Everything needed to generate a ComponentInterface. From 64673707db5ed7ef7a712df563f2d94801d9530a Mon Sep 17 00:00:00 2001 From: Bastian Gruber Date: Mon, 22 Jul 2024 11:08:27 -0300 Subject: [PATCH 29/62] fix: fmt and clippy --- uniffi_bindgen/src/bindings/python/gen_python/mod.rs | 2 +- uniffi_bindgen/src/interface/conventions.rs | 2 -- uniffi_bindgen/src/interface/enum_.rs | 4 +++- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/uniffi_bindgen/src/bindings/python/gen_python/mod.rs b/uniffi_bindgen/src/bindings/python/gen_python/mod.rs index 52d21be6fc..45740b4434 100644 --- a/uniffi_bindgen/src/bindings/python/gen_python/mod.rs +++ b/uniffi_bindgen/src/bindings/python/gen_python/mod.rs @@ -300,7 +300,7 @@ pub struct PythonWrapper<'a> { } impl<'a> PythonWrapper<'a> { pub fn new(config: Config, ci: &'a mut ComponentInterface) -> Self { - ci.apply_naming_conventions(PythonCodeOracle::default()); + ci.apply_naming_conventions(PythonCodeOracle); let type_renderer = TypeRenderer::new(&config, ci); let type_helper_code = type_renderer.render().unwrap(); diff --git a/uniffi_bindgen/src/interface/conventions.rs b/uniffi_bindgen/src/interface/conventions.rs index b43122258f..9965b814ec 100644 --- a/uniffi_bindgen/src/interface/conventions.rs +++ b/uniffi_bindgen/src/interface/conventions.rs @@ -27,8 +27,6 @@ impl ComponentInterface { } } } - - } for record_item in self.records.values_mut() { diff --git a/uniffi_bindgen/src/interface/enum_.rs b/uniffi_bindgen/src/interface/enum_.rs index 760c9d0e5f..0e1ddc75b4 100644 --- a/uniffi_bindgen/src/interface/enum_.rs +++ b/uniffi_bindgen/src/interface/enum_.rs @@ -308,7 +308,9 @@ impl Variant { &self.name } - pub fn is_name(&self) -> &str { &self.is_name } + pub fn is_name(&self) -> &str { + &self.is_name + } pub fn fields(&self) -> &[Field] { &self.fields From 1edaf372f57399bf5ddbef1a53adc6c970936bc9 Mon Sep 17 00:00:00 2001 From: Bastian Gruber Date: Mon, 22 Jul 2024 11:38:55 -0300 Subject: [PATCH 30/62] fix: clippy --- uniffi_bindgen/src/interface/enum_.rs | 1 + uniffi_bindgen/src/interface/mod.rs | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/uniffi_bindgen/src/interface/enum_.rs b/uniffi_bindgen/src/interface/enum_.rs index 0e1ddc75b4..69a6114d03 100644 --- a/uniffi_bindgen/src/interface/enum_.rs +++ b/uniffi_bindgen/src/interface/enum_.rs @@ -663,6 +663,7 @@ mod test { fn variant(val: Option) -> Variant { Variant { name: "v".to_string(), + is_name: "v".to_string(), discr: val.map(Literal::new_uint), fields: vec![], docstring: None, diff --git a/uniffi_bindgen/src/interface/mod.rs b/uniffi_bindgen/src/interface/mod.rs index 1e90b8a097..5fb2275eba 100644 --- a/uniffi_bindgen/src/interface/mod.rs +++ b/uniffi_bindgen/src/interface/mod.rs @@ -1172,12 +1172,14 @@ existing definition: Enum { variants: [ Variant { name: \"one\", + is_name: \"one\", discr: None, fields: [], docstring: None, }, Variant { name: \"two\", + is_name: \"two\", discr: None, fields: [], docstring: None, @@ -1194,12 +1196,14 @@ new definition: Enum { variants: [ Variant { name: \"three\", + is_name: \"trhee\", discr: None, fields: [], docstring: None, }, Variant { name: \"four\", + is_name: \"four\", discr: None, fields: [], docstring: None, From 9bb443bfa4f247c2ae44a7f99766fade69771a52 Mon Sep 17 00:00:00 2001 From: Bastian Gruber Date: Mon, 22 Jul 2024 14:14:55 -0300 Subject: [PATCH 31/62] fix: test import with filter --- uniffi_bindgen/src/bindings/python/gen_python/mod.rs | 5 +++++ .../src/bindings/python/templates/ExternalTemplate.py | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/uniffi_bindgen/src/bindings/python/gen_python/mod.rs b/uniffi_bindgen/src/bindings/python/gen_python/mod.rs index 45740b4434..50609180b4 100644 --- a/uniffi_bindgen/src/bindings/python/gen_python/mod.rs +++ b/uniffi_bindgen/src/bindings/python/gen_python/mod.rs @@ -504,6 +504,11 @@ pub mod filters { Ok(as_ct.as_codetype().type_label()) } + /// Get the idiomatic Python rendering of a class name (for enums, records, errors, etc). + pub fn class_name(nm: &str) -> Result { + Ok(PythonCodeOracle.class_name(nm)) + } + pub(super) fn ffi_converter_name(as_ct: &impl AsCodeType) -> Result { Ok(String::from("_Uniffi") + &as_ct.as_codetype().ffi_converter_name()[3..]) } diff --git a/uniffi_bindgen/src/bindings/python/templates/ExternalTemplate.py b/uniffi_bindgen/src/bindings/python/templates/ExternalTemplate.py index 26e4639e19..6c0cee85ef 100644 --- a/uniffi_bindgen/src/bindings/python/templates/ExternalTemplate.py +++ b/uniffi_bindgen/src/bindings/python/templates/ExternalTemplate.py @@ -3,7 +3,7 @@ # External type {{name}} is in namespace "{{namespace}}", crate {{module_path}} {%- let ffi_converter_name = "_UniffiConverterType{}"|format(name) %} {{ self.add_import_of(module, ffi_converter_name) }} -{{ self.add_import_of(module, name) }} {#- import the type alias itself -#} +{{ self.add_import_of(module, name|class_name) }} {#- import the type alias itself -#} {%- let rustbuffer_local_name = "_UniffiRustBuffer{}"|format(name) %} {{ self.add_import_of_as(module, "_UniffiRustBuffer", rustbuffer_local_name) }} From 8b18a0dcb0db9a5d681debe9f7c66ab94c11303f Mon Sep 17 00:00:00 2001 From: Bastian Gruber Date: Mon, 22 Jul 2024 14:28:35 -0300 Subject: [PATCH 32/62] fix: add renamings --- uniffi_bindgen/src/interface/conventions.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/uniffi_bindgen/src/interface/conventions.rs b/uniffi_bindgen/src/interface/conventions.rs index 9965b814ec..835388f7cc 100644 --- a/uniffi_bindgen/src/interface/conventions.rs +++ b/uniffi_bindgen/src/interface/conventions.rs @@ -45,6 +45,10 @@ impl ComponentInterface { } } + for f in self.function_definitions().iter_mut() { + f.rename(oracle.fn_name(f.name())); + } + for object_item in self.objects.iter_mut() { object_item.rename(oracle.class_name(object_item.name())); @@ -57,7 +61,7 @@ impl ComponentInterface { } } - for callback_interface in self.callback_interfaces.iter_mut() { + for callback_interface in self.callback_interface_definitions().iter_mut() { callback_interface.rename(oracle.class_name(callback_interface.name())); } } From 1115ae0674663a594d58756ce5b8c84424af5fae Mon Sep 17 00:00:00 2001 From: Bastian Gruber Date: Mon, 22 Jul 2024 14:33:07 -0300 Subject: [PATCH 33/62] fix: fix --- uniffi_bindgen/src/interface/conventions.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/uniffi_bindgen/src/interface/conventions.rs b/uniffi_bindgen/src/interface/conventions.rs index 835388f7cc..9965b814ec 100644 --- a/uniffi_bindgen/src/interface/conventions.rs +++ b/uniffi_bindgen/src/interface/conventions.rs @@ -45,10 +45,6 @@ impl ComponentInterface { } } - for f in self.function_definitions().iter_mut() { - f.rename(oracle.fn_name(f.name())); - } - for object_item in self.objects.iter_mut() { object_item.rename(oracle.class_name(object_item.name())); @@ -61,7 +57,7 @@ impl ComponentInterface { } } - for callback_interface in self.callback_interface_definitions().iter_mut() { + for callback_interface in self.callback_interfaces.iter_mut() { callback_interface.rename(oracle.class_name(callback_interface.name())); } } From e08129a407f8e83afa9e28b58deaeb541e74c226 Mon Sep 17 00:00:00 2001 From: Bastian Gruber Date: Tue, 23 Jul 2024 09:42:01 -0300 Subject: [PATCH 34/62] wip --- .../src/bindings/python/gen_python/mod.rs | 16 +++++++++ .../src/bindings/python/templates/macros.py | 18 +++++----- .../src/bindings/python/templates/wrapper.py | 2 +- uniffi_bindgen/src/interface/conventions.rs | 35 +++++++++++++++---- uniffi_bindgen/src/interface/ffi.rs | 8 +++++ uniffi_bindgen/src/interface/object.rs | 4 +-- 6 files changed, 65 insertions(+), 18 deletions(-) diff --git a/uniffi_bindgen/src/bindings/python/gen_python/mod.rs b/uniffi_bindgen/src/bindings/python/gen_python/mod.rs index 50609180b4..0984645fdd 100644 --- a/uniffi_bindgen/src/bindings/python/gen_python/mod.rs +++ b/uniffi_bindgen/src/bindings/python/gen_python/mod.rs @@ -504,11 +504,27 @@ pub mod filters { Ok(as_ct.as_codetype().type_label()) } + /// Get the idiomatic Python rendering of a class name (for enums, records, errors, etc). /// Get the idiomatic Python rendering of a class name (for enums, records, errors, etc). pub fn class_name(nm: &str) -> Result { Ok(PythonCodeOracle.class_name(nm)) } + /// Get the idiomatic Python rendering of a function name. + pub fn fn_name(nm: &str) -> Result { + Ok(PythonCodeOracle.fn_name(nm)) + } + + /// Get the idiomatic Python rendering of a variable name. + pub fn var_name(nm: &str) -> Result { + Ok(PythonCodeOracle.var_name(nm)) + } + + /// Get the idiomatic Python rendering of an individual enum variant. + pub fn enum_variant_py(nm: &str) -> Result { + Ok(PythonCodeOracle.enum_variant_name(nm)) + } + pub(super) fn ffi_converter_name(as_ct: &impl AsCodeType) -> Result { Ok(String::from("_Uniffi") + &as_ct.as_codetype().ffi_converter_name()[3..]) } diff --git a/uniffi_bindgen/src/bindings/python/templates/macros.py b/uniffi_bindgen/src/bindings/python/templates/macros.py index 8d84ccc251..7fdf66e02b 100644 --- a/uniffi_bindgen/src/bindings/python/templates/macros.py +++ b/uniffi_bindgen/src/bindings/python/templates/macros.py @@ -34,7 +34,7 @@ {%- macro arg_list_lowered(func) %} {%- for arg in func.arguments() %} - {{ arg|lower_fn }}({{ arg.name() }}) + {{ arg|lower_fn }}({{ arg.name()|var_name }}) {%- if !loop.last %},{% endif %} {%- endfor %} {%- endmacro -%} @@ -54,12 +54,12 @@ {#- // Arglist as used in Python declarations of methods, functions and constructors. -// Note the and type_name filters. +// Note the var_name and type_name filters. -#} {% macro arg_list_decl(func) %} {%- for arg in func.arguments() -%} - {{ arg.name() }} + {{ arg.name()|var_name }} {%- match arg.default_value() %} {%- when Some with(literal) %}: "typing.Union[object, {{ arg|type_name -}}]" = _DEFAULT {%- else %}: "{{ arg|type_name -}}" @@ -88,10 +88,10 @@ {%- match arg.default_value() %} {%- when None %} {%- when Some with(literal) %} - if {{ arg.name() }} is _DEFAULT: - {{ arg.name() }} = {{ literal|literal_py(arg.as_type().borrow()) }} + if {{ arg.name()|var_name }} is _DEFAULT: + {{ arg.name()|var_name }} = {{ literal|literal_py(arg.as_type().borrow()) }} {%- endmatch %} - {{ arg|check_lower_fn }}({{ arg.name() }}) + {{ arg|check_lower_fn }}({{ arg.name()|var_name }}) {% endfor -%} {%- endmacro -%} @@ -104,10 +104,10 @@ {%- match arg.default_value() %} {%- when None %} {%- when Some with(literal) %} - if {{ arg.name() }} is _DEFAULT: - {{ arg.name() }} = {{ literal|literal_py(arg.as_type().borrow()) }} + if {{ arg.name()|var_name }} is _DEFAULT: + {{ arg.name()|var_name }} = {{ literal|literal_py(arg.as_type().borrow()) }} {%- endmatch %} - {{ arg|check_lower_fn }}({{ arg.name() }}) + {{ arg|check_lower_fn }}({{ arg.name()|var_name }}) {% endfor -%} {%- endmacro -%} diff --git a/uniffi_bindgen/src/bindings/python/templates/wrapper.py b/uniffi_bindgen/src/bindings/python/templates/wrapper.py index c81052e4cc..aca217ed8c 100644 --- a/uniffi_bindgen/src/bindings/python/templates/wrapper.py +++ b/uniffi_bindgen/src/bindings/python/templates/wrapper.py @@ -67,7 +67,7 @@ "{{ record|type_name }}", {%- endfor %} {%- for func in ci.function_definitions() %} - "{{ func.name() }}", + "{{ func.name() }}", {%- endfor %} {%- for obj in ci.object_definitions() %} "{{ obj|type_name }}", diff --git a/uniffi_bindgen/src/interface/conventions.rs b/uniffi_bindgen/src/interface/conventions.rs index 9965b814ec..d8e2cb2dec 100644 --- a/uniffi_bindgen/src/interface/conventions.rs +++ b/uniffi_bindgen/src/interface/conventions.rs @@ -1,4 +1,5 @@ use crate::{CodeOracle, ComponentInterface, Renameable}; +use crate::interface::FfiDefinition; impl ComponentInterface { pub fn apply_naming_conventions(&mut self, oracle: O) { @@ -46,19 +47,41 @@ impl ComponentInterface { } for object_item in self.objects.iter_mut() { - object_item.rename(oracle.class_name(object_item.name())); - for meth in &mut object_item.methods { meth.rename(oracle.fn_name(meth.name())); } + + for (ffi_callback, m) in object_item.vtable_methods().iter_mut() { + m.rename(oracle.fn_name(m.name())); + + for arg in &mut ffi_callback.arguments { + arg.rename(oracle.var_name(arg.name())); + } + } + for cons in &mut object_item.constructors { - cons.rename(oracle.fn_name(cons.name())); + if !cons.is_primary_constructor() { + cons.rename(oracle.fn_name(cons.name())); + } + } } + // + // for callback_interface in self.callback_interfaces.iter_mut() { + // callback_interface.rename(oracle.class_name(callback_interface.name())); + // } - for callback_interface in self.callback_interfaces.iter_mut() { - callback_interface.rename(oracle.class_name(callback_interface.name())); - } + // for field in self.ffi_definitions() { + // match field { + // FfiDefinition::Function(_) => {} + // FfiDefinition::CallbackFunction(_) => {} + // FfiDefinition::Struct(mut ffi_struct) => { + // for f in &mut ffi_struct.fields { + // f.rename(oracle.var_name(f.name())); + // } + // } + // } + // } } } diff --git a/uniffi_bindgen/src/interface/ffi.rs b/uniffi_bindgen/src/interface/ffi.rs index a1dc29713a..9a2fc843bb 100644 --- a/uniffi_bindgen/src/interface/ffi.rs +++ b/uniffi_bindgen/src/interface/ffi.rs @@ -296,6 +296,10 @@ impl FfiArgument { pub fn type_(&self) -> FfiType { self.type_.clone() } + + pub fn rename(&mut self, name: String) { + self.name = name; + } } /// Represents an "extern C"-style callback function @@ -369,6 +373,10 @@ impl FfiField { pub fn type_(&self) -> FfiType { self.type_.clone() } + + pub fn rename(&mut self, name: String) { + self.name = name; + } } impl From for FfiDefinition { diff --git a/uniffi_bindgen/src/interface/object.rs b/uniffi_bindgen/src/interface/object.rs index c2f91e5291..c8a7a293f4 100644 --- a/uniffi_bindgen/src/interface/object.rs +++ b/uniffi_bindgen/src/interface/object.rs @@ -267,14 +267,14 @@ impl Object { } /// Vec of (ffi_callback_name, method) pairs - pub fn vtable_methods(&self) -> Vec<(FfiCallbackFunction, &Method)> { + pub fn vtable_methods(&self) -> Vec<(FfiCallbackFunction, Method)> { self.methods .iter() .enumerate() .map(|(i, method)| { ( callbacks::method_ffi_callback(&self.name, method, i), - method, + method.clone(), ) }) .collect() From 93e9bf5c95763d85fc6389030e9a1dbdbc005891 Mon Sep 17 00:00:00 2001 From: Bastian Gruber Date: Tue, 23 Jul 2024 13:58:31 -0300 Subject: [PATCH 35/62] wip --- .../src/bindings/python/gen_python/mod.rs | 2 +- .../python/templates/CallbackInterfaceImpl.py | 10 +- .../bindings/python/templates/EnumTemplate.py | 24 ++-- .../python/templates/ErrorTemplate.py | 10 +- .../templates/NamespaceLibraryTemplate.py | 4 +- .../python/templates/ObjectTemplate.py | 6 +- .../src/bindings/python/templates/Protocol.py | 2 +- .../python/templates/RecordTemplate.py | 16 +-- .../src/bindings/python/templates/macros.py | 18 +-- .../src/bindings/python/templates/wrapper.py | 4 +- uniffi_bindgen/src/interface/callbacks.rs | 23 ++-- uniffi_bindgen/src/interface/conventions.rs | 120 +++++++++++------- uniffi_bindgen/src/interface/enum_.rs | 37 ++---- uniffi_bindgen/src/interface/ffi.rs | 8 ++ uniffi_bindgen/src/interface/function.rs | 30 ++--- uniffi_bindgen/src/interface/mod.rs | 2 +- uniffi_bindgen/src/interface/universe.rs | 4 +- uniffi_meta/src/types.rs | 48 +++++++ 18 files changed, 211 insertions(+), 157 deletions(-) diff --git a/uniffi_bindgen/src/bindings/python/gen_python/mod.rs b/uniffi_bindgen/src/bindings/python/gen_python/mod.rs index 0984645fdd..835882239b 100644 --- a/uniffi_bindgen/src/bindings/python/gen_python/mod.rs +++ b/uniffi_bindgen/src/bindings/python/gen_python/mod.rs @@ -301,7 +301,7 @@ pub struct PythonWrapper<'a> { impl<'a> PythonWrapper<'a> { pub fn new(config: Config, ci: &'a mut ComponentInterface) -> Self { ci.apply_naming_conventions(PythonCodeOracle); - + dbg!("{:#?}", ci.clone()); let type_renderer = TypeRenderer::new(&config, ci); let type_helper_code = type_renderer.render().unwrap(); let type_imports = type_renderer.imports.into_inner(); diff --git a/uniffi_bindgen/src/bindings/python/templates/CallbackInterfaceImpl.py b/uniffi_bindgen/src/bindings/python/templates/CallbackInterfaceImpl.py index 1009505d63..d3d67a1a97 100644 --- a/uniffi_bindgen/src/bindings/python/templates/CallbackInterfaceImpl.py +++ b/uniffi_bindgen/src/bindings/python/templates/CallbackInterfaceImpl.py @@ -7,9 +7,9 @@ class {{ trait_impl }}: {%- for (ffi_callback, meth) in vtable_methods.iter() %} @{{ ffi_callback.name()|ffi_callback_name }} - def {{ meth.name() }}( + def {{ meth.name()|fn_name }}( {%- for arg in ffi_callback.arguments() %} - {{ arg.name() }}, + {{ arg.name()|var_name }}, {%- endfor -%} {%- if ffi_callback.has_rust_call_status_arg() %} uniffi_call_status_ptr, @@ -17,8 +17,8 @@ def {{ meth.name() }}( ): uniffi_obj = {{ ffi_converter_name }}._handle_map.get(uniffi_handle) def make_call(): - args = ({% for arg in meth.arguments() %}{{ arg|lift_fn }}({{ arg.name() }}), {% endfor %}) - method = uniffi_obj.{{ meth.name() }} + args = ({% for arg in meth.arguments() %}{{ arg|lift_fn }}({{ arg.name()|var_name }}), {% endfor %}) + method = uniffi_obj.{{ meth.name()|fn_name }} return method(*args) {% if !meth.is_async() %} @@ -89,7 +89,7 @@ def _uniffi_free(uniffi_handle): # Generate the FFI VTable. This has a field for each callback interface method. _uniffi_vtable = {{ vtable|ffi_type_name }}( {%- for (_, meth) in vtable_methods.iter() %} - {{ meth.name() }}, + {{ meth.name()|fn_name }}, {%- endfor %} _uniffi_free ) diff --git a/uniffi_bindgen/src/bindings/python/templates/EnumTemplate.py b/uniffi_bindgen/src/bindings/python/templates/EnumTemplate.py index a94f910044..49867330ff 100644 --- a/uniffi_bindgen/src/bindings/python/templates/EnumTemplate.py +++ b/uniffi_bindgen/src/bindings/python/templates/EnumTemplate.py @@ -61,7 +61,7 @@ def __init__(self,{% for field in variant.fields() %}{{ field.name() }}: "{{- fi {%- endif %} def __str__(self): - return "{{ type_name }}.{{ variant.name() }}({% for field in variant.fields() %}{{ field.name() }}={}{% if loop.last %}{% else %}, {% endif %}{% endfor %})".format({% for field in variant.fields() %}self.{{ field.name() }}{% if loop.last %}{% else %}, {% endif %}{% endfor %}) + return "{{ type_name }}.{{ variant.name() }}({% for field in variant.fields() %}{{ field.name() }}={}{% if loop.last %}{% else %}, {% endif %}{% endfor %})".format({% for field in variant.fields() %}self.{{ field.name() }}{% if loop.last %}{% else %}, {% endif %}{% endfor %}) def __eq__(self, other): if not other.is_{{ variant.is_name() }}(): @@ -77,15 +77,15 @@ def __eq__(self, other): # For each variant, we have an `is_NAME` method for easily checking # whether an instance is that variant. {% for variant in e.variants() -%} - def is_{{ variant.is_name() }}(self) -> bool: - return isinstance(self, {{ type_name }}.{{ variant.name() }}) + def is_{{ variant.name()|var_name }}(self) -> bool: + return isinstance(self, {{ type_name }}.{{ variant.name()|enum_variant_py }}) {% endfor %} # Now, a little trick - we make each nested variant class be a subclass of the main # enum class, so that method calls and instance checks etc will work intuitively. # We might be able to do this a little more neatly with a metaclass, but this'll do. {% for variant in e.variants() -%} -{{ type_name }}.{{ variant.name() }} = type("{{ type_name }}.{{ variant.name() }}", ({{ type_name }}.{{variant.name()}}, {{ type_name }},), {}) # type: ignore +{{ type_name }}.{{ variant.name()|enum_variant_py }} = type("{{ type_name }}.{{ variant.name()|enum_variant_py }}", ({{ type_name }}.{{variant.name()|enum_variant_py}}, {{ type_name }},), {}) # type: ignore {% endfor %} {% endif %} @@ -98,9 +98,9 @@ def read(buf): {%- for variant in e.variants() %} if variant == {{ loop.index }}: {%- if e.is_flat() %} - return {{ type_name }}.{{variant.name()}} + return {{ type_name }}.{{variant.name()|enum_variant_py}} {%- else %} - return {{ type_name }}.{{variant.name()}}( + return {{ type_name }}.{{variant.name()|enum_variant_py}}( {%- for field in variant.fields() %} {{ field|read_fn }}(buf), {%- endfor %} @@ -116,15 +116,15 @@ def check_lower(value): {%- else %} {%- for variant in e.variants() %} {%- if e.is_flat() %} - if value == {{ type_name }}.{{ variant.name() }}: + if value == {{ type_name }}.{{ variant.name()|enum_variant_py }}: {%- else %} - if value.is_{{ variant.is_name() }}(): + if value.is_{{ variant.name()|var_name }}(): {%- endif %} {%- for field in variant.fields() %} {%- if variant.has_nameless_fields() %} {{ field|check_lower_fn }}(value._values[{{ loop.index0 }}]) {%- else %} - {{ field|check_lower_fn }}(value.{{ field.name() }}) + {{ field|check_lower_fn }}(value.{{ field.name()|var_name }}) {%- endif %} {%- endfor %} return @@ -136,16 +136,16 @@ def check_lower(value): def write(value, buf): {%- for variant in e.variants() %} {%- if e.is_flat() %} - if value == {{ type_name }}.{{ variant.name() }}: + if value == {{ type_name }}.{{ variant.name()|enum_variant_py }}: buf.write_i32({{ loop.index }}) {%- else %} - if value.is_{{ variant.is_name() }}(): + if value.is_{{ variant.name()|var_name }}(): buf.write_i32({{ loop.index }}) {%- for field in variant.fields() %} {%- if variant.has_nameless_fields() %} {{ field|write_fn }}(value._values[{{ loop.index0 }}], buf) {%- else %} - {{ field|write_fn }}(value.{{ field.name() }}, buf) + {{ field|write_fn }}(value.{{ field.name()|var_name }}, buf) {%- endif %} {%- endfor %} {%- endif %} diff --git a/uniffi_bindgen/src/bindings/python/templates/ErrorTemplate.py b/uniffi_bindgen/src/bindings/python/templates/ErrorTemplate.py index 131e7efa01..9d3b30831d 100644 --- a/uniffi_bindgen/src/bindings/python/templates/ErrorTemplate.py +++ b/uniffi_bindgen/src/bindings/python/templates/ErrorTemplate.py @@ -39,15 +39,15 @@ def __getitem__(self, index): return self._values[index] {%- else %} - def __init__(self{% for field in variant.fields() %}, {{ field.name() }}{% endfor %}): + def __init__(self{% for field in variant.fields() %}, {{ field.name() }}{% endfor %}): {%- if variant.has_fields() %} super().__init__(", ".join([ {%- for field in variant.fields() %} - "{{ field.name() }}={!r}".format({{ field.name() }}), + "{{ field.name() }}={!r}".format({{ field.name() }}), {%- endfor %} ])) {%- for field in variant.fields() %} - self.{{ field.name() }} = {{ field.name() }} + self.{{ field.name() }} = {{ field.name() }} {%- endfor %} {%- else %} pass @@ -93,7 +93,7 @@ def check_lower(value): {%- if variant.has_nameless_fields() %} {{ field|check_lower_fn }}(value._values[{{ loop.index0 }}]) {%- else %} - {{ field|check_lower_fn }}(value.{{ field.name() }}) + {{ field|check_lower_fn }}(value.{{ field.name() }}) {%- endif %} {%- endfor %} return @@ -109,7 +109,7 @@ def write(value, buf): {%- if variant.has_nameless_fields() %} {{ field|write_fn }}(value._values[{{ loop.index0 }}], buf) {%- else %} - {{ field|write_fn }}(value.{{ field.name() }}, buf) + {{ field|write_fn }}(value.{{ field.name() }}, buf) {%- endif %} {%- endfor %} {%- endfor %} diff --git a/uniffi_bindgen/src/bindings/python/templates/NamespaceLibraryTemplate.py b/uniffi_bindgen/src/bindings/python/templates/NamespaceLibraryTemplate.py index 174ea2d353..f35c3a766d 100644 --- a/uniffi_bindgen/src/bindings/python/templates/NamespaceLibraryTemplate.py +++ b/uniffi_bindgen/src/bindings/python/templates/NamespaceLibraryTemplate.py @@ -76,7 +76,7 @@ def _uniffi_check_api_checksums(lib): class {{ ffi_struct.name()|ffi_struct_name }}(ctypes.Structure): _fields_ = [ {%- for field in ffi_struct.fields() %} - ("{{ field.name() }}", {{ field.type_().borrow()|ffi_type_name }}), + ("{{ field.name()|var_name }}", {{ field.type_().borrow()|ffi_type_name }}), {%- endfor %} ] {%- when FfiDefinition::Function(func) %} @@ -89,4 +89,4 @@ class {{ ffi_struct.name()|ffi_struct_name }}(ctypes.Structure): {# Ensure to call the contract verification only after we defined all functions. -#} _uniffi_check_contract_api_version(_UniffiLib) -_uniffi_check_api_checksums(_UniffiLib) +# _uniffi_check_api_checksums(_UniffiLib) diff --git a/uniffi_bindgen/src/bindings/python/templates/ObjectTemplate.py b/uniffi_bindgen/src/bindings/python/templates/ObjectTemplate.py index 5772ad24db..33a914195e 100644 --- a/uniffi_bindgen/src/bindings/python/templates/ObjectTemplate.py +++ b/uniffi_bindgen/src/bindings/python/templates/ObjectTemplate.py @@ -52,7 +52,7 @@ def _make_instance_(cls, pointer): @classmethod {%- if cons.is_async() %} - async def {{ cons.name() }}(cls, {% call py::arg_list_decl(cons) %}): + async def {{ cons.name()|fn_name }}(cls, {% call py::arg_list_decl(cons) %}): {%- call py::docstring(cons, 8) %} {%- call py::setup_args_extra_indent(cons) %} @@ -65,7 +65,7 @@ async def {{ cons.name() }}(cls, {% call py::arg_list_decl(cons) %}): {% call py::error_ffi_converter(cons) %} ) {%- else %} - def {{ cons.name() }}(cls, {% call py::arg_list_decl(cons) %}): + def {{ cons.name()|fn_name }}(cls, {% call py::arg_list_decl(cons) %}): {%- call py::docstring(cons, 8) %} {%- call py::setup_args_extra_indent(cons) %} # Call the (fallible) function before creating any half-baked object instances. @@ -75,7 +75,7 @@ def {{ cons.name() }}(cls, {% call py::arg_list_decl(cons) %}): {% endfor %} {%- for meth in obj.methods() -%} - {%- call py::method_decl(meth.name(), meth) %} + {%- call py::method_decl(meth.name()|fn_name, meth) %} {%- endfor %} {%- for tm in obj.uniffi_traits() -%} {%- match tm %} diff --git a/uniffi_bindgen/src/bindings/python/templates/Protocol.py b/uniffi_bindgen/src/bindings/python/templates/Protocol.py index 8e5012ddd4..3b7e93596a 100644 --- a/uniffi_bindgen/src/bindings/python/templates/Protocol.py +++ b/uniffi_bindgen/src/bindings/python/templates/Protocol.py @@ -1,7 +1,7 @@ class {{ protocol_name }}(typing.Protocol): {%- call py::docstring_value(protocol_docstring, 4) %} {%- for meth in methods.iter() %} - def {{ meth.name() }}(self, {% call py::arg_list_decl(meth) %}): + def {{ meth.name()|fn_name }}(self, {% call py::arg_list_decl(meth) %}): {%- call py::docstring(meth, 8) %} raise NotImplementedError {%- else %} diff --git a/uniffi_bindgen/src/bindings/python/templates/RecordTemplate.py b/uniffi_bindgen/src/bindings/python/templates/RecordTemplate.py index d778b90ebd..cf0d0653d7 100644 --- a/uniffi_bindgen/src/bindings/python/templates/RecordTemplate.py +++ b/uniffi_bindgen/src/bindings/python/templates/RecordTemplate.py @@ -2,18 +2,18 @@ class {{ type_name }}: {%- call py::docstring(rec, 4) %} {%- for field in rec.fields() %} - {{ field.name() }}: "{{ field|type_name }}" + {{ field.name()|var_name }}: "{{ field|type_name }}" {%- call py::docstring(field, 4) %} {%- endfor %} {%- if rec.has_fields() %} def __init__(self, *, {% for field in rec.fields() %} - {{- field.name() }}: "{{- field|type_name }}" + {{- field.name()|var_name }}: "{{- field|type_name }}" {%- if field.default_value().is_some() %} = _DEFAULT{% endif %} {%- if !loop.last %}, {% endif %} {%- endfor %}): {%- for field in rec.fields() %} - {%- let field_name = field.name() %} + {%- let field_name = field.name()|var_name %} {%- match field.default_value() %} {%- when None %} self.{{ field_name }} = {{ field_name }} @@ -27,11 +27,11 @@ def __init__(self, *, {% for field in rec.fields() %} {%- endif %} def __str__(self): - return "{{ type_name }}({% for field in rec.fields() %}{{ field.name() }}={}{% if loop.last %}{% else %}, {% endif %}{% endfor %})".format({% for field in rec.fields() %}self.{{ field.name() }}{% if loop.last %}{% else %}, {% endif %}{% endfor %}) + return "{{ type_name }}({% for field in rec.fields() %}{{ field.name()|var_name }}={}{% if loop.last %}{% else %}, {% endif %}{% endfor %})".format({% for field in rec.fields() %}self.{{ field.name()|var_name }}{% if loop.last %}{% else %}, {% endif %}{% endfor %}) def __eq__(self, other): {%- for field in rec.fields() %} - if self.{{ field.name() }} != other.{{ field.name() }}: + if self.{{ field.name()|var_name }} != other.{{ field.name()|var_name }}: return False {%- endfor %} return True @@ -41,7 +41,7 @@ class {{ ffi_converter_name }}(_UniffiConverterRustBuffer): def read(buf): return {{ type_name }}( {%- for field in rec.fields() %} - {{ field.name() }}={{ field|read_fn }}(buf), + {{ field.name()|var_name }}={{ field|read_fn }}(buf), {%- endfor %} ) @@ -51,7 +51,7 @@ def check_lower(value): pass {%- else %} {%- for field in rec.fields() %} - {{ field|check_lower_fn }}(value.{{ field.name() }}) + {{ field|check_lower_fn }}(value.{{ field.name()|var_name }}) {%- endfor %} {%- endif %} @@ -59,7 +59,7 @@ def check_lower(value): def write(value, buf): {%- if rec.has_fields() %} {%- for field in rec.fields() %} - {{ field|write_fn }}(value.{{ field.name() }}, buf) + {{ field|write_fn }}(value.{{ field.name()|var_name }}, buf) {%- endfor %} {%- else %} pass diff --git a/uniffi_bindgen/src/bindings/python/templates/macros.py b/uniffi_bindgen/src/bindings/python/templates/macros.py index 7fdf66e02b..1a47226bd0 100644 --- a/uniffi_bindgen/src/bindings/python/templates/macros.py +++ b/uniffi_bindgen/src/bindings/python/templates/macros.py @@ -34,7 +34,7 @@ {%- macro arg_list_lowered(func) %} {%- for arg in func.arguments() %} - {{ arg|lower_fn }}({{ arg.name()|var_name }}) + {{ arg|lower_fn }}({{ arg.name() }}) {%- if !loop.last %},{% endif %} {%- endfor %} {%- endmacro -%} @@ -54,12 +54,12 @@ {#- // Arglist as used in Python declarations of methods, functions and constructors. -// Note the var_name and type_name filters. +// Note the type_name filters. -#} {% macro arg_list_decl(func) %} {%- for arg in func.arguments() -%} - {{ arg.name()|var_name }} + {{ arg.name() }} {%- match arg.default_value() %} {%- when Some with(literal) %}: "typing.Union[object, {{ arg|type_name -}}]" = _DEFAULT {%- else %}: "{{ arg|type_name -}}" @@ -88,10 +88,10 @@ {%- match arg.default_value() %} {%- when None %} {%- when Some with(literal) %} - if {{ arg.name()|var_name }} is _DEFAULT: - {{ arg.name()|var_name }} = {{ literal|literal_py(arg.as_type().borrow()) }} + if {{ arg.name() }} is _DEFAULT: + {{ arg.name() }} = {{ literal|literal_py(arg.as_type().borrow()) }} {%- endmatch %} - {{ arg|check_lower_fn }}({{ arg.name()|var_name }}) + {{ arg|check_lower_fn }}({{ arg.name() }}) {% endfor -%} {%- endmacro -%} @@ -104,10 +104,10 @@ {%- match arg.default_value() %} {%- when None %} {%- when Some with(literal) %} - if {{ arg.name()|var_name }} is _DEFAULT: - {{ arg.name()|var_name }} = {{ literal|literal_py(arg.as_type().borrow()) }} + if {{ arg.name() }} is _DEFAULT: + {{ arg.name() }} = {{ literal|literal_py(arg.as_type().borrow()) }} {%- endmatch %} - {{ arg|check_lower_fn }}({{ arg.name()|var_name }}) + {{ arg|check_lower_fn }}({{ arg.name() }}) {% endfor -%} {%- endmacro -%} diff --git a/uniffi_bindgen/src/bindings/python/templates/wrapper.py b/uniffi_bindgen/src/bindings/python/templates/wrapper.py index aca217ed8c..d88497c313 100644 --- a/uniffi_bindgen/src/bindings/python/templates/wrapper.py +++ b/uniffi_bindgen/src/bindings/python/templates/wrapper.py @@ -67,13 +67,13 @@ "{{ record|type_name }}", {%- endfor %} {%- for func in ci.function_definitions() %} - "{{ func.name() }}", + "{{ func.name() }}", {%- endfor %} {%- for obj in ci.object_definitions() %} "{{ obj|type_name }}", {%- endfor %} {%- for c in ci.callback_interface_definitions() %} - "{{ c.name() }}", + "{{ c.display() }}", {%- endfor %} ] diff --git a/uniffi_bindgen/src/interface/callbacks.rs b/uniffi_bindgen/src/interface/callbacks.rs index 345673f84d..92bb095bc8 100644 --- a/uniffi_bindgen/src/interface/callbacks.rs +++ b/uniffi_bindgen/src/interface/callbacks.rs @@ -35,7 +35,6 @@ use std::iter; -use crate::Renameable; use heck::ToUpperCamelCase; use uniffi_meta::Checksum; @@ -46,6 +45,7 @@ use super::{AsType, Type, TypeIterator}; #[derive(Debug, Clone, Checksum)] pub struct CallbackInterface { pub(super) name: String, + pub(super) display: String, pub(super) module_path: String, pub(super) methods: Vec, // We don't include the FFIFunc in the hash calculation, because: @@ -65,6 +65,14 @@ impl CallbackInterface { &self.name } + pub fn display(&self) -> &str { + &self.display + } + + pub fn rename_display(&mut self, new_name: String) { + self.display = new_name; + } + pub fn methods(&self) -> Vec<&Method> { self.methods.iter().collect() } @@ -115,16 +123,6 @@ impl CallbackInterface { } } -impl Renameable for CallbackInterface { - fn name(&self) -> &str { - &self.name - } - - fn rename(&mut self, name: String) { - self.name = name; - } -} - impl AsType for CallbackInterface { fn as_type(&self) -> Type { Type::CallbackInterface { @@ -139,7 +137,8 @@ impl TryFrom for CallbackInterface { fn try_from(meta: uniffi_meta::CallbackInterfaceMetadata) -> anyhow::Result { Ok(Self { - name: meta.name, + name: meta.name.clone(), + display: meta.name, module_path: meta.module_path, methods: Default::default(), ffi_init_callback: Default::default(), diff --git a/uniffi_bindgen/src/interface/conventions.rs b/uniffi_bindgen/src/interface/conventions.rs index d8e2cb2dec..ecdfaaf08b 100644 --- a/uniffi_bindgen/src/interface/conventions.rs +++ b/uniffi_bindgen/src/interface/conventions.rs @@ -1,8 +1,40 @@ +use std::collections::BTreeSet; +use uniffi_meta::Type; use crate::{CodeOracle, ComponentInterface, Renameable}; use crate::interface::FfiDefinition; impl ComponentInterface { pub fn apply_naming_conventions(&mut self, oracle: O) { + for (_, t) in &mut self.types.type_definitions { + if t.name().is_some() { + t.rename(oracle.var_name(&t.name().unwrap())) + } + } + + let mut known: BTreeSet = BTreeSet::new(); + dbg!("{:?}", self.clone()); + for t in &mut self.types.all_known_types.iter() { + let mut ty = t.clone(); + if t.name().is_some() { + ty.rename(oracle.var_name(&t.name().unwrap())) + } + known.insert(ty.clone()); + } + + self.types.all_known_types = known; + dbg!("{:?}", self.clone()); + for function_item in self.functions.iter_mut() { + function_item.rename(oracle.fn_name(function_item.name())); + + for arg in &mut function_item.arguments { + arg.rename(oracle.var_name(arg.name())); + } + } + + for callback_interface in self.callback_interfaces.iter_mut() { + callback_interface.rename_display(oracle.class_name(callback_interface.name())); + } + let errors = self.errors.clone(); for enum_item in self.enums.values_mut() { @@ -29,59 +61,53 @@ impl ComponentInterface { } } } - - for record_item in self.records.values_mut() { - record_item.rename(oracle.class_name(record_item.name())); - - for field in &mut record_item.fields { - field.rename(oracle.var_name(field.name())); - } - } - - for function_item in self.functions.iter_mut() { - function_item.rename(oracle.fn_name(function_item.name())); - - for arg in &mut function_item.arguments { - arg.rename(oracle.var_name(arg.name())); - } - } - - for object_item in self.objects.iter_mut() { - for meth in &mut object_item.methods { - meth.rename(oracle.fn_name(meth.name())); - } + // + // for record_item in self.records.values_mut() { + // record_item.rename(oracle.class_name(record_item.name())); + // + // for field in &mut record_item.fields { + // field.rename(oracle.var_name(field.name())); + // } + // } + // - for (ffi_callback, m) in object_item.vtable_methods().iter_mut() { - m.rename(oracle.fn_name(m.name())); + // for object_item in self.objects.iter_mut() { + // for meth in &mut object_item.methods { + // meth.rename(oracle.fn_name(meth.name())); + // } + // + // + // for (ffi_callback, m) in object_item.vtable_methods().iter_mut() { + // m.rename(oracle.fn_name(m.name())); + // + // for arg in &mut ffi_callback.arguments { + // arg.rename(oracle.var_name(arg.name())); + // } + // } + // + // for cons in &mut object_item.constructors { + // if !cons.is_primary_constructor() { + // cons.rename(oracle.fn_name(cons.name())); + // } + // + // } + // } - for arg in &mut ffi_callback.arguments { - arg.rename(oracle.var_name(arg.name())); + for field in self.ffi_definitions() { + match field { + FfiDefinition::Function(mut ffi_function) => { + ffi_function.rename(oracle.var_name(ffi_function.name())); } - } - - for cons in &mut object_item.constructors { - if !cons.is_primary_constructor() { - cons.rename(oracle.fn_name(cons.name())); + FfiDefinition::CallbackFunction(mut callback_function) => { + callback_function.rename(oracle.var_name(callback_function.name())) + } + FfiDefinition::Struct(mut ffi_struct) => { + for f in &mut ffi_struct.fields { + f.rename(oracle.var_name(f.name())); + } } - } } - // - // for callback_interface in self.callback_interfaces.iter_mut() { - // callback_interface.rename(oracle.class_name(callback_interface.name())); - // } - - // for field in self.ffi_definitions() { - // match field { - // FfiDefinition::Function(_) => {} - // FfiDefinition::CallbackFunction(_) => {} - // FfiDefinition::Struct(mut ffi_struct) => { - // for f in &mut ffi_struct.fields { - // f.rename(oracle.var_name(f.name())); - // } - // } - // } - // } } } diff --git a/uniffi_bindgen/src/interface/enum_.rs b/uniffi_bindgen/src/interface/enum_.rs index 69a6114d03..06009b1651 100644 --- a/uniffi_bindgen/src/interface/enum_.rs +++ b/uniffi_bindgen/src/interface/enum_.rs @@ -159,7 +159,6 @@ //! # Ok::<(), anyhow::Error>(()) //! ``` -use crate::Renameable; use anyhow::Result; use uniffi_meta::{Checksum, EnumShape}; @@ -188,6 +187,10 @@ impl Enum { &self.name } + pub fn rename(&mut self, name: String) { + self.name = name; + } + pub fn variants(&self) -> &[Variant] { &self.variants } @@ -251,16 +254,6 @@ impl Enum { } } -impl Renameable for Enum { - fn name(&self) -> &str { - &self.name - } - - fn rename(&mut self, name: String) { - self.name = name; - } -} - impl TryFrom for Enum { type Error = anyhow::Error; @@ -308,10 +301,18 @@ impl Variant { &self.name } + pub fn rename(&mut self, new_name: String) { + self.name = new_name; + } + pub fn is_name(&self) -> &str { &self.is_name } + pub fn set_is_name(&mut self, name: String) { + self.is_name = name; + } + pub fn fields(&self) -> &[Field] { &self.fields } @@ -331,20 +332,6 @@ impl Variant { pub fn iter_types(&self) -> TypeIterator<'_> { Box::new(self.fields.iter().flat_map(Field::iter_types)) } - - pub fn set_is_name(&mut self, name: String) { - self.is_name = name; - } -} - -impl Renameable for Variant { - fn name(&self) -> &str { - &self.name - } - - fn rename(&mut self, new_name: String) { - self.name = new_name; - } } impl TryFrom for Variant { diff --git a/uniffi_bindgen/src/interface/ffi.rs b/uniffi_bindgen/src/interface/ffi.rs index 9a2fc843bb..e103cc2a42 100644 --- a/uniffi_bindgen/src/interface/ffi.rs +++ b/uniffi_bindgen/src/interface/ffi.rs @@ -218,6 +218,10 @@ impl FfiFunction { &self.name } + pub fn rename(&mut self, new_name: String) { + self.name = new_name; + } + /// Name of the FFI buffer version of this function that's generated when the /// `scaffolding-ffi-buffer-fns` feature is enabled. pub fn ffi_buffer_fn_name(&self) -> String { @@ -319,6 +323,10 @@ impl FfiCallbackFunction { &self.name } + pub fn rename(&mut self, new_name: String) { + self.name = new_name; + } + pub fn arguments(&self) -> Vec<&FfiArgument> { self.arguments.iter().collect() } diff --git a/uniffi_bindgen/src/interface/function.rs b/uniffi_bindgen/src/interface/function.rs index b2810b1ade..62212f00b5 100644 --- a/uniffi_bindgen/src/interface/function.rs +++ b/uniffi_bindgen/src/interface/function.rs @@ -38,8 +38,6 @@ use uniffi_meta::Checksum; use super::ffi::{FfiArgument, FfiFunction, FfiType}; use super::{AsType, ComponentInterface, Literal, ObjectImpl, Type, TypeIterator}; -use crate::Renameable; - /// Represents a standalone function. /// /// Each `Function` corresponds to a standalone function in the rust module, @@ -75,6 +73,10 @@ impl Function { &self.name } + pub fn rename(&mut self, new_name: String) { + self.name = new_name; + } + pub fn is_async(&self) -> bool { self.is_async } @@ -138,16 +140,6 @@ impl Function { } } -impl Renameable for Function { - fn name(&self) -> &str { - &self.name - } - - fn rename(&mut self, name: String) { - self.name = name; - } -} - impl From for Argument { fn from(meta: uniffi_meta::FnParamMetadata) -> Self { Argument { @@ -206,6 +198,10 @@ impl Argument { &self.name } + pub fn rename(&mut self, new_name: String) { + self.name = new_name; + } + pub fn by_ref(&self) -> bool { self.by_ref } @@ -223,16 +219,6 @@ impl Argument { } } -impl Renameable for Argument { - fn name(&self) -> &str { - &self.name - } - - fn rename(&mut self, new_name: String) { - self.name = new_name; - } -} - impl AsType for Argument { fn as_type(&self) -> Type { self.type_.clone() diff --git a/uniffi_bindgen/src/interface/mod.rs b/uniffi_bindgen/src/interface/mod.rs index 5fb2275eba..78c88a2a09 100644 --- a/uniffi_bindgen/src/interface/mod.rs +++ b/uniffi_bindgen/src/interface/mod.rs @@ -1196,7 +1196,7 @@ new definition: Enum { variants: [ Variant { name: \"three\", - is_name: \"trhee\", + is_name: \"three\", discr: None, fields: [], docstring: None, diff --git a/uniffi_bindgen/src/interface/universe.rs b/uniffi_bindgen/src/interface/universe.rs index a1cd2cab71..64613679fa 100644 --- a/uniffi_bindgen/src/interface/universe.rs +++ b/uniffi_bindgen/src/interface/universe.rs @@ -28,9 +28,9 @@ pub(crate) struct TypeUniverse { pub namespace_docstring: Option, // Named type definitions (including aliases). - type_definitions: HashMap, + pub type_definitions: HashMap, // All the types in the universe, by canonical type name, in a well-defined order. - all_known_types: BTreeSet, + pub all_known_types: BTreeSet, } impl TypeUniverse { diff --git a/uniffi_meta/src/types.rs b/uniffi_meta/src/types.rs index afb6218933..445f3217be 100644 --- a/uniffi_meta/src/types.rs +++ b/uniffi_meta/src/types.rs @@ -143,6 +143,54 @@ impl Type { }; Box::new(std::iter::once(self).chain(nested_types)) } + + pub fn name(&self) -> Option { + match self { + // Type::Object { name, ..} => { + // Some(name.to_string()) + // } + // Type::Record { name, .. } => { + // Some(name.to_string()) + // } + // Type::Enum { name, ..} => { + // Some(name.to_string()) + // } + // Type::CallbackInterface { name, .. } => { + // Some(name.to_string()) + // } + // Type::Custom { name, .. } => { + // Some(name.to_string()) + // }, + Type::External { name, .. } => { + Some(name.to_string()) + }, + _ => None, + } + } + + pub fn rename(&mut self, new_name: String) { + match self { + // Type::Object { name, ..} => { + // *name = new_name; + // } + // Type::Record { name, .. } => { + // *name = new_name; + // } + // Type::Enum { name, ..} => { + // *name = new_name; + // } + // Type::CallbackInterface { name, .. } => { + // *name = new_name; + // } + // Type::Custom { name, .. } => { + // *name = new_name; + // }, + Type::External { name, .. } => { + *name = new_name; + }, + _ => {} + } + } } // A trait so various things can turn into a type. From f87f146ae0f3f526c26c4e372a17835858d404e8 Mon Sep 17 00:00:00 2001 From: Bastian Gruber Date: Tue, 23 Jul 2024 15:50:50 -0300 Subject: [PATCH 36/62] wip --- .../src/bindings/python/gen_python/mod.rs | 6 +- uniffi_bindgen/src/interface/conventions.rs | 92 +++++++++++-------- uniffi_bindgen/src/interface/record.rs | 27 ++---- uniffi_bindgen/src/lib.rs | 2 + uniffi_meta/src/types.rs | 84 +++++++++++------ 5 files changed, 123 insertions(+), 88 deletions(-) diff --git a/uniffi_bindgen/src/bindings/python/gen_python/mod.rs b/uniffi_bindgen/src/bindings/python/gen_python/mod.rs index 835882239b..df13a4babd 100644 --- a/uniffi_bindgen/src/bindings/python/gen_python/mod.rs +++ b/uniffi_bindgen/src/bindings/python/gen_python/mod.rs @@ -301,7 +301,7 @@ pub struct PythonWrapper<'a> { impl<'a> PythonWrapper<'a> { pub fn new(config: Config, ci: &'a mut ComponentInterface) -> Self { ci.apply_naming_conventions(PythonCodeOracle); - dbg!("{:#?}", ci.clone()); + let type_renderer = TypeRenderer::new(&config, ci); let type_helper_code = type_renderer.render().unwrap(); let type_imports = type_renderer.imports.into_inner(); @@ -342,6 +342,10 @@ impl CodeOracle for PythonCodeOracle { fixup_keyword(nm.to_string().to_upper_camel_case()) } + fn external_types_name(&self, nm: &str) -> String { + fixup_keyword(nm.to_string()) + } + /// Get the idiomatic Python rendering of a function name. fn fn_name(&self, nm: &str) -> String { fixup_keyword(nm.to_string().to_snake_case()) diff --git a/uniffi_bindgen/src/interface/conventions.rs b/uniffi_bindgen/src/interface/conventions.rs index ecdfaaf08b..c1cfe46cb1 100644 --- a/uniffi_bindgen/src/interface/conventions.rs +++ b/uniffi_bindgen/src/interface/conventions.rs @@ -1,28 +1,33 @@ -use std::collections::BTreeSet; +use std::collections::{BTreeMap, BTreeSet}; use uniffi_meta::Type; use crate::{CodeOracle, ComponentInterface, Renameable}; -use crate::interface::FfiDefinition; +use crate::interface::{FfiDefinition, Record}; impl ComponentInterface { pub fn apply_naming_conventions(&mut self, oracle: O) { + for (_, t) in &mut self.types.type_definitions { + if t.name().is_some() { - t.rename(oracle.var_name(&t.name().unwrap())) + t.rename(oracle.var_name(&t.name().unwrap())); + } } let mut known: BTreeSet = BTreeSet::new(); - dbg!("{:?}", self.clone()); + for t in &mut self.types.all_known_types.iter() { + dbg!("type in all_known {:#?}", t.clone()); let mut ty = t.clone(); if t.name().is_some() { - ty.rename(oracle.var_name(&t.name().unwrap())) + ty.rename(oracle.external_types_name(&t.name().unwrap())); + dbg!("t has name {:#?}", ty.clone()); } known.insert(ty.clone()); } self.types.all_known_types = known; - dbg!("{:?}", self.clone()); + for function_item in self.functions.iter_mut() { function_item.rename(oracle.fn_name(function_item.name())); @@ -61,38 +66,48 @@ impl ComponentInterface { } } } - // - // for record_item in self.records.values_mut() { - // record_item.rename(oracle.class_name(record_item.name())); - // - // for field in &mut record_item.fields { - // field.rename(oracle.var_name(field.name())); - // } - // } - // - - - // for object_item in self.objects.iter_mut() { - // for meth in &mut object_item.methods { - // meth.rename(oracle.fn_name(meth.name())); - // } - // - // - // for (ffi_callback, m) in object_item.vtable_methods().iter_mut() { - // m.rename(oracle.fn_name(m.name())); - // - // for arg in &mut ffi_callback.arguments { - // arg.rename(oracle.var_name(arg.name())); - // } - // } - // - // for cons in &mut object_item.constructors { - // if !cons.is_primary_constructor() { - // cons.rename(oracle.fn_name(cons.name())); - // } - // - // } - // } + + let mut new_records: BTreeMap = BTreeMap::new(); + + for (key, record_item) in self.records.iter_mut() { + let mut record = record_item.clone(); + + record.rename(oracle.external_types_name(record_item.name())); + + for field in &mut record.fields { + field.rename(oracle.var_name(field.name())); + } + + new_records.insert(oracle.external_types_name(key), record); + } + + self.records = new_records; + + + + for object_item in self.objects.iter_mut() { + object_item.rename(oracle.external_types_name(object_item.name())); + + for meth in &mut object_item.methods { + meth.rename(oracle.fn_name(meth.name())); + } + + + for (ffi_callback, m) in object_item.vtable_methods().iter_mut() { + m.rename(oracle.fn_name(m.name())); + + for arg in &mut ffi_callback.arguments { + arg.rename(oracle.var_name(arg.name())); + } + } + + for cons in &mut object_item.constructors { + if !cons.is_primary_constructor() { + cons.rename(oracle.fn_name(cons.name())); + } + + } + } for field in self.ffi_definitions() { match field { @@ -109,5 +124,6 @@ impl ComponentInterface { } } } + } } diff --git a/uniffi_bindgen/src/interface/record.rs b/uniffi_bindgen/src/interface/record.rs index 794d1e49d7..3af6495c99 100644 --- a/uniffi_bindgen/src/interface/record.rs +++ b/uniffi_bindgen/src/interface/record.rs @@ -44,7 +44,6 @@ //! # Ok::<(), anyhow::Error>(()) //! ``` -use crate::Renameable; use anyhow::Result; use uniffi_meta::Checksum; @@ -70,6 +69,10 @@ impl Record { &self.name } + pub fn rename(&mut self, name: String) { + self.name = name; + } + pub fn fields(&self) -> &[Field] { &self.fields } @@ -87,15 +90,6 @@ impl Record { } } -impl Renameable for Record { - fn name(&self) -> &str { - &self.name - } - fn rename(&mut self, name: String) { - self.name = name; - } -} - impl AsType for Record { fn as_type(&self) -> Type { Type::Record { @@ -137,6 +131,10 @@ impl Field { &self.name } + pub fn rename(&mut self, name: String) { + self.name = name; + } + pub fn default_value(&self) -> Option<&Literal> { self.default.as_ref() } @@ -150,15 +148,6 @@ impl Field { } } -impl Renameable for Field { - fn name(&self) -> &str { - &self.name - } - fn rename(&mut self, name: String) { - self.name = name; - } -} - impl AsType for Field { fn as_type(&self) -> Type { self.type_.clone() diff --git a/uniffi_bindgen/src/lib.rs b/uniffi_bindgen/src/lib.rs index dca247bbbe..ea80e5f7fe 100644 --- a/uniffi_bindgen/src/lib.rs +++ b/uniffi_bindgen/src/lib.rs @@ -165,6 +165,8 @@ pub trait CodeOracle { /// Get the idiomatic Python rendering of a class name (for enums, records, errors, etc). fn class_name(&self, nm: &str) -> String; + fn external_types_name(&self, nm: &str) -> String; + /// Get the idiomatic Python rendering of a function name. fn fn_name(&self, nm: &str) -> String; diff --git a/uniffi_meta/src/types.rs b/uniffi_meta/src/types.rs index 445f3217be..9bd26461f6 100644 --- a/uniffi_meta/src/types.rs +++ b/uniffi_meta/src/types.rs @@ -146,48 +146,72 @@ impl Type { pub fn name(&self) -> Option { match self { - // Type::Object { name, ..} => { - // Some(name.to_string()) - // } - // Type::Record { name, .. } => { - // Some(name.to_string()) - // } - // Type::Enum { name, ..} => { - // Some(name.to_string()) - // } - // Type::CallbackInterface { name, .. } => { - // Some(name.to_string()) - // } - // Type::Custom { name, .. } => { - // Some(name.to_string()) - // }, + Type::Object { name, ..} => { + Some(name.to_string()) + } + Type::Record { name, .. } => { + Some(name.to_string()) + } + Type::Enum { name, ..} => { + Some(name.to_string()) + } + Type::CallbackInterface { name, .. } => { + Some(name.to_string()) + } + Type::Custom { name, .. } => { + Some(name.to_string()) + }, Type::External { name, .. } => { Some(name.to_string()) }, + Type::Optional { inner_type} | Type::Sequence { inner_type} => { + if inner_type.name().is_some() { + Some(inner_type.name().unwrap()) + } else { + None + } + } + Type::Map { value_type, .. } => { + if value_type.name().is_some() { + Some(value_type.name().unwrap()) + } else { + None + } + } _ => None, } } pub fn rename(&mut self, new_name: String) { match self { - // Type::Object { name, ..} => { - // *name = new_name; - // } - // Type::Record { name, .. } => { - // *name = new_name; - // } - // Type::Enum { name, ..} => { - // *name = new_name; - // } - // Type::CallbackInterface { name, .. } => { - // *name = new_name; - // } - // Type::Custom { name, .. } => { - // *name = new_name; - // }, + Type::Object { name, ..} => { + *name = new_name; + } + Type::Record { name, .. } => { + *name = new_name; + } + Type::Enum { name, ..} => { + *name = new_name; + } + Type::CallbackInterface { name, .. } => { + *name = new_name; + } + Type::Custom { name, builtin, .. } => { + *name = new_name; + }, Type::External { name, .. } => { *name = new_name; }, + Type::Optional { inner_type} | Type::Sequence { inner_type} => { + if inner_type.name().is_some() { + inner_type.rename(new_name); + } + } + Type::Map { value_type, .. } => { + if value_type.name().is_some() { + value_type.rename(new_name); + } + } _ => {} } } From 97ab00462ba639e216b273b4c36807417c63dbc3 Mon Sep 17 00:00:00 2001 From: Bastian Gruber Date: Tue, 23 Jul 2024 16:01:12 -0300 Subject: [PATCH 37/62] wip --- uniffi_bindgen/src/bindings/python/gen_python/mod.rs | 2 +- uniffi_bindgen/src/bindings/python/templates/macros.py | 2 +- uniffi_bindgen/src/interface/conventions.rs | 2 -- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/uniffi_bindgen/src/bindings/python/gen_python/mod.rs b/uniffi_bindgen/src/bindings/python/gen_python/mod.rs index df13a4babd..604e15b974 100644 --- a/uniffi_bindgen/src/bindings/python/gen_python/mod.rs +++ b/uniffi_bindgen/src/bindings/python/gen_python/mod.rs @@ -301,7 +301,7 @@ pub struct PythonWrapper<'a> { impl<'a> PythonWrapper<'a> { pub fn new(config: Config, ci: &'a mut ComponentInterface) -> Self { ci.apply_naming_conventions(PythonCodeOracle); - + dbg!("{:#?}", ci.clone()); let type_renderer = TypeRenderer::new(&config, ci); let type_helper_code = type_renderer.render().unwrap(); let type_imports = type_renderer.imports.into_inner(); diff --git a/uniffi_bindgen/src/bindings/python/templates/macros.py b/uniffi_bindgen/src/bindings/python/templates/macros.py index 1a47226bd0..3040ff0ab4 100644 --- a/uniffi_bindgen/src/bindings/python/templates/macros.py +++ b/uniffi_bindgen/src/bindings/python/templates/macros.py @@ -34,7 +34,7 @@ {%- macro arg_list_lowered(func) %} {%- for arg in func.arguments() %} - {{ arg|lower_fn }}({{ arg.name() }}) + {{ arg|lower_fn }}({{ arg.name()|var_name }}) {%- if !loop.last %},{% endif %} {%- endfor %} {%- endmacro -%} diff --git a/uniffi_bindgen/src/interface/conventions.rs b/uniffi_bindgen/src/interface/conventions.rs index c1cfe46cb1..4b8bd88ac9 100644 --- a/uniffi_bindgen/src/interface/conventions.rs +++ b/uniffi_bindgen/src/interface/conventions.rs @@ -83,8 +83,6 @@ impl ComponentInterface { self.records = new_records; - - for object_item in self.objects.iter_mut() { object_item.rename(oracle.external_types_name(object_item.name())); From bad1957af1a7908fbbb9baa792c73aab980ca373 Mon Sep 17 00:00:00 2001 From: Bastian Gruber Date: Wed, 24 Jul 2024 10:32:10 -0300 Subject: [PATCH 38/62] fix: enum, error, cbinterface works --- .../src/bindings/python/gen_python/mod.rs | 8 +- .../python/templates/CallbackInterfaceImpl.py | 10 +- .../bindings/python/templates/EnumTemplate.py | 22 +-- .../python/templates/ErrorTemplate.py | 6 +- .../python/templates/RecordTemplate.py | 16 +- .../templates/TopLevelFunctionTemplate.py | 8 +- .../src/bindings/python/templates/macros.py | 14 +- .../src/bindings/python/templates/wrapper.py | 4 +- uniffi_bindgen/src/interface/callbacks.rs | 4 +- uniffi_bindgen/src/interface/conventions.rs | 146 +++++++++--------- uniffi_meta/src/types.rs | 38 ++--- 11 files changed, 127 insertions(+), 149 deletions(-) diff --git a/uniffi_bindgen/src/bindings/python/gen_python/mod.rs b/uniffi_bindgen/src/bindings/python/gen_python/mod.rs index 604e15b974..31c0fdc022 100644 --- a/uniffi_bindgen/src/bindings/python/gen_python/mod.rs +++ b/uniffi_bindgen/src/bindings/python/gen_python/mod.rs @@ -301,7 +301,7 @@ pub struct PythonWrapper<'a> { impl<'a> PythonWrapper<'a> { pub fn new(config: Config, ci: &'a mut ComponentInterface) -> Self { ci.apply_naming_conventions(PythonCodeOracle); - dbg!("{:#?}", ci.clone()); + let type_renderer = TypeRenderer::new(&config, ci); let type_helper_code = type_renderer.render().unwrap(); let type_imports = type_renderer.imports.into_inner(); @@ -508,7 +508,6 @@ pub mod filters { Ok(as_ct.as_codetype().type_label()) } - /// Get the idiomatic Python rendering of a class name (for enums, records, errors, etc). /// Get the idiomatic Python rendering of a class name (for enums, records, errors, etc). pub fn class_name(nm: &str) -> Result { Ok(PythonCodeOracle.class_name(nm)) @@ -524,11 +523,6 @@ pub mod filters { Ok(PythonCodeOracle.var_name(nm)) } - /// Get the idiomatic Python rendering of an individual enum variant. - pub fn enum_variant_py(nm: &str) -> Result { - Ok(PythonCodeOracle.enum_variant_name(nm)) - } - pub(super) fn ffi_converter_name(as_ct: &impl AsCodeType) -> Result { Ok(String::from("_Uniffi") + &as_ct.as_codetype().ffi_converter_name()[3..]) } diff --git a/uniffi_bindgen/src/bindings/python/templates/CallbackInterfaceImpl.py b/uniffi_bindgen/src/bindings/python/templates/CallbackInterfaceImpl.py index d3d67a1a97..1009505d63 100644 --- a/uniffi_bindgen/src/bindings/python/templates/CallbackInterfaceImpl.py +++ b/uniffi_bindgen/src/bindings/python/templates/CallbackInterfaceImpl.py @@ -7,9 +7,9 @@ class {{ trait_impl }}: {%- for (ffi_callback, meth) in vtable_methods.iter() %} @{{ ffi_callback.name()|ffi_callback_name }} - def {{ meth.name()|fn_name }}( + def {{ meth.name() }}( {%- for arg in ffi_callback.arguments() %} - {{ arg.name()|var_name }}, + {{ arg.name() }}, {%- endfor -%} {%- if ffi_callback.has_rust_call_status_arg() %} uniffi_call_status_ptr, @@ -17,8 +17,8 @@ def {{ meth.name()|fn_name }}( ): uniffi_obj = {{ ffi_converter_name }}._handle_map.get(uniffi_handle) def make_call(): - args = ({% for arg in meth.arguments() %}{{ arg|lift_fn }}({{ arg.name()|var_name }}), {% endfor %}) - method = uniffi_obj.{{ meth.name()|fn_name }} + args = ({% for arg in meth.arguments() %}{{ arg|lift_fn }}({{ arg.name() }}), {% endfor %}) + method = uniffi_obj.{{ meth.name() }} return method(*args) {% if !meth.is_async() %} @@ -89,7 +89,7 @@ def _uniffi_free(uniffi_handle): # Generate the FFI VTable. This has a field for each callback interface method. _uniffi_vtable = {{ vtable|ffi_type_name }}( {%- for (_, meth) in vtable_methods.iter() %} - {{ meth.name()|fn_name }}, + {{ meth.name() }}, {%- endfor %} _uniffi_free ) diff --git a/uniffi_bindgen/src/bindings/python/templates/EnumTemplate.py b/uniffi_bindgen/src/bindings/python/templates/EnumTemplate.py index 49867330ff..cb7931443a 100644 --- a/uniffi_bindgen/src/bindings/python/templates/EnumTemplate.py +++ b/uniffi_bindgen/src/bindings/python/templates/EnumTemplate.py @@ -77,15 +77,15 @@ def __eq__(self, other): # For each variant, we have an `is_NAME` method for easily checking # whether an instance is that variant. {% for variant in e.variants() -%} - def is_{{ variant.name()|var_name }}(self) -> bool: - return isinstance(self, {{ type_name }}.{{ variant.name()|enum_variant_py }}) + def is_{{ variant.is_name() }}(self) -> bool: + return isinstance(self, {{ type_name }}.{{ variant.name() }}) {% endfor %} # Now, a little trick - we make each nested variant class be a subclass of the main # enum class, so that method calls and instance checks etc will work intuitively. # We might be able to do this a little more neatly with a metaclass, but this'll do. {% for variant in e.variants() -%} -{{ type_name }}.{{ variant.name()|enum_variant_py }} = type("{{ type_name }}.{{ variant.name()|enum_variant_py }}", ({{ type_name }}.{{variant.name()|enum_variant_py}}, {{ type_name }},), {}) # type: ignore +{{ type_name }}.{{ variant.name() }} = type("{{ type_name }}.{{ variant.name() }}", ({{ type_name }}.{{variant.name()}}, {{ type_name }},), {}) # type: ignore {% endfor %} {% endif %} @@ -98,9 +98,9 @@ def read(buf): {%- for variant in e.variants() %} if variant == {{ loop.index }}: {%- if e.is_flat() %} - return {{ type_name }}.{{variant.name()|enum_variant_py}} + return {{ type_name }}.{{variant.name()}} {%- else %} - return {{ type_name }}.{{variant.name()|enum_variant_py}}( + return {{ type_name }}.{{variant.name()}}( {%- for field in variant.fields() %} {{ field|read_fn }}(buf), {%- endfor %} @@ -116,15 +116,15 @@ def check_lower(value): {%- else %} {%- for variant in e.variants() %} {%- if e.is_flat() %} - if value == {{ type_name }}.{{ variant.name()|enum_variant_py }}: + if value == {{ type_name }}.{{ variant.name() }}: {%- else %} - if value.is_{{ variant.name()|var_name }}(): + if value.is_{{ variant.is_name() }}(): {%- endif %} {%- for field in variant.fields() %} {%- if variant.has_nameless_fields() %} {{ field|check_lower_fn }}(value._values[{{ loop.index0 }}]) {%- else %} - {{ field|check_lower_fn }}(value.{{ field.name()|var_name }}) + {{ field|check_lower_fn }}(value.{{ field.name() }}) {%- endif %} {%- endfor %} return @@ -136,16 +136,16 @@ def check_lower(value): def write(value, buf): {%- for variant in e.variants() %} {%- if e.is_flat() %} - if value == {{ type_name }}.{{ variant.name()|enum_variant_py }}: + if value == {{ type_name }}.{{ variant.name() }}: buf.write_i32({{ loop.index }}) {%- else %} - if value.is_{{ variant.name()|var_name }}(): + if value.is_{{ variant.is_name() }}(): buf.write_i32({{ loop.index }}) {%- for field in variant.fields() %} {%- if variant.has_nameless_fields() %} {{ field|write_fn }}(value._values[{{ loop.index0 }}], buf) {%- else %} - {{ field|write_fn }}(value.{{ field.name()|var_name }}, buf) + {{ field|write_fn }}(value.{{ field.name() }}, buf) {%- endif %} {%- endfor %} {%- endif %} diff --git a/uniffi_bindgen/src/bindings/python/templates/ErrorTemplate.py b/uniffi_bindgen/src/bindings/python/templates/ErrorTemplate.py index 9d3b30831d..450364c55b 100644 --- a/uniffi_bindgen/src/bindings/python/templates/ErrorTemplate.py +++ b/uniffi_bindgen/src/bindings/python/templates/ErrorTemplate.py @@ -39,15 +39,15 @@ def __getitem__(self, index): return self._values[index] {%- else %} - def __init__(self{% for field in variant.fields() %}, {{ field.name() }}{% endfor %}): + def __init__(self{% for field in variant.fields() %}, {{ field.name() }}{% endfor %}): {%- if variant.has_fields() %} super().__init__(", ".join([ {%- for field in variant.fields() %} - "{{ field.name() }}={!r}".format({{ field.name() }}), + "{{ field.name() }}={!r}".format({{ field.name() }}), {%- endfor %} ])) {%- for field in variant.fields() %} - self.{{ field.name() }} = {{ field.name() }} + self.{{ field.name() }} = {{ field.name() }} {%- endfor %} {%- else %} pass diff --git a/uniffi_bindgen/src/bindings/python/templates/RecordTemplate.py b/uniffi_bindgen/src/bindings/python/templates/RecordTemplate.py index cf0d0653d7..d778b90ebd 100644 --- a/uniffi_bindgen/src/bindings/python/templates/RecordTemplate.py +++ b/uniffi_bindgen/src/bindings/python/templates/RecordTemplate.py @@ -2,18 +2,18 @@ class {{ type_name }}: {%- call py::docstring(rec, 4) %} {%- for field in rec.fields() %} - {{ field.name()|var_name }}: "{{ field|type_name }}" + {{ field.name() }}: "{{ field|type_name }}" {%- call py::docstring(field, 4) %} {%- endfor %} {%- if rec.has_fields() %} def __init__(self, *, {% for field in rec.fields() %} - {{- field.name()|var_name }}: "{{- field|type_name }}" + {{- field.name() }}: "{{- field|type_name }}" {%- if field.default_value().is_some() %} = _DEFAULT{% endif %} {%- if !loop.last %}, {% endif %} {%- endfor %}): {%- for field in rec.fields() %} - {%- let field_name = field.name()|var_name %} + {%- let field_name = field.name() %} {%- match field.default_value() %} {%- when None %} self.{{ field_name }} = {{ field_name }} @@ -27,11 +27,11 @@ def __init__(self, *, {% for field in rec.fields() %} {%- endif %} def __str__(self): - return "{{ type_name }}({% for field in rec.fields() %}{{ field.name()|var_name }}={}{% if loop.last %}{% else %}, {% endif %}{% endfor %})".format({% for field in rec.fields() %}self.{{ field.name()|var_name }}{% if loop.last %}{% else %}, {% endif %}{% endfor %}) + return "{{ type_name }}({% for field in rec.fields() %}{{ field.name() }}={}{% if loop.last %}{% else %}, {% endif %}{% endfor %})".format({% for field in rec.fields() %}self.{{ field.name() }}{% if loop.last %}{% else %}, {% endif %}{% endfor %}) def __eq__(self, other): {%- for field in rec.fields() %} - if self.{{ field.name()|var_name }} != other.{{ field.name()|var_name }}: + if self.{{ field.name() }} != other.{{ field.name() }}: return False {%- endfor %} return True @@ -41,7 +41,7 @@ class {{ ffi_converter_name }}(_UniffiConverterRustBuffer): def read(buf): return {{ type_name }}( {%- for field in rec.fields() %} - {{ field.name()|var_name }}={{ field|read_fn }}(buf), + {{ field.name() }}={{ field|read_fn }}(buf), {%- endfor %} ) @@ -51,7 +51,7 @@ def check_lower(value): pass {%- else %} {%- for field in rec.fields() %} - {{ field|check_lower_fn }}(value.{{ field.name()|var_name }}) + {{ field|check_lower_fn }}(value.{{ field.name() }}) {%- endfor %} {%- endif %} @@ -59,7 +59,7 @@ def check_lower(value): def write(value, buf): {%- if rec.has_fields() %} {%- for field in rec.fields() %} - {{ field|write_fn }}(value.{{ field.name()|var_name }}, buf) + {{ field|write_fn }}(value.{{ field.name() }}, buf) {%- endfor %} {%- else %} pass diff --git a/uniffi_bindgen/src/bindings/python/templates/TopLevelFunctionTemplate.py b/uniffi_bindgen/src/bindings/python/templates/TopLevelFunctionTemplate.py index 7a6cb6d7c2..230b9e853f 100644 --- a/uniffi_bindgen/src/bindings/python/templates/TopLevelFunctionTemplate.py +++ b/uniffi_bindgen/src/bindings/python/templates/TopLevelFunctionTemplate.py @@ -2,9 +2,9 @@ {%- match func.return_type() -%} {%- when Some with (return_type) %} -async def {{ func.name() }}({%- call py::arg_list_decl(func) -%}) -> "{{ return_type|type_name }}": +async def {{ func.name()|fn_name }}({%- call py::arg_list_decl(func) -%}) -> "{{ return_type|type_name }}": {% when None %} -async def {{ func.name() }}({%- call py::arg_list_decl(func) -%}) -> None: +async def {{ func.name()|fn_name }}({%- call py::arg_list_decl(func) -%}) -> None: {% endmatch %} {%- call py::docstring(func, 4) %} @@ -28,13 +28,13 @@ async def {{ func.name() }}({%- call py::arg_list_decl(func) -%}) -> None: {%- match func.return_type() -%} {%- when Some with (return_type) %} -def {{ func.name() }}({%- call py::arg_list_decl(func) -%}) -> "{{ return_type|type_name }}": +def {{ func.name()|fn_name }}({%- call py::arg_list_decl(func) -%}) -> "{{ return_type|type_name }}": {%- call py::docstring(func, 4) %} {%- call py::setup_args(func) %} return {{ return_type|lift_fn }}({% call py::to_ffi_call(func) %}) {% when None %} -def {{ func.name() }}({%- call py::arg_list_decl(func) -%}) -> None: +def {{ func.name()|fn_name }}({%- call py::arg_list_decl(func) -%}) -> None: {%- call py::docstring(func, 4) %} {%- call py::setup_args(func) %} {% call py::to_ffi_call(func) %} diff --git a/uniffi_bindgen/src/bindings/python/templates/macros.py b/uniffi_bindgen/src/bindings/python/templates/macros.py index 3040ff0ab4..0f4d3a413b 100644 --- a/uniffi_bindgen/src/bindings/python/templates/macros.py +++ b/uniffi_bindgen/src/bindings/python/templates/macros.py @@ -59,7 +59,7 @@ {% macro arg_list_decl(func) %} {%- for arg in func.arguments() -%} - {{ arg.name() }} + {{ arg.name()|var_name }} {%- match arg.default_value() %} {%- when Some with(literal) %}: "typing.Union[object, {{ arg|type_name -}}]" = _DEFAULT {%- else %}: "{{ arg|type_name -}}" @@ -88,10 +88,10 @@ {%- match arg.default_value() %} {%- when None %} {%- when Some with(literal) %} - if {{ arg.name() }} is _DEFAULT: - {{ arg.name() }} = {{ literal|literal_py(arg.as_type().borrow()) }} + if {{ arg.name()|var_name }} is _DEFAULT: + {{ arg.name()|var_name }} = {{ literal|literal_py(arg.as_type().borrow()) }} {%- endmatch %} - {{ arg|check_lower_fn }}({{ arg.name() }}) + {{ arg|check_lower_fn }}({{ arg.name()|var_name }}) {% endfor -%} {%- endmacro -%} @@ -104,10 +104,10 @@ {%- match arg.default_value() %} {%- when None %} {%- when Some with(literal) %} - if {{ arg.name() }} is _DEFAULT: - {{ arg.name() }} = {{ literal|literal_py(arg.as_type().borrow()) }} + if {{ arg.name()|var_name }} is _DEFAULT: + {{ arg.name()|var_name }} = {{ literal|literal_py(arg.as_type().borrow()) }} {%- endmatch %} - {{ arg|check_lower_fn }}({{ arg.name() }}) + {{ arg|check_lower_fn }}({{ arg.name()|var_name }}) {% endfor -%} {%- endmacro -%} diff --git a/uniffi_bindgen/src/bindings/python/templates/wrapper.py b/uniffi_bindgen/src/bindings/python/templates/wrapper.py index d88497c313..7b1f38f6a8 100644 --- a/uniffi_bindgen/src/bindings/python/templates/wrapper.py +++ b/uniffi_bindgen/src/bindings/python/templates/wrapper.py @@ -67,13 +67,13 @@ "{{ record|type_name }}", {%- endfor %} {%- for func in ci.function_definitions() %} - "{{ func.name() }}", + "{{ func.name()|fn_name }}", {%- endfor %} {%- for obj in ci.object_definitions() %} "{{ obj|type_name }}", {%- endfor %} {%- for c in ci.callback_interface_definitions() %} - "{{ c.display() }}", + "{{ c.name()|class_name }}", {%- endfor %} ] diff --git a/uniffi_bindgen/src/interface/callbacks.rs b/uniffi_bindgen/src/interface/callbacks.rs index 92bb095bc8..b7ba05624c 100644 --- a/uniffi_bindgen/src/interface/callbacks.rs +++ b/uniffi_bindgen/src/interface/callbacks.rs @@ -102,11 +102,11 @@ impl CallbackInterface { } /// Vec of (ffi_callback, method) pairs - pub fn vtable_methods(&self) -> Vec<(FfiCallbackFunction, &Method)> { + pub fn vtable_methods(&self) -> Vec<(FfiCallbackFunction, Method)> { self.methods .iter() .enumerate() - .map(|(i, method)| (method_ffi_callback(&self.name, method, i), method)) + .map(|(i, method)| (method_ffi_callback(&self.name, method, i), method.clone())) .collect() } diff --git a/uniffi_bindgen/src/interface/conventions.rs b/uniffi_bindgen/src/interface/conventions.rs index 4b8bd88ac9..5b3a61570b 100644 --- a/uniffi_bindgen/src/interface/conventions.rs +++ b/uniffi_bindgen/src/interface/conventions.rs @@ -1,45 +1,51 @@ -use std::collections::{BTreeMap, BTreeSet}; -use uniffi_meta::Type; -use crate::{CodeOracle, ComponentInterface, Renameable}; -use crate::interface::{FfiDefinition, Record}; +use crate::{interface::Record, CodeOracle, ComponentInterface, Renameable}; +use std::collections::BTreeMap; impl ComponentInterface { pub fn apply_naming_conventions(&mut self, oracle: O) { + // Applying changes to the TypeUniverse + // for (_, t) in &mut self.types.type_definitions { + // + // if t.name().is_some() { + // t.rename(oracle.var_name(&t.name().unwrap())); + // + // } + // } + // + // let mut known: BTreeSet = BTreeSet::new(); + // + // for t in &mut self.types.all_known_types.iter() { + // let mut ty = t.clone(); + // if t.name().is_some() { + // ty.rename(oracle.external_types_name(&t.name().unwrap())); + // } + // known.insert(ty.clone()); + // } + // + // self.types.all_known_types = known; + // + // for function_item in self.functions.iter_mut() { + // function_item.rename(oracle.fn_name(function_item.name())); + // + // for arg in &mut function_item.arguments { + // arg.rename(oracle.var_name(arg.name())); + // } + // } + + // Conversions for CallbackInterfaceImpl.py + for callback_interface in self.callback_interfaces.iter_mut() { + // callback_interface.rename_display(oracle.fn_name(callback_interface.name())); - for (_, t) in &mut self.types.type_definitions { - - if t.name().is_some() { - t.rename(oracle.var_name(&t.name().unwrap())); - - } - } - - let mut known: BTreeSet = BTreeSet::new(); - - for t in &mut self.types.all_known_types.iter() { - dbg!("type in all_known {:#?}", t.clone()); - let mut ty = t.clone(); - if t.name().is_some() { - ty.rename(oracle.external_types_name(&t.name().unwrap())); - dbg!("t has name {:#?}", ty.clone()); - } - known.insert(ty.clone()); - } - - self.types.all_known_types = known; - - for function_item in self.functions.iter_mut() { - function_item.rename(oracle.fn_name(function_item.name())); + for method in callback_interface.methods.iter_mut() { + method.rename(oracle.fn_name(method.name())); - for arg in &mut function_item.arguments { - arg.rename(oracle.var_name(arg.name())); + for arg in method.arguments.iter_mut() { + arg.rename(oracle.var_name(arg.name())); + } } } - for callback_interface in self.callback_interfaces.iter_mut() { - callback_interface.rename_display(oracle.class_name(callback_interface.name())); - } - + // Conversions for EnumTemplate.py and ErrorTemplate.py let errors = self.errors.clone(); for enum_item in self.enums.values_mut() { @@ -58,6 +64,7 @@ impl ComponentInterface { for variant in &mut enum_item.variants { variant.rename(oracle.enum_variant_name(variant.name())); + // The template variant.set_is_name(oracle.var_name(variant.name())); for field in &mut variant.fields { @@ -67,61 +74,50 @@ impl ComponentInterface { } } + // Conversions for RecordTemplate.py let mut new_records: BTreeMap = BTreeMap::new(); for (key, record_item) in self.records.iter_mut() { let mut record = record_item.clone(); + // We just want to prefix reserved keywords for the name, without modifying it record.rename(oracle.external_types_name(record_item.name())); for field in &mut record.fields { field.rename(oracle.var_name(field.name())); } - new_records.insert(oracle.external_types_name(key), record); + // We just want to prefix reserved keywords for the name, without modifying it + // new_records.insert(oracle.external_types_name(key), record); + new_records.insert(key.to_string(), record); } + // One cannot alter a BTreeMap in place (with a few hacks maybe...), so we create a new one + // with the adjusted names, and replace it. self.records = new_records; - for object_item in self.objects.iter_mut() { - object_item.rename(oracle.external_types_name(object_item.name())); - - for meth in &mut object_item.methods { - meth.rename(oracle.fn_name(meth.name())); - } - - - for (ffi_callback, m) in object_item.vtable_methods().iter_mut() { - m.rename(oracle.fn_name(m.name())); - - for arg in &mut ffi_callback.arguments { - arg.rename(oracle.var_name(arg.name())); - } - } - - for cons in &mut object_item.constructors { - if !cons.is_primary_constructor() { - cons.rename(oracle.fn_name(cons.name())); - } - - } - } - - for field in self.ffi_definitions() { - match field { - FfiDefinition::Function(mut ffi_function) => { - ffi_function.rename(oracle.var_name(ffi_function.name())); - } - FfiDefinition::CallbackFunction(mut callback_function) => { - callback_function.rename(oracle.var_name(callback_function.name())) - } - FfiDefinition::Struct(mut ffi_struct) => { - for f in &mut ffi_struct.fields { - f.rename(oracle.var_name(f.name())); - } - } - } - } - + // for object_item in self.objects.iter_mut() { + // object_item.rename(oracle.class_name(object_item.name())); + // + // for meth in &mut object_item.methods { + // meth.rename(oracle.fn_name(meth.name())); + // } + // + // + // for (ffi_callback, m) in object_item.vtable_methods().iter_mut() { + // m.rename(oracle.fn_name(m.name())); + // + // for arg in &mut ffi_callback.arguments { + // arg.rename(oracle.var_name(arg.name())); + // } + // } + // + // for cons in &mut object_item.constructors { + // if !cons.is_primary_constructor() { + // cons.rename(oracle.fn_name(cons.name())); + // } + // + // } + // } } } diff --git a/uniffi_meta/src/types.rs b/uniffi_meta/src/types.rs index 9bd26461f6..ee6ecc8ea7 100644 --- a/uniffi_meta/src/types.rs +++ b/uniffi_meta/src/types.rs @@ -146,25 +146,13 @@ impl Type { pub fn name(&self) -> Option { match self { - Type::Object { name, ..} => { - Some(name.to_string()) - } - Type::Record { name, .. } => { - Some(name.to_string()) - } - Type::Enum { name, ..} => { - Some(name.to_string()) - } - Type::CallbackInterface { name, .. } => { - Some(name.to_string()) - } - Type::Custom { name, .. } => { - Some(name.to_string()) - }, - Type::External { name, .. } => { - Some(name.to_string()) - }, - Type::Optional { inner_type} | Type::Sequence { inner_type} => { + Type::Object { name, .. } => Some(name.to_string()), + Type::Record { name, .. } => Some(name.to_string()), + Type::Enum { name, .. } => Some(name.to_string()), + Type::CallbackInterface { name, .. } => Some(name.to_string()), + Type::Custom { name, .. } => Some(name.to_string()), + Type::External { name, .. } => Some(name.to_string()), + Type::Optional { inner_type } | Type::Sequence { inner_type } => { if inner_type.name().is_some() { Some(inner_type.name().unwrap()) } else { @@ -184,25 +172,25 @@ impl Type { pub fn rename(&mut self, new_name: String) { match self { - Type::Object { name, ..} => { + Type::Object { name, .. } => { *name = new_name; } Type::Record { name, .. } => { *name = new_name; } - Type::Enum { name, ..} => { + Type::Enum { name, .. } => { *name = new_name; } Type::CallbackInterface { name, .. } => { *name = new_name; } - Type::Custom { name, builtin, .. } => { + Type::Custom { name, .. } => { *name = new_name; - }, + } Type::External { name, .. } => { *name = new_name; - }, - Type::Optional { inner_type} | Type::Sequence { inner_type} => { + } + Type::Optional { inner_type } | Type::Sequence { inner_type } => { if inner_type.name().is_some() { inner_type.rename(new_name); } From 727cf9f60dee86092c1f7a2e4b633ac8abe1d7a1 Mon Sep 17 00:00:00 2001 From: Bastian Gruber Date: Wed, 24 Jul 2024 10:53:42 -0300 Subject: [PATCH 39/62] feat: ObjectTemplate.py --- .../python/templates/ObjectTemplate.py | 6 +-- .../src/bindings/python/templates/wrapper.py | 2 +- uniffi_bindgen/src/interface/conventions.rs | 42 +++++++++---------- 3 files changed, 23 insertions(+), 27 deletions(-) diff --git a/uniffi_bindgen/src/bindings/python/templates/ObjectTemplate.py b/uniffi_bindgen/src/bindings/python/templates/ObjectTemplate.py index 33a914195e..5772ad24db 100644 --- a/uniffi_bindgen/src/bindings/python/templates/ObjectTemplate.py +++ b/uniffi_bindgen/src/bindings/python/templates/ObjectTemplate.py @@ -52,7 +52,7 @@ def _make_instance_(cls, pointer): @classmethod {%- if cons.is_async() %} - async def {{ cons.name()|fn_name }}(cls, {% call py::arg_list_decl(cons) %}): + async def {{ cons.name() }}(cls, {% call py::arg_list_decl(cons) %}): {%- call py::docstring(cons, 8) %} {%- call py::setup_args_extra_indent(cons) %} @@ -65,7 +65,7 @@ async def {{ cons.name()|fn_name }}(cls, {% call py::arg_list_decl(cons) %}): {% call py::error_ffi_converter(cons) %} ) {%- else %} - def {{ cons.name()|fn_name }}(cls, {% call py::arg_list_decl(cons) %}): + def {{ cons.name() }}(cls, {% call py::arg_list_decl(cons) %}): {%- call py::docstring(cons, 8) %} {%- call py::setup_args_extra_indent(cons) %} # Call the (fallible) function before creating any half-baked object instances. @@ -75,7 +75,7 @@ def {{ cons.name()|fn_name }}(cls, {% call py::arg_list_decl(cons) %}): {% endfor %} {%- for meth in obj.methods() -%} - {%- call py::method_decl(meth.name()|fn_name, meth) %} + {%- call py::method_decl(meth.name(), meth) %} {%- endfor %} {%- for tm in obj.uniffi_traits() -%} {%- match tm %} diff --git a/uniffi_bindgen/src/bindings/python/templates/wrapper.py b/uniffi_bindgen/src/bindings/python/templates/wrapper.py index 7b1f38f6a8..4380041ad8 100644 --- a/uniffi_bindgen/src/bindings/python/templates/wrapper.py +++ b/uniffi_bindgen/src/bindings/python/templates/wrapper.py @@ -67,7 +67,7 @@ "{{ record|type_name }}", {%- endfor %} {%- for func in ci.function_definitions() %} - "{{ func.name()|fn_name }}", + "{{ func.name() }}", {%- endfor %} {%- for obj in ci.object_definitions() %} "{{ obj|type_name }}", diff --git a/uniffi_bindgen/src/interface/conventions.rs b/uniffi_bindgen/src/interface/conventions.rs index 5b3a61570b..cc5bf2e27b 100644 --- a/uniffi_bindgen/src/interface/conventions.rs +++ b/uniffi_bindgen/src/interface/conventions.rs @@ -96,28 +96,24 @@ impl ComponentInterface { // with the adjusted names, and replace it. self.records = new_records; - // for object_item in self.objects.iter_mut() { - // object_item.rename(oracle.class_name(object_item.name())); - // - // for meth in &mut object_item.methods { - // meth.rename(oracle.fn_name(meth.name())); - // } - // - // - // for (ffi_callback, m) in object_item.vtable_methods().iter_mut() { - // m.rename(oracle.fn_name(m.name())); - // - // for arg in &mut ffi_callback.arguments { - // arg.rename(oracle.var_name(arg.name())); - // } - // } - // - // for cons in &mut object_item.constructors { - // if !cons.is_primary_constructor() { - // cons.rename(oracle.fn_name(cons.name())); - // } - // - // } - // } + // Conversions for ObjectTemplate.py + for object_item in self.objects.iter_mut() { + for meth in &mut object_item.methods { + meth.rename(oracle.fn_name(meth.name())); + } + + for cons in &mut object_item.constructors { + if !cons.is_primary_constructor() { + cons.rename(oracle.fn_name(cons.name())); + } + + } + } + + // Conversions for wrapper.py + //TODO: Renaming the function name in wrapper.py is not currently tested + for func in self.functions.iter_mut() { + func.rename(oracle.fn_name(func.name())); + } } } From 53b2f4c5d4e757fcc91019f97d2163c13b966cab Mon Sep 17 00:00:00 2001 From: Bastian Gruber Date: Wed, 24 Jul 2024 12:02:54 -0300 Subject: [PATCH 40/62] wip --- .../src/bindings/kotlin/templates/Types.kt | 2 +- .../src/bindings/python/gen_python/mod.rs | 8 +-- .../python/templates/ExternalTemplate.py | 2 +- .../src/bindings/python/templates/Types.py | 2 +- .../src/bindings/python/templates/wrapper.py | 2 +- uniffi_bindgen/src/interface/conventions.rs | 58 +++++++++---------- uniffi_meta/src/group.rs | 13 +++-- uniffi_meta/src/types.rs | 55 +++++++++--------- uniffi_udl/src/finder.rs | 3 +- 9 files changed, 71 insertions(+), 74 deletions(-) diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/Types.kt b/uniffi_bindgen/src/bindings/kotlin/templates/Types.kt index c27121b701..cfe0c42a0c 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/Types.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/Types.kt @@ -124,7 +124,7 @@ object NoPointer {%- when Type::Custom { module_path, name, builtin } %} {% include "CustomTypeTemplate.kt" %} -{%- when Type::External { module_path, name, namespace, kind, tagged } %} +{%- when Type::External { module_path, name, import_name, namespace, kind, tagged } %} {% include "ExternalTypeTemplate.kt" %} {%- else %} diff --git a/uniffi_bindgen/src/bindings/python/gen_python/mod.rs b/uniffi_bindgen/src/bindings/python/gen_python/mod.rs index 31c0fdc022..d7eb8fe6a6 100644 --- a/uniffi_bindgen/src/bindings/python/gen_python/mod.rs +++ b/uniffi_bindgen/src/bindings/python/gen_python/mod.rs @@ -300,8 +300,9 @@ pub struct PythonWrapper<'a> { } impl<'a> PythonWrapper<'a> { pub fn new(config: Config, ci: &'a mut ComponentInterface) -> Self { + dbg!("{:#?}", ci.clone()); ci.apply_naming_conventions(PythonCodeOracle); - + dbg!("{:#?}", ci.clone()); let type_renderer = TypeRenderer::new(&config, ci); let type_helper_code = type_renderer.render().unwrap(); let type_imports = type_renderer.imports.into_inner(); @@ -508,11 +509,6 @@ pub mod filters { Ok(as_ct.as_codetype().type_label()) } - /// Get the idiomatic Python rendering of a class name (for enums, records, errors, etc). - pub fn class_name(nm: &str) -> Result { - Ok(PythonCodeOracle.class_name(nm)) - } - /// Get the idiomatic Python rendering of a function name. pub fn fn_name(nm: &str) -> Result { Ok(PythonCodeOracle.fn_name(nm)) diff --git a/uniffi_bindgen/src/bindings/python/templates/ExternalTemplate.py b/uniffi_bindgen/src/bindings/python/templates/ExternalTemplate.py index 6c0cee85ef..fb41d7ab02 100644 --- a/uniffi_bindgen/src/bindings/python/templates/ExternalTemplate.py +++ b/uniffi_bindgen/src/bindings/python/templates/ExternalTemplate.py @@ -3,7 +3,7 @@ # External type {{name}} is in namespace "{{namespace}}", crate {{module_path}} {%- let ffi_converter_name = "_UniffiConverterType{}"|format(name) %} {{ self.add_import_of(module, ffi_converter_name) }} -{{ self.add_import_of(module, name|class_name) }} {#- import the type alias itself -#} +{{ self.add_import_of(module, import_name) }} {#- import the type alias itself -#} {%- let rustbuffer_local_name = "_UniffiRustBuffer{}"|format(name) %} {{ self.add_import_of_as(module, "_UniffiRustBuffer", rustbuffer_local_name) }} diff --git a/uniffi_bindgen/src/bindings/python/templates/Types.py b/uniffi_bindgen/src/bindings/python/templates/Types.py index 29f66fe886..acfd43fae9 100644 --- a/uniffi_bindgen/src/bindings/python/templates/Types.py +++ b/uniffi_bindgen/src/bindings/python/templates/Types.py @@ -91,7 +91,7 @@ {%- when Type::Custom { name, module_path, builtin } %} {%- include "CustomType.py" %} -{%- when Type::External { name, module_path, namespace, kind, tagged } %} +{%- when Type::External { name, import_name, module_path, namespace, kind, tagged } %} {%- include "ExternalTemplate.py" %} {%- else %} diff --git a/uniffi_bindgen/src/bindings/python/templates/wrapper.py b/uniffi_bindgen/src/bindings/python/templates/wrapper.py index 4380041ad8..d88497c313 100644 --- a/uniffi_bindgen/src/bindings/python/templates/wrapper.py +++ b/uniffi_bindgen/src/bindings/python/templates/wrapper.py @@ -73,7 +73,7 @@ "{{ obj|type_name }}", {%- endfor %} {%- for c in ci.callback_interface_definitions() %} - "{{ c.name()|class_name }}", + "{{ c.display() }}", {%- endfor %} ] diff --git a/uniffi_bindgen/src/interface/conventions.rs b/uniffi_bindgen/src/interface/conventions.rs index cc5bf2e27b..b61fbafa69 100644 --- a/uniffi_bindgen/src/interface/conventions.rs +++ b/uniffi_bindgen/src/interface/conventions.rs @@ -1,36 +1,27 @@ use crate::{interface::Record, CodeOracle, ComponentInterface, Renameable}; -use std::collections::BTreeMap; +use std::collections::{BTreeMap, BTreeSet}; +use uniffi_meta::Type; impl ComponentInterface { pub fn apply_naming_conventions(&mut self, oracle: O) { // Applying changes to the TypeUniverse - // for (_, t) in &mut self.types.type_definitions { - // - // if t.name().is_some() { - // t.rename(oracle.var_name(&t.name().unwrap())); - // - // } - // } - // - // let mut known: BTreeSet = BTreeSet::new(); - // - // for t in &mut self.types.all_known_types.iter() { - // let mut ty = t.clone(); - // if t.name().is_some() { - // ty.rename(oracle.external_types_name(&t.name().unwrap())); - // } - // known.insert(ty.clone()); - // } - // - // self.types.all_known_types = known; - // - // for function_item in self.functions.iter_mut() { - // function_item.rename(oracle.fn_name(function_item.name())); - // - // for arg in &mut function_item.arguments { - // arg.rename(oracle.var_name(arg.name())); - // } - // } + for (_, t) in &mut self.types.type_definitions { + if t.name().is_some() { + t.rename(oracle.class_name(&t.name().unwrap())); + } + } + + let mut known: BTreeSet = BTreeSet::new(); + + for t in &mut self.types.all_known_types.iter() { + let mut ty = t.clone(); + if t.name().is_some() { + ty.rename(oracle.class_name(&t.name().unwrap())); + } + known.insert(ty.clone()); + } + + self.types.all_known_types = known; // Conversions for CallbackInterfaceImpl.py for callback_interface in self.callback_interfaces.iter_mut() { @@ -81,14 +72,13 @@ impl ComponentInterface { let mut record = record_item.clone(); // We just want to prefix reserved keywords for the name, without modifying it - record.rename(oracle.external_types_name(record_item.name())); + record.rename(oracle.class_name(record_item.name())); for field in &mut record.fields { field.rename(oracle.var_name(field.name())); } - // We just want to prefix reserved keywords for the name, without modifying it - // new_records.insert(oracle.external_types_name(key), record); + // new_records.insert(oracle.class_name(key), record); new_records.insert(key.to_string(), record); } @@ -106,14 +96,18 @@ impl ComponentInterface { if !cons.is_primary_constructor() { cons.rename(oracle.fn_name(cons.name())); } - } } // Conversions for wrapper.py //TODO: Renaming the function name in wrapper.py is not currently tested + //TODO: Renaming the callback_interface name in wrapper.py is currently not tested for func in self.functions.iter_mut() { func.rename(oracle.fn_name(func.name())); } + + for ci_def in self.callback_interfaces.iter_mut() { + ci_def.rename_display(oracle.class_name(ci_def.name())); + } } } diff --git a/uniffi_meta/src/group.rs b/uniffi_meta/src/group.rs index a41776bf8a..cadb0044e6 100644 --- a/uniffi_meta/src/group.rs +++ b/uniffi_meta/src/group.rs @@ -181,7 +181,8 @@ impl<'a> ExternalTypeConverter<'a> { Type::External { namespace: self.crate_to_namespace(&module_path), module_path, - name, + name: name.clone(), + import_name: name, kind: ExternalKind::DataClass, tagged: false, } @@ -194,7 +195,8 @@ impl<'a> ExternalTypeConverter<'a> { Type::External { namespace: self.crate_to_namespace(&module_path), module_path, - name, + name: name.clone(), + import_name: name, kind: ExternalKind::DataClass, tagged: false, } @@ -204,7 +206,8 @@ impl<'a> ExternalTypeConverter<'a> { } if self.is_module_path_external(&module_path) => Type::External { namespace: self.crate_to_namespace(&module_path), module_path, - name, + name: name.clone(), + import_name: name, kind: ExternalKind::Interface, tagged: false, }, @@ -242,6 +245,7 @@ impl<'a> ExternalTypeConverter<'a> { namespace, module_path, name, + import_name, kind, tagged, } => { @@ -249,7 +253,8 @@ impl<'a> ExternalTypeConverter<'a> { Type::External { namespace: self.crate_to_namespace(&module_path), module_path, - name, + name: name.clone(), + import_name, kind, tagged, } diff --git a/uniffi_meta/src/types.rs b/uniffi_meta/src/types.rs index ee6ecc8ea7..74bc5fdf15 100644 --- a/uniffi_meta/src/types.rs +++ b/uniffi_meta/src/types.rs @@ -116,6 +116,7 @@ pub enum Type { External { module_path: String, name: String, + import_name: String, #[checksum_ignore] // The namespace is not known generating scaffolding. namespace: String, kind: ExternalKind, @@ -172,34 +173,34 @@ impl Type { pub fn rename(&mut self, new_name: String) { match self { - Type::Object { name, .. } => { - *name = new_name; - } - Type::Record { name, .. } => { - *name = new_name; - } - Type::Enum { name, .. } => { - *name = new_name; - } - Type::CallbackInterface { name, .. } => { - *name = new_name; - } - Type::Custom { name, .. } => { - *name = new_name; - } - Type::External { name, .. } => { - *name = new_name; - } - Type::Optional { inner_type } | Type::Sequence { inner_type } => { - if inner_type.name().is_some() { - inner_type.rename(new_name); - } - } - Type::Map { value_type, .. } => { - if value_type.name().is_some() { - value_type.rename(new_name); - } + // Type::Object { name, .. } => { + // *name = new_name; + // } + // Type::Record { name, .. } => { + // *name = new_name; + // } + // Type::Enum { name, .. } => { + // *name = new_name; + // } + // Type::CallbackInterface { name, .. } => { + // *name = new_name; + // } + // Type::Custom { name, .. } => { + // *name = new_name; + // } + Type::External { import_name, .. } => { + *import_name = new_name; } + // Type::Optional { inner_type } | Type::Sequence { inner_type } => { + // if inner_type.name().is_some() { + // inner_type.rename(new_name); + // } + // } + // Type::Map { value_type, .. } => { + // if value_type.name().is_some() { + // value_type.rename(new_name); + // } + // } _ => {} } } diff --git a/uniffi_udl/src/finder.rs b/uniffi_udl/src/finder.rs index 259557ad07..c1e12643a5 100644 --- a/uniffi_udl/src/finder.rs +++ b/uniffi_udl/src/finder.rs @@ -155,7 +155,8 @@ impl TypeFinder for weedle::TypedefDefinition<'_> { let kind = attrs.external_kind().expect("External missing kind"); let tagged = attrs.external_tagged().expect("External missing tagged"); Type::External { - name, + name: name.clone(), + import_name: name, namespace: "".to_string(), // we don't know this yet module_path: attrs.get_crate_name(), kind, From 3f2671b16b611c014e679c025d7049f96a3032ed Mon Sep 17 00:00:00 2001 From: Bastian Gruber Date: Wed, 24 Jul 2024 12:21:43 -0300 Subject: [PATCH 41/62] fix: clippy --- uniffi_bindgen/src/interface/conventions.rs | 2 +- uniffi_meta/src/types.rs | 32 ++------------------- 2 files changed, 3 insertions(+), 31 deletions(-) diff --git a/uniffi_bindgen/src/interface/conventions.rs b/uniffi_bindgen/src/interface/conventions.rs index b61fbafa69..7e48c11300 100644 --- a/uniffi_bindgen/src/interface/conventions.rs +++ b/uniffi_bindgen/src/interface/conventions.rs @@ -5,7 +5,7 @@ use uniffi_meta::Type; impl ComponentInterface { pub fn apply_naming_conventions(&mut self, oracle: O) { // Applying changes to the TypeUniverse - for (_, t) in &mut self.types.type_definitions { + for t in &mut self.types.type_definitions.values_mut() { if t.name().is_some() { t.rename(oracle.class_name(&t.name().unwrap())); } diff --git a/uniffi_meta/src/types.rs b/uniffi_meta/src/types.rs index 74bc5fdf15..9719b41c15 100644 --- a/uniffi_meta/src/types.rs +++ b/uniffi_meta/src/types.rs @@ -172,36 +172,8 @@ impl Type { } pub fn rename(&mut self, new_name: String) { - match self { - // Type::Object { name, .. } => { - // *name = new_name; - // } - // Type::Record { name, .. } => { - // *name = new_name; - // } - // Type::Enum { name, .. } => { - // *name = new_name; - // } - // Type::CallbackInterface { name, .. } => { - // *name = new_name; - // } - // Type::Custom { name, .. } => { - // *name = new_name; - // } - Type::External { import_name, .. } => { - *import_name = new_name; - } - // Type::Optional { inner_type } | Type::Sequence { inner_type } => { - // if inner_type.name().is_some() { - // inner_type.rename(new_name); - // } - // } - // Type::Map { value_type, .. } => { - // if value_type.name().is_some() { - // value_type.rename(new_name); - // } - // } - _ => {} + if let Type::External { import_name, .. } = self { + *import_name = new_name; } } } From 1375b17bd84eb7a44f0d34b5b15f3e6208234e43 Mon Sep 17 00:00:00 2001 From: Bastian Gruber Date: Wed, 24 Jul 2024 14:01:56 -0300 Subject: [PATCH 42/62] feat: replace fn_name and var_name filters --- .../src/bindings/python/gen_python/mod.rs | 10 ------ .../templates/NamespaceLibraryTemplate.py | 2 +- .../src/bindings/python/templates/Protocol.py | 2 +- .../templates/TopLevelFunctionTemplate.py | 8 ++--- .../src/bindings/python/templates/macros.py | 16 ++++----- uniffi_bindgen/src/interface/conventions.rs | 36 ++++++++++++++++++- uniffi_bindgen/src/interface/ffi.rs | 4 +++ uniffi_meta/src/types.rs | 2 +- 8 files changed, 54 insertions(+), 26 deletions(-) diff --git a/uniffi_bindgen/src/bindings/python/gen_python/mod.rs b/uniffi_bindgen/src/bindings/python/gen_python/mod.rs index d7eb8fe6a6..6cddd668dc 100644 --- a/uniffi_bindgen/src/bindings/python/gen_python/mod.rs +++ b/uniffi_bindgen/src/bindings/python/gen_python/mod.rs @@ -509,16 +509,6 @@ pub mod filters { Ok(as_ct.as_codetype().type_label()) } - /// Get the idiomatic Python rendering of a function name. - pub fn fn_name(nm: &str) -> Result { - Ok(PythonCodeOracle.fn_name(nm)) - } - - /// Get the idiomatic Python rendering of a variable name. - pub fn var_name(nm: &str) -> Result { - Ok(PythonCodeOracle.var_name(nm)) - } - pub(super) fn ffi_converter_name(as_ct: &impl AsCodeType) -> Result { Ok(String::from("_Uniffi") + &as_ct.as_codetype().ffi_converter_name()[3..]) } diff --git a/uniffi_bindgen/src/bindings/python/templates/NamespaceLibraryTemplate.py b/uniffi_bindgen/src/bindings/python/templates/NamespaceLibraryTemplate.py index f35c3a766d..e5d0eba423 100644 --- a/uniffi_bindgen/src/bindings/python/templates/NamespaceLibraryTemplate.py +++ b/uniffi_bindgen/src/bindings/python/templates/NamespaceLibraryTemplate.py @@ -76,7 +76,7 @@ def _uniffi_check_api_checksums(lib): class {{ ffi_struct.name()|ffi_struct_name }}(ctypes.Structure): _fields_ = [ {%- for field in ffi_struct.fields() %} - ("{{ field.name()|var_name }}", {{ field.type_().borrow()|ffi_type_name }}), + ("{{ field.name() }}", {{ field.type_().borrow()|ffi_type_name }}), {%- endfor %} ] {%- when FfiDefinition::Function(func) %} diff --git a/uniffi_bindgen/src/bindings/python/templates/Protocol.py b/uniffi_bindgen/src/bindings/python/templates/Protocol.py index 3b7e93596a..8e5012ddd4 100644 --- a/uniffi_bindgen/src/bindings/python/templates/Protocol.py +++ b/uniffi_bindgen/src/bindings/python/templates/Protocol.py @@ -1,7 +1,7 @@ class {{ protocol_name }}(typing.Protocol): {%- call py::docstring_value(protocol_docstring, 4) %} {%- for meth in methods.iter() %} - def {{ meth.name()|fn_name }}(self, {% call py::arg_list_decl(meth) %}): + def {{ meth.name() }}(self, {% call py::arg_list_decl(meth) %}): {%- call py::docstring(meth, 8) %} raise NotImplementedError {%- else %} diff --git a/uniffi_bindgen/src/bindings/python/templates/TopLevelFunctionTemplate.py b/uniffi_bindgen/src/bindings/python/templates/TopLevelFunctionTemplate.py index 230b9e853f..7a6cb6d7c2 100644 --- a/uniffi_bindgen/src/bindings/python/templates/TopLevelFunctionTemplate.py +++ b/uniffi_bindgen/src/bindings/python/templates/TopLevelFunctionTemplate.py @@ -2,9 +2,9 @@ {%- match func.return_type() -%} {%- when Some with (return_type) %} -async def {{ func.name()|fn_name }}({%- call py::arg_list_decl(func) -%}) -> "{{ return_type|type_name }}": +async def {{ func.name() }}({%- call py::arg_list_decl(func) -%}) -> "{{ return_type|type_name }}": {% when None %} -async def {{ func.name()|fn_name }}({%- call py::arg_list_decl(func) -%}) -> None: +async def {{ func.name() }}({%- call py::arg_list_decl(func) -%}) -> None: {% endmatch %} {%- call py::docstring(func, 4) %} @@ -28,13 +28,13 @@ async def {{ func.name()|fn_name }}({%- call py::arg_list_decl(func) -%}) -> Non {%- match func.return_type() -%} {%- when Some with (return_type) %} -def {{ func.name()|fn_name }}({%- call py::arg_list_decl(func) -%}) -> "{{ return_type|type_name }}": +def {{ func.name() }}({%- call py::arg_list_decl(func) -%}) -> "{{ return_type|type_name }}": {%- call py::docstring(func, 4) %} {%- call py::setup_args(func) %} return {{ return_type|lift_fn }}({% call py::to_ffi_call(func) %}) {% when None %} -def {{ func.name()|fn_name }}({%- call py::arg_list_decl(func) -%}) -> None: +def {{ func.name() }}({%- call py::arg_list_decl(func) -%}) -> None: {%- call py::docstring(func, 4) %} {%- call py::setup_args(func) %} {% call py::to_ffi_call(func) %} diff --git a/uniffi_bindgen/src/bindings/python/templates/macros.py b/uniffi_bindgen/src/bindings/python/templates/macros.py index 0f4d3a413b..1a47226bd0 100644 --- a/uniffi_bindgen/src/bindings/python/templates/macros.py +++ b/uniffi_bindgen/src/bindings/python/templates/macros.py @@ -34,7 +34,7 @@ {%- macro arg_list_lowered(func) %} {%- for arg in func.arguments() %} - {{ arg|lower_fn }}({{ arg.name()|var_name }}) + {{ arg|lower_fn }}({{ arg.name() }}) {%- if !loop.last %},{% endif %} {%- endfor %} {%- endmacro -%} @@ -59,7 +59,7 @@ {% macro arg_list_decl(func) %} {%- for arg in func.arguments() -%} - {{ arg.name()|var_name }} + {{ arg.name() }} {%- match arg.default_value() %} {%- when Some with(literal) %}: "typing.Union[object, {{ arg|type_name -}}]" = _DEFAULT {%- else %}: "{{ arg|type_name -}}" @@ -88,10 +88,10 @@ {%- match arg.default_value() %} {%- when None %} {%- when Some with(literal) %} - if {{ arg.name()|var_name }} is _DEFAULT: - {{ arg.name()|var_name }} = {{ literal|literal_py(arg.as_type().borrow()) }} + if {{ arg.name() }} is _DEFAULT: + {{ arg.name() }} = {{ literal|literal_py(arg.as_type().borrow()) }} {%- endmatch %} - {{ arg|check_lower_fn }}({{ arg.name()|var_name }}) + {{ arg|check_lower_fn }}({{ arg.name() }}) {% endfor -%} {%- endmacro -%} @@ -104,10 +104,10 @@ {%- match arg.default_value() %} {%- when None %} {%- when Some with(literal) %} - if {{ arg.name()|var_name }} is _DEFAULT: - {{ arg.name()|var_name }} = {{ literal|literal_py(arg.as_type().borrow()) }} + if {{ arg.name() }} is _DEFAULT: + {{ arg.name() }} = {{ literal|literal_py(arg.as_type().borrow()) }} {%- endmatch %} - {{ arg|check_lower_fn }}({{ arg.name()|var_name }}) + {{ arg|check_lower_fn }}({{ arg.name() }}) {% endfor -%} {%- endmacro -%} diff --git a/uniffi_bindgen/src/interface/conventions.rs b/uniffi_bindgen/src/interface/conventions.rs index 7e48c11300..ec3aa12b61 100644 --- a/uniffi_bindgen/src/interface/conventions.rs +++ b/uniffi_bindgen/src/interface/conventions.rs @@ -1,4 +1,7 @@ -use crate::{interface::Record, CodeOracle, ComponentInterface, Renameable}; +use crate::{ + interface::{FfiDefinition, Record}, + CodeOracle, ComponentInterface, Renameable, +}; use std::collections::{BTreeMap, BTreeSet}; use uniffi_meta::Type; @@ -88,14 +91,24 @@ impl ComponentInterface { // Conversions for ObjectTemplate.py for object_item in self.objects.iter_mut() { + // object_item.rename(oracle.class_name(object_item.name())); for meth in &mut object_item.methods { meth.rename(oracle.fn_name(meth.name())); + + for arg in meth.arguments.iter_mut() { + arg.rename(oracle.var_name(arg.name())); + } } for cons in &mut object_item.constructors { if !cons.is_primary_constructor() { cons.rename(oracle.fn_name(cons.name())); } + + // For macros.py + for arg in cons.arguments.iter_mut() { + arg.rename(oracle.var_name(arg.name())); + } } } @@ -109,5 +122,26 @@ impl ComponentInterface { for ci_def in self.callback_interfaces.iter_mut() { ci_def.rename_display(oracle.class_name(ci_def.name())); } + + // Applying the var_name filter to function arguments, + for func in self.functions.iter_mut() { + for arg in func.arguments.iter_mut() { + arg.rename(oracle.var_name(arg.name())); + } + } + + //TODO: Renaming the fields for a FfiStruct is currently not being tested + // Replace var_name filter for NamespaceLibraryTemplate.py + for def in self.ffi_definitions() { + match def { + FfiDefinition::Function(_) => {} + FfiDefinition::CallbackFunction(_) => {} + FfiDefinition::Struct(mut ffi_struct) => { + for field in ffi_struct.fields.iter_mut() { + field.rename(oracle.var_name(field.name())); + } + } + } + } } } diff --git a/uniffi_bindgen/src/interface/ffi.rs b/uniffi_bindgen/src/interface/ffi.rs index e103cc2a42..86b7a785ff 100644 --- a/uniffi_bindgen/src/interface/ffi.rs +++ b/uniffi_bindgen/src/interface/ffi.rs @@ -353,6 +353,10 @@ impl FfiStruct { &self.name } + pub fn rename(&mut self, new_name: String) { + self.name = new_name; + } + /// Get the fields for this struct pub fn fields(&self) -> &[FfiField] { &self.fields diff --git a/uniffi_meta/src/types.rs b/uniffi_meta/src/types.rs index 9719b41c15..12460ca313 100644 --- a/uniffi_meta/src/types.rs +++ b/uniffi_meta/src/types.rs @@ -173,7 +173,7 @@ impl Type { pub fn rename(&mut self, new_name: String) { if let Type::External { import_name, .. } = self { - *import_name = new_name; + *import_name = new_name } } } From 0f7c5601fb8a665170a541692263c17d5c333965 Mon Sep 17 00:00:00 2001 From: Bastian Gruber Date: Tue, 30 Jul 2024 13:36:58 -0300 Subject: [PATCH 43/62] feat: rework the visitor trait --- .../python/gen_python/callback_interface.rs | 1 - .../src/bindings/python/gen_python/custom.rs | 1 - .../src/bindings/python/gen_python/enum_.rs | 1 - .../bindings/python/gen_python/external.rs | 1 - .../src/bindings/python/gen_python/mod.rs | 182 ++++++++++++++++-- .../src/bindings/python/gen_python/object.rs | 1 - .../src/bindings/python/gen_python/record.rs | 1 - uniffi_bindgen/src/bindings/python/mod.rs | 5 +- .../bindings/python/templates/EnumTemplate.py | 10 +- .../src/bindings/python/templates/wrapper.py | 2 +- uniffi_bindgen/src/interface/callbacks.rs | 14 +- uniffi_bindgen/src/interface/conventions.rs | 152 +-------------- uniffi_bindgen/src/interface/enum_.rs | 15 +- uniffi_bindgen/src/interface/ffi.rs | 2 +- uniffi_bindgen/src/interface/function.rs | 2 +- uniffi_bindgen/src/interface/mod.rs | 16 +- uniffi_bindgen/src/interface/object.rs | 51 ++--- uniffi_bindgen/src/interface/record.rs | 2 +- uniffi_bindgen/src/lib.rs | 79 ++++---- 19 files changed, 253 insertions(+), 285 deletions(-) diff --git a/uniffi_bindgen/src/bindings/python/gen_python/callback_interface.rs b/uniffi_bindgen/src/bindings/python/gen_python/callback_interface.rs index 55a2096a4a..9c93965e35 100644 --- a/uniffi_bindgen/src/bindings/python/gen_python/callback_interface.rs +++ b/uniffi_bindgen/src/bindings/python/gen_python/callback_interface.rs @@ -4,7 +4,6 @@ use super::CodeType; use crate::backend::Literal; -use crate::CodeOracle; #[derive(Debug)] pub struct CallbackInterfaceCodeType { diff --git a/uniffi_bindgen/src/bindings/python/gen_python/custom.rs b/uniffi_bindgen/src/bindings/python/gen_python/custom.rs index 5e05832cc7..0818e7198b 100644 --- a/uniffi_bindgen/src/bindings/python/gen_python/custom.rs +++ b/uniffi_bindgen/src/bindings/python/gen_python/custom.rs @@ -3,7 +3,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use super::CodeType; -use crate::CodeOracle; #[derive(Debug)] pub struct CustomCodeType { diff --git a/uniffi_bindgen/src/bindings/python/gen_python/enum_.rs b/uniffi_bindgen/src/bindings/python/gen_python/enum_.rs index 7ba7150404..83ce177e07 100644 --- a/uniffi_bindgen/src/bindings/python/gen_python/enum_.rs +++ b/uniffi_bindgen/src/bindings/python/gen_python/enum_.rs @@ -4,7 +4,6 @@ use super::CodeType; use crate::backend::Literal; -use crate::CodeOracle; #[derive(Debug)] pub struct EnumCodeType { diff --git a/uniffi_bindgen/src/bindings/python/gen_python/external.rs b/uniffi_bindgen/src/bindings/python/gen_python/external.rs index 519fb16d4f..0a46251d6d 100644 --- a/uniffi_bindgen/src/bindings/python/gen_python/external.rs +++ b/uniffi_bindgen/src/bindings/python/gen_python/external.rs @@ -3,7 +3,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use super::CodeType; -use crate::CodeOracle; #[derive(Debug)] pub struct ExternalCodeType { diff --git a/uniffi_bindgen/src/bindings/python/gen_python/mod.rs b/uniffi_bindgen/src/bindings/python/gen_python/mod.rs index 6cddd668dc..6ca42329a0 100644 --- a/uniffi_bindgen/src/bindings/python/gen_python/mod.rs +++ b/uniffi_bindgen/src/bindings/python/gen_python/mod.rs @@ -10,13 +10,13 @@ use once_cell::sync::Lazy; use serde::{Deserialize, Serialize}; use std::borrow::Borrow; use std::cell::RefCell; -use std::collections::{BTreeSet, HashMap, HashSet}; +use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; use std::fmt::Debug; use crate::backend::TemplateExpression; -use crate::CodeOracle; use crate::interface::*; +use crate::VisitMut; mod callback_interface; mod compounds; @@ -300,9 +300,8 @@ pub struct PythonWrapper<'a> { } impl<'a> PythonWrapper<'a> { pub fn new(config: Config, ci: &'a mut ComponentInterface) -> Self { - dbg!("{:#?}", ci.clone()); - ci.apply_naming_conventions(PythonCodeOracle); - dbg!("{:#?}", ci.clone()); + ci.visit_mut(&PythonCodeOracle); + let type_renderer = TypeRenderer::new(&config, ci); let type_helper_code = type_renderer.render().unwrap(); let type_imports = type_renderer.imports.into_inner(); @@ -335,18 +334,12 @@ impl PythonCodeOracle { fn find(&self, type_: &Type) -> Box { type_.clone().as_type().as_codetype() } -} -impl CodeOracle for PythonCodeOracle { /// Get the idiomatic Python rendering of a class name (for enums, records, errors, etc). fn class_name(&self, nm: &str) -> String { fixup_keyword(nm.to_string().to_upper_camel_case()) } - fn external_types_name(&self, nm: &str) -> String { - fixup_keyword(nm.to_string()) - } - /// Get the idiomatic Python rendering of a function name. fn fn_name(&self, nm: &str) -> String { fixup_keyword(nm.to_string().to_snake_case()) @@ -448,6 +441,159 @@ impl CodeOracle for PythonCodeOracle { } } +impl VisitMut for PythonCodeOracle { + fn visit_record(&self, ci: &mut ComponentInterface) { + // Conversions for RecordTemplate.py + let mut new_records: BTreeMap = BTreeMap::new(); + + for (key, record_item) in ci.records.iter_mut() { + let mut record = record_item.clone(); + + // We just want to prefix reserved keywords for the name, without modifying it + record.rename(self.class_name(record_item.name())); + + for field in &mut record.fields { + field.rename(self.var_name(field.name())); + } + + // new_records.insert(oracle.class_name(key), record); + new_records.insert(key.to_string(), record); + } + + // One cannot alter a BTreeMap in place (with a few hacks maybe...), so we create a new one + // with the adjusted names, and replace it. + ci.records = new_records; + } + + fn visit_enum(&self, ci: &mut ComponentInterface) { + // Conversions for EnumTemplate.py and ErrorTemplate.py + let errors = ci.errors.clone(); + + for enum_item in ci.enums.values_mut() { + if errors.contains(enum_item.name()) { + enum_item.rename(self.class_name(enum_item.name())); + + for variant in &mut enum_item.variants { + variant.rename(self.class_name(variant.name())); + + for field in &mut variant.fields { + field.rename(self.var_name(field.name())); + } + } + } else { + enum_item.rename(self.enum_variant_name(enum_item.name())); + + for variant in &mut enum_item.variants { + variant.rename(self.enum_variant_name(variant.name())); + + //TODO: If we want to remove the last var_name filter + // in the template, this is it. We need an additional + // attribute for the `Variant` so we can + // display Python is_NAME functions + // variant.set_is_name(self.var_name(variant.name())); + + for field in &mut variant.fields { + field.rename(self.var_name(field.name())); + } + } + } + } + } + + fn visit_type(&self, ci: &mut ComponentInterface) { + // Applying changes to the TypeUniverse + for t in &mut ci.types.type_definitions.values_mut() { + if t.name().is_some() { + t.rename(self.class_name(&t.name().unwrap())); + } + } + + let mut known: BTreeSet = BTreeSet::new(); + + for t in &mut ci.types.all_known_types.iter() { + let mut ty = t.clone(); + if t.name().is_some() { + ty.rename(self.class_name(&t.name().unwrap())); + } + known.insert(ty.clone()); + } + + ci.types.all_known_types = known; + } + + fn visit_object(&self, ci: &mut ComponentInterface) { + // Conversions for ObjectTemplate.py + for object_item in ci.objects.iter_mut() { + // object_item.rename(oracle.class_name(object_item.name())); + for meth in &mut object_item.methods { + meth.rename(self.fn_name(meth.name())); + + for arg in meth.arguments.iter_mut() { + arg.rename(self.var_name(arg.name())); + } + } + + for cons in &mut object_item.constructors { + if !cons.is_primary_constructor() { + cons.rename(self.fn_name(cons.name())); + } + + // For macros.py + for arg in cons.arguments.iter_mut() { + arg.rename(self.var_name(arg.name())); + } + } + } + } + + fn visit_function(&self, ci: &mut ComponentInterface) { + // Conversions for wrapper.py + //TODO: Renaming the function name in wrapper.py is not currently tested + //TODO: Renaming the callback_interface name in wrapper.py is currently not tested + for func in ci.functions.iter_mut() { + func.rename(self.fn_name(func.name())); + + for arg in func.arguments.iter_mut() { + arg.rename(self.var_name(arg.name())); + } + } + } + + fn visit_callback_interface(&self, ci: &mut ComponentInterface) { + // Conversions for CallbackInterfaceImpl.py + for callback_interface in ci.callback_interfaces.iter_mut() { + //TODO: To remove the last fn_name filter, we need to add an attribute for + // the `CallbackInterface` so we can alter one specific point in the template + // without altering the name everywhere else. + // callback_interface.rename_display(oracle.fn_name(callback_interface.name())); + + for method in callback_interface.methods.iter_mut() { + method.rename(self.fn_name(method.name())); + + for arg in method.arguments.iter_mut() { + arg.rename(self.var_name(arg.name())); + } + } + } + } + + fn visit_ffi_defitinion(&self, ci: &mut ComponentInterface) { + //TODO: Renaming the fields for a FfiStruct is currently not being tested + // Replace var_name filter for NamespaceLibraryTemplate.py + for def in ci.ffi_definitions() { + match def { + FfiDefinition::Function(_) => {} + FfiDefinition::CallbackFunction(_) => {} + FfiDefinition::Struct(mut ffi_struct) => { + for field in ffi_struct.fields.iter_mut() { + field.rename(self.var_name(field.name())); + } + } + } + } + } +} + trait AsCodeType { fn as_codetype(&self) -> Box; } @@ -509,6 +655,20 @@ pub mod filters { Ok(as_ct.as_codetype().type_label()) } + //TODO: Remove. Currently just being used by EnumTemplate.py to + // display is_NAME_OF_ENUM. + /// Get the idiomatic Python rendering of a variable name. + pub fn var_name(nm: &str) -> Result { + Ok(PythonCodeOracle.var_name(nm)) + } + + //TODO: Remove. Currently just being used by wrapper.py to display the + // callback_interface function names. + /// Get the idiomatic Python rendering of a class name (for enums, records, errors, etc). + pub fn class_name(nm: &str) -> Result { + Ok(PythonCodeOracle.class_name(nm)) + } + pub(super) fn ffi_converter_name(as_ct: &impl AsCodeType) -> Result { Ok(String::from("_Uniffi") + &as_ct.as_codetype().ffi_converter_name()[3..]) } diff --git a/uniffi_bindgen/src/bindings/python/gen_python/object.rs b/uniffi_bindgen/src/bindings/python/gen_python/object.rs index 31667ad31d..1165bb0e54 100644 --- a/uniffi_bindgen/src/bindings/python/gen_python/object.rs +++ b/uniffi_bindgen/src/bindings/python/gen_python/object.rs @@ -4,7 +4,6 @@ use super::CodeType; use crate::backend::Literal; -use crate::CodeOracle; #[derive(Debug)] pub struct ObjectCodeType { diff --git a/uniffi_bindgen/src/bindings/python/gen_python/record.rs b/uniffi_bindgen/src/bindings/python/gen_python/record.rs index a395442b73..df00f98e8b 100644 --- a/uniffi_bindgen/src/bindings/python/gen_python/record.rs +++ b/uniffi_bindgen/src/bindings/python/gen_python/record.rs @@ -4,7 +4,6 @@ use super::CodeType; use crate::backend::Literal; -use crate::CodeOracle; #[derive(Debug)] pub struct RecordCodeType { diff --git a/uniffi_bindgen/src/bindings/python/mod.rs b/uniffi_bindgen/src/bindings/python/mod.rs index 66c3965f68..ca598baf56 100644 --- a/uniffi_bindgen/src/bindings/python/mod.rs +++ b/uniffi_bindgen/src/bindings/python/mod.rs @@ -10,12 +10,13 @@ use fs_err as fs; mod gen_python; #[cfg(feature = "bindgen-tests")] pub mod test; -use crate::{Component, GenerationSettings}; +use crate::{BindingGenerator, Component, GenerationSettings}; + use gen_python::{generate_python_bindings, Config}; pub struct PythonBindingGenerator; -impl crate::BindingGenerator for PythonBindingGenerator { +impl BindingGenerator for PythonBindingGenerator { type Config = Config; fn new_config(&self, root_toml: &toml::Value) -> Result { diff --git a/uniffi_bindgen/src/bindings/python/templates/EnumTemplate.py b/uniffi_bindgen/src/bindings/python/templates/EnumTemplate.py index cb7931443a..cfbf972603 100644 --- a/uniffi_bindgen/src/bindings/python/templates/EnumTemplate.py +++ b/uniffi_bindgen/src/bindings/python/templates/EnumTemplate.py @@ -41,7 +41,7 @@ def __str__(self): return f"{{ type_name }}.{{ variant.name() }}{self._values!r}" def __eq__(self, other): - if not other.is_{{ variant.is_name() }}(): + if not other.is_{{ variant.name()|var_name }}(): return False return self._values == other._values @@ -64,7 +64,7 @@ def __str__(self): return "{{ type_name }}.{{ variant.name() }}({% for field in variant.fields() %}{{ field.name() }}={}{% if loop.last %}{% else %}, {% endif %}{% endfor %})".format({% for field in variant.fields() %}self.{{ field.name() }}{% if loop.last %}{% else %}, {% endif %}{% endfor %}) def __eq__(self, other): - if not other.is_{{ variant.is_name() }}(): + if not other.is_{{ variant.name()|var_name }}(): return False {%- for field in variant.fields() %} if self.{{ field.name() }} != other.{{ field.name() }}: @@ -77,7 +77,7 @@ def __eq__(self, other): # For each variant, we have an `is_NAME` method for easily checking # whether an instance is that variant. {% for variant in e.variants() -%} - def is_{{ variant.is_name() }}(self) -> bool: + def is_{{ variant.name()|var_name }}(self) -> bool: return isinstance(self, {{ type_name }}.{{ variant.name() }}) {% endfor %} @@ -118,7 +118,7 @@ def check_lower(value): {%- if e.is_flat() %} if value == {{ type_name }}.{{ variant.name() }}: {%- else %} - if value.is_{{ variant.is_name() }}(): + if value.is_{{ variant.name()|var_name }}(): {%- endif %} {%- for field in variant.fields() %} {%- if variant.has_nameless_fields() %} @@ -139,7 +139,7 @@ def write(value, buf): if value == {{ type_name }}.{{ variant.name() }}: buf.write_i32({{ loop.index }}) {%- else %} - if value.is_{{ variant.is_name() }}(): + if value.is_{{ variant.name()|var_name }}(): buf.write_i32({{ loop.index }}) {%- for field in variant.fields() %} {%- if variant.has_nameless_fields() %} diff --git a/uniffi_bindgen/src/bindings/python/templates/wrapper.py b/uniffi_bindgen/src/bindings/python/templates/wrapper.py index d88497c313..e559120d84 100644 --- a/uniffi_bindgen/src/bindings/python/templates/wrapper.py +++ b/uniffi_bindgen/src/bindings/python/templates/wrapper.py @@ -73,7 +73,7 @@ "{{ obj|type_name }}", {%- endfor %} {%- for c in ci.callback_interface_definitions() %} - "{{ c.display() }}", + "{{ c.name()|class_name }}", {%- endfor %} ] diff --git a/uniffi_bindgen/src/interface/callbacks.rs b/uniffi_bindgen/src/interface/callbacks.rs index b7ba05624c..14886d9b02 100644 --- a/uniffi_bindgen/src/interface/callbacks.rs +++ b/uniffi_bindgen/src/interface/callbacks.rs @@ -45,9 +45,8 @@ use super::{AsType, Type, TypeIterator}; #[derive(Debug, Clone, Checksum)] pub struct CallbackInterface { pub(super) name: String, - pub(super) display: String, pub(super) module_path: String, - pub(super) methods: Vec, + pub(crate) methods: Vec, // We don't include the FFIFunc in the hash calculation, because: // - it is entirely determined by the other fields, // so excluding it is safe. @@ -65,14 +64,6 @@ impl CallbackInterface { &self.name } - pub fn display(&self) -> &str { - &self.display - } - - pub fn rename_display(&mut self, new_name: String) { - self.display = new_name; - } - pub fn methods(&self) -> Vec<&Method> { self.methods.iter().collect() } @@ -137,8 +128,7 @@ impl TryFrom for CallbackInterface { fn try_from(meta: uniffi_meta::CallbackInterfaceMetadata) -> anyhow::Result { Ok(Self { - name: meta.name.clone(), - display: meta.name, + name: meta.name, module_path: meta.module_path, methods: Default::default(), ffi_init_callback: Default::default(), diff --git a/uniffi_bindgen/src/interface/conventions.rs b/uniffi_bindgen/src/interface/conventions.rs index ec3aa12b61..5b79afa214 100644 --- a/uniffi_bindgen/src/interface/conventions.rs +++ b/uniffi_bindgen/src/interface/conventions.rs @@ -1,147 +1,13 @@ -use crate::{ - interface::{FfiDefinition, Record}, - CodeOracle, ComponentInterface, Renameable, -}; -use std::collections::{BTreeMap, BTreeSet}; -use uniffi_meta::Type; +use crate::{ComponentInterface, VisitMut}; impl ComponentInterface { - pub fn apply_naming_conventions(&mut self, oracle: O) { - // Applying changes to the TypeUniverse - for t in &mut self.types.type_definitions.values_mut() { - if t.name().is_some() { - t.rename(oracle.class_name(&t.name().unwrap())); - } - } - - let mut known: BTreeSet = BTreeSet::new(); - - for t in &mut self.types.all_known_types.iter() { - let mut ty = t.clone(); - if t.name().is_some() { - ty.rename(oracle.class_name(&t.name().unwrap())); - } - known.insert(ty.clone()); - } - - self.types.all_known_types = known; - - // Conversions for CallbackInterfaceImpl.py - for callback_interface in self.callback_interfaces.iter_mut() { - // callback_interface.rename_display(oracle.fn_name(callback_interface.name())); - - for method in callback_interface.methods.iter_mut() { - method.rename(oracle.fn_name(method.name())); - - for arg in method.arguments.iter_mut() { - arg.rename(oracle.var_name(arg.name())); - } - } - } - - // Conversions for EnumTemplate.py and ErrorTemplate.py - let errors = self.errors.clone(); - - for enum_item in self.enums.values_mut() { - if errors.contains(enum_item.name()) { - enum_item.rename(oracle.class_name(enum_item.name())); - - for variant in &mut enum_item.variants { - variant.rename(oracle.class_name(variant.name())); - - for field in &mut variant.fields { - field.rename(oracle.var_name(field.name())); - } - } - } else { - enum_item.rename(oracle.enum_variant_name(enum_item.name())); - - for variant in &mut enum_item.variants { - variant.rename(oracle.enum_variant_name(variant.name())); - // The template - variant.set_is_name(oracle.var_name(variant.name())); - - for field in &mut variant.fields { - field.rename(oracle.var_name(field.name())); - } - } - } - } - - // Conversions for RecordTemplate.py - let mut new_records: BTreeMap = BTreeMap::new(); - - for (key, record_item) in self.records.iter_mut() { - let mut record = record_item.clone(); - - // We just want to prefix reserved keywords for the name, without modifying it - record.rename(oracle.class_name(record_item.name())); - - for field in &mut record.fields { - field.rename(oracle.var_name(field.name())); - } - - // new_records.insert(oracle.class_name(key), record); - new_records.insert(key.to_string(), record); - } - - // One cannot alter a BTreeMap in place (with a few hacks maybe...), so we create a new one - // with the adjusted names, and replace it. - self.records = new_records; - - // Conversions for ObjectTemplate.py - for object_item in self.objects.iter_mut() { - // object_item.rename(oracle.class_name(object_item.name())); - for meth in &mut object_item.methods { - meth.rename(oracle.fn_name(meth.name())); - - for arg in meth.arguments.iter_mut() { - arg.rename(oracle.var_name(arg.name())); - } - } - - for cons in &mut object_item.constructors { - if !cons.is_primary_constructor() { - cons.rename(oracle.fn_name(cons.name())); - } - - // For macros.py - for arg in cons.arguments.iter_mut() { - arg.rename(oracle.var_name(arg.name())); - } - } - } - - // Conversions for wrapper.py - //TODO: Renaming the function name in wrapper.py is not currently tested - //TODO: Renaming the callback_interface name in wrapper.py is currently not tested - for func in self.functions.iter_mut() { - func.rename(oracle.fn_name(func.name())); - } - - for ci_def in self.callback_interfaces.iter_mut() { - ci_def.rename_display(oracle.class_name(ci_def.name())); - } - - // Applying the var_name filter to function arguments, - for func in self.functions.iter_mut() { - for arg in func.arguments.iter_mut() { - arg.rename(oracle.var_name(arg.name())); - } - } - - //TODO: Renaming the fields for a FfiStruct is currently not being tested - // Replace var_name filter for NamespaceLibraryTemplate.py - for def in self.ffi_definitions() { - match def { - FfiDefinition::Function(_) => {} - FfiDefinition::CallbackFunction(_) => {} - FfiDefinition::Struct(mut ffi_struct) => { - for field in ffi_struct.fields.iter_mut() { - field.rename(oracle.var_name(field.name())); - } - } - } - } + pub fn visit_mut(&mut self, visitor: &V) { + visitor.visit_record(self); + visitor.visit_enum(self); + visitor.visit_type(self); + visitor.visit_object(self); + visitor.visit_function(self); + visitor.visit_callback_interface(self); + visitor.visit_ffi_defitinion(self); } } diff --git a/uniffi_bindgen/src/interface/enum_.rs b/uniffi_bindgen/src/interface/enum_.rs index 06009b1651..0a1f7e49cd 100644 --- a/uniffi_bindgen/src/interface/enum_.rs +++ b/uniffi_bindgen/src/interface/enum_.rs @@ -175,7 +175,7 @@ pub struct Enum { pub(super) name: String, pub(super) module_path: String, pub(super) discr_type: Option, - pub(super) variants: Vec, + pub(crate) variants: Vec, pub(super) shape: EnumShape, pub(super) non_exhaustive: bool, #[checksum_ignore] @@ -289,9 +289,8 @@ impl AsType for Enum { #[derive(Debug, Clone, Default, PartialEq, Eq, Checksum)] pub struct Variant { pub(super) name: String, - pub(super) is_name: String, pub(super) discr: Option, - pub(super) fields: Vec, + pub(crate) fields: Vec, #[checksum_ignore] pub(super) docstring: Option, } @@ -305,14 +304,6 @@ impl Variant { self.name = new_name; } - pub fn is_name(&self) -> &str { - &self.is_name - } - - pub fn set_is_name(&mut self, name: String) { - self.is_name = name; - } - pub fn fields(&self) -> &[Field] { &self.fields } @@ -340,7 +331,6 @@ impl TryFrom for Variant { fn try_from(meta: uniffi_meta::VariantMetadata) -> Result { Ok(Self { name: meta.name.clone(), - is_name: meta.name, discr: meta.discr, fields: meta .fields @@ -650,7 +640,6 @@ mod test { fn variant(val: Option) -> Variant { Variant { name: "v".to_string(), - is_name: "v".to_string(), discr: val.map(Literal::new_uint), fields: vec![], docstring: None, diff --git a/uniffi_bindgen/src/interface/ffi.rs b/uniffi_bindgen/src/interface/ffi.rs index 86b7a785ff..d644647c51 100644 --- a/uniffi_bindgen/src/interface/ffi.rs +++ b/uniffi_bindgen/src/interface/ffi.rs @@ -344,7 +344,7 @@ impl FfiCallbackFunction { #[derive(Debug, Default, Clone)] pub struct FfiStruct { pub(super) name: String, - pub(super) fields: Vec, + pub(crate) fields: Vec, } impl FfiStruct { diff --git a/uniffi_bindgen/src/interface/function.rs b/uniffi_bindgen/src/interface/function.rs index 62212f00b5..850677abe2 100644 --- a/uniffi_bindgen/src/interface/function.rs +++ b/uniffi_bindgen/src/interface/function.rs @@ -49,7 +49,7 @@ pub struct Function { pub(super) name: String, pub(super) module_path: String, pub(super) is_async: bool, - pub(super) arguments: Vec, + pub(crate) arguments: Vec, pub(super) return_type: Option, // We don't include the FFIFunc in the hash calculation, because: // - it is entirely determined by the other fields, diff --git a/uniffi_bindgen/src/interface/mod.rs b/uniffi_bindgen/src/interface/mod.rs index 78c88a2a09..d8c0896952 100644 --- a/uniffi_bindgen/src/interface/mod.rs +++ b/uniffi_bindgen/src/interface/mod.rs @@ -87,13 +87,13 @@ pub struct ComponentInterface { // anyway, so it's safe to ignore it. pub(super) types: TypeUniverse, /// The high-level API provided by the component. - enums: BTreeMap, - records: BTreeMap, - functions: Vec, - objects: Vec, - callback_interfaces: Vec, + pub(crate) enums: BTreeMap, + pub(crate) records: BTreeMap, + pub(crate) functions: Vec, + pub(crate) objects: Vec, + pub(crate) callback_interfaces: Vec, // Type names which were seen used as an error. - errors: HashSet, + pub(crate) errors: HashSet, // Types which were seen used as callback interface error. callback_interface_throws_types: BTreeSet, } @@ -1172,14 +1172,12 @@ existing definition: Enum { variants: [ Variant { name: \"one\", - is_name: \"one\", discr: None, fields: [], docstring: None, }, Variant { name: \"two\", - is_name: \"two\", discr: None, fields: [], docstring: None, @@ -1196,14 +1194,12 @@ new definition: Enum { variants: [ Variant { name: \"three\", - is_name: \"three\", discr: None, fields: [], docstring: None, }, Variant { name: \"four\", - is_name: \"four\", discr: None, fields: [], docstring: None, diff --git a/uniffi_bindgen/src/interface/object.rs b/uniffi_bindgen/src/interface/object.rs index c8a7a293f4..8c0dab7502 100644 --- a/uniffi_bindgen/src/interface/object.rs +++ b/uniffi_bindgen/src/interface/object.rs @@ -57,7 +57,6 @@ //! # Ok::<(), anyhow::Error>(()) //! ``` -use crate::Renameable; use anyhow::Result; use uniffi_meta::Checksum; @@ -86,8 +85,8 @@ pub struct Object { /// How this object is implemented in Rust pub(super) imp: ObjectImpl, pub(super) module_path: String, - pub(super) constructors: Vec, - pub(super) methods: Vec, + pub(crate) constructors: Vec, + pub(crate) methods: Vec, // The "trait" methods - they have a (presumably "well known") name, and // a regular method (albeit with a generated name) // XXX - this should really be a HashSet, but not enough transient types support hash to make it worthwhile now. @@ -117,6 +116,10 @@ impl Object { &self.name } + pub fn rename(&mut self, new_name: String) { + self.name = new_name; + } + /// Returns the fully qualified name that should be used by Rust code for this object. /// Includes `r#`, traits get a leading `dyn`. If we ever supported associated types, then /// this would also include them. @@ -292,16 +295,6 @@ impl Object { } } -impl Renameable for Object { - fn name(&self) -> &str { - &self.name - } - - fn rename(&mut self, new_name: String) { - self.name = new_name; - } -} - impl AsType for Object { fn as_type(&self) -> Type { Type::Object { @@ -367,7 +360,7 @@ pub struct Constructor { pub(super) object_name: String, pub(super) object_module_path: String, pub(super) is_async: bool, - pub(super) arguments: Vec, + pub(crate) arguments: Vec, // We don't include the FFIFunc in the hash calculation, because: // - it is entirely determined by the other fields, // so excluding it is safe. @@ -390,6 +383,10 @@ impl Constructor { &self.name } + pub fn rename(&mut self, new_name: String) { + self.name = new_name; + } + pub fn arguments(&self) -> Vec<&Argument> { self.arguments.iter().collect() } @@ -443,16 +440,6 @@ impl Constructor { } } -impl Renameable for Constructor { - fn name(&self) -> &str { - &self.name - } - - fn rename(&mut self, new_name: String) { - self.name = new_name; - } -} - impl From for Constructor { fn from(meta: uniffi_meta::ConstructorMetadata) -> Self { let ffi_name = meta.ffi_symbol_name(); @@ -490,7 +477,7 @@ pub struct Method { pub(super) object_module_path: String, pub(super) is_async: bool, pub(super) object_impl: ObjectImpl, - pub(super) arguments: Vec, + pub(crate) arguments: Vec, pub(super) return_type: Option, // We don't include the FFIFunc in the hash calculation, because: // - it is entirely determined by the other fields, @@ -515,6 +502,10 @@ impl Method { &self.name } + pub fn rename(&mut self, new_name: String) { + self.name = new_name; + } + pub fn is_async(&self) -> bool { self.is_async } @@ -604,16 +595,6 @@ impl Method { } } -impl Renameable for Method { - fn name(&self) -> &str { - &self.name - } - - fn rename(&mut self, new_name: String) { - self.name = new_name; - } -} - impl From for Method { fn from(meta: uniffi_meta::MethodMetadata) -> Self { let ffi_name = meta.ffi_symbol_name(); diff --git a/uniffi_bindgen/src/interface/record.rs b/uniffi_bindgen/src/interface/record.rs index 3af6495c99..bb5d8a4136 100644 --- a/uniffi_bindgen/src/interface/record.rs +++ b/uniffi_bindgen/src/interface/record.rs @@ -59,7 +59,7 @@ use super::{AsType, Type, TypeIterator}; pub struct Record { pub(super) name: String, pub(super) module_path: String, - pub(super) fields: Vec, + pub(crate) fields: Vec, #[checksum_ignore] pub(super) docstring: Option, } diff --git a/uniffi_bindgen/src/lib.rs b/uniffi_bindgen/src/lib.rs index ea80e5f7fe..90a48439c7 100644 --- a/uniffi_bindgen/src/lib.rs +++ b/uniffi_bindgen/src/lib.rs @@ -110,7 +110,7 @@ pub mod scaffolding; #[cfg(feature = "cargo-metadata")] pub mod cargo_metadata; -use crate::interface::{FfiType, Object}; +use crate::interface::Object; pub use interface::ComponentInterface; use scaffolding::RustScaffolding; @@ -161,49 +161,40 @@ pub trait BindingGenerator: Sized { ) -> Result<()>; } -pub trait CodeOracle { - /// Get the idiomatic Python rendering of a class name (for enums, records, errors, etc). - fn class_name(&self, nm: &str) -> String; - - fn external_types_name(&self, nm: &str) -> String; - - /// Get the idiomatic Python rendering of a function name. - fn fn_name(&self, nm: &str) -> String; - - /// Get the idiomatic Python rendering of a variable name. - fn var_name(&self, nm: &str) -> String; - - /// Get the idiomatic Python rendering of an individual enum variant. - fn enum_variant_name(&self, nm: &str) -> String; - - /// Get the idiomatic Python rendering of an FFI callback function name - fn ffi_callback_name(&self, nm: &str) -> String; - - /// Get the idiomatic Python rendering of an FFI struct name - fn ffi_struct_name(&self, nm: &str) -> String; - - fn ffi_type_label(&self, ffi_type: &FfiType) -> String; - - /// Default values for FFI types - /// - /// Used to set a default return value when returning an error - fn ffi_default_value(&self, return_type: Option<&FfiType>) -> String; - - /// Get the name of the protocol and class name for an object. - /// - /// If we support callback interfaces, the protocol name is the object name, and the class name is derived from that. - /// Otherwise, the class name is the object name and the protocol name is derived from that. - /// - /// This split determines what types `FfiConverter.lower()` inputs. If we support callback - /// interfaces, `lower` must lower anything that implements the protocol. If not, then lower - /// only lowers the concrete class. - fn object_names(&self, obj: &Object) -> (String, String); -} - -pub trait Renameable { - fn name(&self) -> &str; - - fn rename(&mut self, new_name: String); +/// A trait to alter language specific type representations. +/// +/// It is meant to be implemented by each language oracle. It takes a +/// ['ComponentInterface'] and uses its own specific language adjustment +/// functions to be able to generate language specific templates. +pub trait VisitMut { + /// Go through each [`Record`] of a [`ComponentInterface`] and + /// adjust it to language specific naming conventions. + fn visit_record(&self, ci: &mut ComponentInterface); + + /// Go through each [`Enum`] of a [`ComponentInterface`] and + /// adjust it to language specific naming conventions. + fn visit_enum(&self, ci: &mut ComponentInterface); + + /// Go through each [`Type`] in the [`TypeUniverse`] of + /// a [`ComponentInterface`] and adjust it to language specific + /// naming conventions. + fn visit_type(&self, ci: &mut ComponentInterface); + + /// Go through each [`Object`] of a [`ComponentInterface`] and + /// adjust it to language specific naming conventions. + fn visit_object(&self, ci: &mut ComponentInterface); + + /// Go through each [`Function`] of a [`ComponentInterface`] and + /// adjust it to language specific naming conventions. + fn visit_function(&self, ci: &mut ComponentInterface); + + /// Go through each [`CallbackInterface`] of a [`ComponentInterface`] and + /// adjust it to language specific naming conventions. + fn visit_callback_interface(&self, ci: &mut ComponentInterface); + + /// Go through each [`FfiDefinition`] of a [`ComponentInterface`] and + /// adjust it to language specific naming conventions. + fn visit_ffi_defitinion(&self, ci: &mut ComponentInterface); } /// Everything needed to generate a ComponentInterface. From 06a0d3ab97ce27ab4b44b876858fea729350c840 Mon Sep 17 00:00:00 2001 From: Bastian Gruber Date: Mon, 29 Jul 2024 09:53:50 -0300 Subject: [PATCH 44/62] fix: linting --- uniffi_bindgen/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uniffi_bindgen/src/lib.rs b/uniffi_bindgen/src/lib.rs index 90a48439c7..2c6d826ae7 100644 --- a/uniffi_bindgen/src/lib.rs +++ b/uniffi_bindgen/src/lib.rs @@ -192,7 +192,7 @@ pub trait VisitMut { /// adjust it to language specific naming conventions. fn visit_callback_interface(&self, ci: &mut ComponentInterface); - /// Go through each [`FfiDefinition`] of a [`ComponentInterface`] and + /// Go through each FfiDefinition of a [`ComponentInterface`] and /// adjust it to language specific naming conventions. fn visit_ffi_defitinion(&self, ci: &mut ComponentInterface); } From b737d4beaf757506d96a7f1f8487d53eaa24e8f7 Mon Sep 17 00:00:00 2001 From: Bastian Gruber Date: Mon, 29 Jul 2024 10:04:28 -0300 Subject: [PATCH 45/62] fix: cargo docs --- uniffi_bindgen/src/lib.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/uniffi_bindgen/src/lib.rs b/uniffi_bindgen/src/lib.rs index 2c6d826ae7..565499041a 100644 --- a/uniffi_bindgen/src/lib.rs +++ b/uniffi_bindgen/src/lib.rs @@ -167,28 +167,28 @@ pub trait BindingGenerator: Sized { /// ['ComponentInterface'] and uses its own specific language adjustment /// functions to be able to generate language specific templates. pub trait VisitMut { - /// Go through each [`Record`] of a [`ComponentInterface`] and + /// Go through each `Record` of a [`ComponentInterface`] and /// adjust it to language specific naming conventions. fn visit_record(&self, ci: &mut ComponentInterface); - /// Go through each [`Enum`] of a [`ComponentInterface`] and + /// Go through each `Enum` of a [`ComponentInterface`] and /// adjust it to language specific naming conventions. fn visit_enum(&self, ci: &mut ComponentInterface); - /// Go through each [`Type`] in the [`TypeUniverse`] of + /// Go through each `Type` in the `TypeUniverse` of /// a [`ComponentInterface`] and adjust it to language specific /// naming conventions. fn visit_type(&self, ci: &mut ComponentInterface); - /// Go through each [`Object`] of a [`ComponentInterface`] and + /// Go through each 'Object` of a [`ComponentInterface`] and /// adjust it to language specific naming conventions. fn visit_object(&self, ci: &mut ComponentInterface); - /// Go through each [`Function`] of a [`ComponentInterface`] and + /// Go through each `Function`] of a [`ComponentInterface`] and /// adjust it to language specific naming conventions. fn visit_function(&self, ci: &mut ComponentInterface); - /// Go through each [`CallbackInterface`] of a [`ComponentInterface`] and + /// Go through each `CallbackInterface` of a [`ComponentInterface`] and /// adjust it to language specific naming conventions. fn visit_callback_interface(&self, ci: &mut ComponentInterface); From 03c78fceae2f03fc8ce55fa39272a229094f819a Mon Sep 17 00:00:00 2001 From: Bastian Gruber Date: Mon, 29 Jul 2024 10:31:52 -0300 Subject: [PATCH 46/62] fix: cleanup --- uniffi_bindgen/src/bindings/python/gen_python/mod.rs | 3 --- uniffi_meta/src/types.rs | 3 +++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/uniffi_bindgen/src/bindings/python/gen_python/mod.rs b/uniffi_bindgen/src/bindings/python/gen_python/mod.rs index 6ca42329a0..3834517852 100644 --- a/uniffi_bindgen/src/bindings/python/gen_python/mod.rs +++ b/uniffi_bindgen/src/bindings/python/gen_python/mod.rs @@ -456,7 +456,6 @@ impl VisitMut for PythonCodeOracle { field.rename(self.var_name(field.name())); } - // new_records.insert(oracle.class_name(key), record); new_records.insert(key.to_string(), record); } @@ -524,7 +523,6 @@ impl VisitMut for PythonCodeOracle { fn visit_object(&self, ci: &mut ComponentInterface) { // Conversions for ObjectTemplate.py for object_item in ci.objects.iter_mut() { - // object_item.rename(oracle.class_name(object_item.name())); for meth in &mut object_item.methods { meth.rename(self.fn_name(meth.name())); @@ -549,7 +547,6 @@ impl VisitMut for PythonCodeOracle { fn visit_function(&self, ci: &mut ComponentInterface) { // Conversions for wrapper.py //TODO: Renaming the function name in wrapper.py is not currently tested - //TODO: Renaming the callback_interface name in wrapper.py is currently not tested for func in ci.functions.iter_mut() { func.rename(self.fn_name(func.name())); diff --git a/uniffi_meta/src/types.rs b/uniffi_meta/src/types.rs index 12460ca313..71cfd57a9e 100644 --- a/uniffi_meta/src/types.rs +++ b/uniffi_meta/src/types.rs @@ -171,6 +171,9 @@ impl Type { } } + // Currently we just change the `import_name`. Different languages have slightly different + // naming patterns. We don't change the name of the external type, because the original + // names are used to find them in the `ComponentInterface`. pub fn rename(&mut self, new_name: String) { if let Type::External { import_name, .. } = self { *import_name = new_name From 0e975da7444e7098631e2f1c4f694277e808343e Mon Sep 17 00:00:00 2001 From: Bastian Gruber Date: Mon, 29 Jul 2024 11:32:23 -0300 Subject: [PATCH 47/62] fix: adjustments --- .../src/bindings/python/templates/ErrorTemplate.py | 2 +- uniffi_bindgen/src/bindings/python/templates/wrapper.py | 2 +- uniffi_bindgen/src/interface/conventions.rs | 5 +++++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/uniffi_bindgen/src/bindings/python/templates/ErrorTemplate.py b/uniffi_bindgen/src/bindings/python/templates/ErrorTemplate.py index 450364c55b..b1062bc04b 100644 --- a/uniffi_bindgen/src/bindings/python/templates/ErrorTemplate.py +++ b/uniffi_bindgen/src/bindings/python/templates/ErrorTemplate.py @@ -47,7 +47,7 @@ def __init__(self{% for field in variant.fields() %}, {{ field.name() }}{% endf {%- endfor %} ])) {%- for field in variant.fields() %} - self.{{ field.name() }} = {{ field.name() }} + self.{{ field.name() }} = {{ field.name() }} {%- endfor %} {%- else %} pass diff --git a/uniffi_bindgen/src/bindings/python/templates/wrapper.py b/uniffi_bindgen/src/bindings/python/templates/wrapper.py index e559120d84..4380041ad8 100644 --- a/uniffi_bindgen/src/bindings/python/templates/wrapper.py +++ b/uniffi_bindgen/src/bindings/python/templates/wrapper.py @@ -73,7 +73,7 @@ "{{ obj|type_name }}", {%- endfor %} {%- for c in ci.callback_interface_definitions() %} - "{{ c.name()|class_name }}", + "{{ c.name()|class_name }}", {%- endfor %} ] diff --git a/uniffi_bindgen/src/interface/conventions.rs b/uniffi_bindgen/src/interface/conventions.rs index 5b79afa214..a1a2d3eaf3 100644 --- a/uniffi_bindgen/src/interface/conventions.rs +++ b/uniffi_bindgen/src/interface/conventions.rs @@ -1,6 +1,11 @@ use crate::{ComponentInterface, VisitMut}; impl ComponentInterface { + /// Walk down the [`ComponentInterface`] and adjust the names of each type + /// based on the naming conventions of the supported languages. + /// + /// Each suppoerted language implements the [`VisitMut`] Trait and is able + /// to alter the functions, enums etc. to its own naming conventions. pub fn visit_mut(&mut self, visitor: &V) { visitor.visit_record(self); visitor.visit_enum(self); From ec31e9f8c6a579a6e214317e80978cb39b557bca Mon Sep 17 00:00:00 2001 From: Bastian Gruber Date: Mon, 29 Jul 2024 11:39:21 -0300 Subject: [PATCH 48/62] fix: remove whitespaces --- uniffi_bindgen/src/bindings/python/templates/ErrorTemplate.py | 4 ++-- uniffi_bindgen/src/interface/enum_.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/uniffi_bindgen/src/bindings/python/templates/ErrorTemplate.py b/uniffi_bindgen/src/bindings/python/templates/ErrorTemplate.py index b1062bc04b..9d3b30831d 100644 --- a/uniffi_bindgen/src/bindings/python/templates/ErrorTemplate.py +++ b/uniffi_bindgen/src/bindings/python/templates/ErrorTemplate.py @@ -39,11 +39,11 @@ def __getitem__(self, index): return self._values[index] {%- else %} - def __init__(self{% for field in variant.fields() %}, {{ field.name() }}{% endfor %}): + def __init__(self{% for field in variant.fields() %}, {{ field.name() }}{% endfor %}): {%- if variant.has_fields() %} super().__init__(", ".join([ {%- for field in variant.fields() %} - "{{ field.name() }}={!r}".format({{ field.name() }}), + "{{ field.name() }}={!r}".format({{ field.name() }}), {%- endfor %} ])) {%- for field in variant.fields() %} diff --git a/uniffi_bindgen/src/interface/enum_.rs b/uniffi_bindgen/src/interface/enum_.rs index 0a1f7e49cd..97cd8e0be7 100644 --- a/uniffi_bindgen/src/interface/enum_.rs +++ b/uniffi_bindgen/src/interface/enum_.rs @@ -330,7 +330,7 @@ impl TryFrom for Variant { fn try_from(meta: uniffi_meta::VariantMetadata) -> Result { Ok(Self { - name: meta.name.clone(), + name: meta.name, discr: meta.discr, fields: meta .fields From 6bd2e6f35272307184737fa4e13d99f99a45911d Mon Sep 17 00:00:00 2001 From: Bastian Gruber Date: Wed, 31 Jul 2024 21:18:51 -0300 Subject: [PATCH 49/62] fix: pr review --- .../src/bindings/python/gen_python/mod.rs | 177 ++++++------------ uniffi_bindgen/src/interface/callbacks.rs | 6 +- uniffi_bindgen/src/interface/conventions.rs | 128 ++++++++++++- uniffi_bindgen/src/interface/enum_.rs | 4 +- uniffi_bindgen/src/interface/ffi.rs | 10 +- uniffi_bindgen/src/interface/function.rs | 4 +- uniffi_bindgen/src/interface/mod.rs | 12 +- uniffi_bindgen/src/interface/object.rs | 8 +- uniffi_bindgen/src/interface/record.rs | 2 +- uniffi_bindgen/src/interface/universe.rs | 4 +- uniffi_bindgen/src/lib.rs | 42 +++-- uniffi_meta/src/types.rs | 48 +++-- 12 files changed, 264 insertions(+), 181 deletions(-) diff --git a/uniffi_bindgen/src/bindings/python/gen_python/mod.rs b/uniffi_bindgen/src/bindings/python/gen_python/mod.rs index 3834517852..1c1bbff627 100644 --- a/uniffi_bindgen/src/bindings/python/gen_python/mod.rs +++ b/uniffi_bindgen/src/bindings/python/gen_python/mod.rs @@ -10,7 +10,7 @@ use once_cell::sync::Lazy; use serde::{Deserialize, Serialize}; use std::borrow::Borrow; use std::cell::RefCell; -use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; +use std::collections::{BTreeSet, HashMap, HashSet}; use std::fmt::Debug; use crate::backend::TemplateExpression; @@ -303,7 +303,7 @@ impl<'a> PythonWrapper<'a> { ci.visit_mut(&PythonCodeOracle); let type_renderer = TypeRenderer::new(&config, ci); - let type_helper_code = type_renderer.render().unwrap(); + let type_helper_code = type_renderer.render().map_err(|e| println!("{e}")).unwrap(); let type_imports = type_renderer.imports.into_inner(); Self { @@ -442,152 +442,81 @@ impl PythonCodeOracle { } impl VisitMut for PythonCodeOracle { - fn visit_record(&self, ci: &mut ComponentInterface) { - // Conversions for RecordTemplate.py - let mut new_records: BTreeMap = BTreeMap::new(); - - for (key, record_item) in ci.records.iter_mut() { - let mut record = record_item.clone(); - - // We just want to prefix reserved keywords for the name, without modifying it - record.rename(self.class_name(record_item.name())); - - for field in &mut record.fields { - field.rename(self.var_name(field.name())); - } - - new_records.insert(key.to_string(), record); - } - - // One cannot alter a BTreeMap in place (with a few hacks maybe...), so we create a new one - // with the adjusted names, and replace it. - ci.records = new_records; + fn visit_record(&self, record: &mut Record) { + record.rename(self.class_name(record.name())); } - fn visit_enum(&self, ci: &mut ComponentInterface) { - // Conversions for EnumTemplate.py and ErrorTemplate.py - let errors = ci.errors.clone(); - - for enum_item in ci.enums.values_mut() { - if errors.contains(enum_item.name()) { - enum_item.rename(self.class_name(enum_item.name())); - - for variant in &mut enum_item.variants { - variant.rename(self.class_name(variant.name())); - - for field in &mut variant.fields { - field.rename(self.var_name(field.name())); - } - } - } else { - enum_item.rename(self.enum_variant_name(enum_item.name())); - - for variant in &mut enum_item.variants { - variant.rename(self.enum_variant_name(variant.name())); + fn visit_object(&self, object: &mut Object) { + object.rename(self.class_name(object.name())); + } - //TODO: If we want to remove the last var_name filter - // in the template, this is it. We need an additional - // attribute for the `Variant` so we can - // display Python is_NAME functions - // variant.set_is_name(self.var_name(variant.name())); + fn visit_record_key(&self, key: &str) -> String { + self.class_name(key) + } - for field in &mut variant.fields { - field.rename(self.var_name(field.name())); - } - } - } - } + fn visit_field(&self, field: &mut Field) { + field.rename(self.var_name(field.name())); } - fn visit_type(&self, ci: &mut ComponentInterface) { - // Applying changes to the TypeUniverse - for t in &mut ci.types.type_definitions.values_mut() { - if t.name().is_some() { - t.rename(self.class_name(&t.name().unwrap())); - } - } + fn visit_ffi_field(&self, ffi_field: &mut FfiField) { + ffi_field.rename(self.var_name(ffi_field.name())); + } - let mut known: BTreeSet = BTreeSet::new(); + fn visit_ffi_argument(&self, ffi_argument: &mut FfiArgument) { + ffi_argument.rename(self.class_name(ffi_argument.name())); + } - for t in &mut ci.types.all_known_types.iter() { - let mut ty = t.clone(); - if t.name().is_some() { - ty.rename(self.class_name(&t.name().unwrap())); - } - known.insert(ty.clone()); + fn visit_enum(&self, is_error: bool, enum_: &mut Enum) { + if is_error { + enum_.rename(self.class_name(enum_.name())); + } else { + enum_.rename(self.enum_variant_name(enum_.name())); } - - ci.types.all_known_types = known; } - fn visit_object(&self, ci: &mut ComponentInterface) { - // Conversions for ObjectTemplate.py - for object_item in ci.objects.iter_mut() { - for meth in &mut object_item.methods { - meth.rename(self.fn_name(meth.name())); - - for arg in meth.arguments.iter_mut() { - arg.rename(self.var_name(arg.name())); - } - } + fn visit_enum_key(&self, key: &str) -> String { + self.class_name(key) + } - for cons in &mut object_item.constructors { - if !cons.is_primary_constructor() { - cons.rename(self.fn_name(cons.name())); - } + fn visit_variant(&self, is_error: bool, variant: &mut Variant) { + //TODO: If we want to remove the last var_name filter + // in the template, this is it. We need an additional + // attribute for the `Variant` so we can + // display Python is_NAME functions + // variant.set_is_name(self.var_name(variant.name())); - // For macros.py - for arg in cons.arguments.iter_mut() { - arg.rename(self.var_name(arg.name())); - } - } + if is_error { + variant.rename(self.class_name(variant.name())); + } else { + variant.rename(self.enum_variant_name(variant.name())); } } - fn visit_function(&self, ci: &mut ComponentInterface) { - // Conversions for wrapper.py - //TODO: Renaming the function name in wrapper.py is not currently tested - for func in ci.functions.iter_mut() { - func.rename(self.fn_name(func.name())); - - for arg in func.arguments.iter_mut() { - arg.rename(self.var_name(arg.name())); - } + fn visit_type(&self, type_: &mut Type) { + // Applying changes to the TypeUniverse + if type_.name().is_some() { + type_.rename(self.class_name(&type_.name().unwrap())); } } - fn visit_callback_interface(&self, ci: &mut ComponentInterface) { - // Conversions for CallbackInterfaceImpl.py - for callback_interface in ci.callback_interfaces.iter_mut() { - //TODO: To remove the last fn_name filter, we need to add an attribute for - // the `CallbackInterface` so we can alter one specific point in the template - // without altering the name everywhere else. - // callback_interface.rename_display(oracle.fn_name(callback_interface.name())); + fn visit_method(&self, method: &mut Method) { + method.rename(self.fn_name(method.name())); + } - for method in callback_interface.methods.iter_mut() { - method.rename(self.fn_name(method.name())); + fn visit_argument(&self, argument: &mut Argument) { + argument.rename(self.var_name(argument.name())); + } - for arg in method.arguments.iter_mut() { - arg.rename(self.var_name(arg.name())); - } - } + fn visit_constructor(&self, constructor: &mut Constructor) { + if !constructor.is_primary_constructor() { + constructor.rename(self.fn_name(constructor.name())); } } - fn visit_ffi_defitinion(&self, ci: &mut ComponentInterface) { - //TODO: Renaming the fields for a FfiStruct is currently not being tested - // Replace var_name filter for NamespaceLibraryTemplate.py - for def in ci.ffi_definitions() { - match def { - FfiDefinition::Function(_) => {} - FfiDefinition::CallbackFunction(_) => {} - FfiDefinition::Struct(mut ffi_struct) => { - for field in ffi_struct.fields.iter_mut() { - field.rename(self.var_name(field.name())); - } - } - } - } + fn visit_function(&self, function: &mut Function) { + // Conversions for wrapper.py + //TODO: Renaming the function name in wrapper.py is not currently tested + function.rename(self.fn_name(function.name())); } } diff --git a/uniffi_bindgen/src/interface/callbacks.rs b/uniffi_bindgen/src/interface/callbacks.rs index 14886d9b02..779258db6d 100644 --- a/uniffi_bindgen/src/interface/callbacks.rs +++ b/uniffi_bindgen/src/interface/callbacks.rs @@ -46,7 +46,7 @@ use super::{AsType, Type, TypeIterator}; pub struct CallbackInterface { pub(super) name: String, pub(super) module_path: String, - pub(crate) methods: Vec, + pub(super) methods: Vec, // We don't include the FFIFunc in the hash calculation, because: // - it is entirely determined by the other fields, // so excluding it is safe. @@ -64,6 +64,10 @@ impl CallbackInterface { &self.name } + pub fn rename(&mut self, new_name: String) { + self.name = new_name; + } + pub fn methods(&self) -> Vec<&Method> { self.methods.iter().collect() } diff --git a/uniffi_bindgen/src/interface/conventions.rs b/uniffi_bindgen/src/interface/conventions.rs index a1a2d3eaf3..b7ba2db4b1 100644 --- a/uniffi_bindgen/src/interface/conventions.rs +++ b/uniffi_bindgen/src/interface/conventions.rs @@ -1,4 +1,7 @@ +use crate::interface::{Enum, FfiDefinition, Record}; use crate::{ComponentInterface, VisitMut}; +use std::collections::{BTreeMap, BTreeSet}; +use uniffi_meta::Type; impl ComponentInterface { /// Walk down the [`ComponentInterface`] and adjust the names of each type @@ -7,12 +10,123 @@ impl ComponentInterface { /// Each suppoerted language implements the [`VisitMut`] Trait and is able /// to alter the functions, enums etc. to its own naming conventions. pub fn visit_mut(&mut self, visitor: &V) { - visitor.visit_record(self); - visitor.visit_enum(self); - visitor.visit_type(self); - visitor.visit_object(self); - visitor.visit_function(self); - visitor.visit_callback_interface(self); - visitor.visit_ffi_defitinion(self); + for type_ in self.types.type_definitions.values_mut() { + visitor.visit_type(type_); + } + + let mut all_known_types_altered: BTreeSet = BTreeSet::new(); + + for type_ in self.types.all_known_types.iter() { + let mut type_altered = type_.clone(); + visitor.visit_type(&mut type_altered); + all_known_types_altered.insert(type_altered); + } + + self.types.all_known_types = all_known_types_altered; + + self.fix_keys_after_rename(visitor); + + let mut updated_enums: BTreeMap = BTreeMap::new(); + let errors_clone = self.errors.clone(); + for (enum_name, enum_item) in self.enums.iter_mut() { + let updated_key = visitor.visit_enum_key(enum_name); + let is_error = errors_clone.contains(enum_item.name()); + + visitor.visit_enum(is_error, enum_item); + + for variant in enum_item.variants.iter_mut() { + visitor.visit_variant(is_error, variant); + + for field in variant.fields.iter_mut() { + visitor.visit_field(field); + visitor.visit_type(&mut field.type_); + } + } + updated_enums.insert(updated_key, enum_item.clone()); + } + self.enums = updated_enums; + + for record_item in self.records.values_mut() { + visitor.visit_record(record_item); + + for field in &mut record_item.fields { + visitor.visit_field(field); + visitor.visit_type(&mut field.type_); + } + } + + for function in self.functions.iter_mut() { + visitor.visit_function(function); + + for argument in function.arguments.iter_mut() { + visitor.visit_argument(argument); + visitor.visit_type(&mut argument.type_); + } + } + + for object in self.objects.iter_mut() { + visitor.visit_object(object); + + for method in object.methods.iter_mut() { + visitor.visit_method(method); + + for argument in method.arguments.iter_mut() { + visitor.visit_argument(argument); + visitor.visit_type(&mut argument.type_); + } + } + + for constructor in object.constructors.iter_mut() { + visitor.visit_constructor(constructor); + + for argument in constructor.arguments.iter_mut() { + visitor.visit_argument(argument); + visitor.visit_type(&mut argument.type_); + } + } + } + + for callback_interface in self.callback_interfaces.iter_mut() { + for method in callback_interface.methods.iter_mut() { + visitor.visit_method(method); + + for argument in method.arguments.iter_mut() { + visitor.visit_argument(argument); + visitor.visit_type(&mut argument.type_); + } + } + + for ffi_arg in callback_interface.ffi_init_callback.arguments.iter_mut() { + visitor.visit_ffi_argument(ffi_arg); + } + } + + let mut throw_types_altered: BTreeSet = BTreeSet::new(); + + for throw_type in self.callback_interface_throws_types.iter() { + let mut type_ = throw_type.clone(); + visitor.visit_type(&mut type_); + throw_types_altered.insert(type_); + } + + self.callback_interface_throws_types = throw_types_altered; + + for ffi_definition in self.ffi_definitions() { + if let FfiDefinition::Struct(mut ffi_struct) = ffi_definition { + for field in ffi_struct.fields.iter_mut() { + visitor.visit_ffi_field(field); + } + } + } + } + + fn fix_keys_after_rename(&mut self, visitor: &V) { + let mut new_records: BTreeMap = BTreeMap::new(); + + for (key, record) in self.records.iter() { + new_records.insert(visitor.visit_record_key(key), record.clone()); + } + + self.records = new_records; } } diff --git a/uniffi_bindgen/src/interface/enum_.rs b/uniffi_bindgen/src/interface/enum_.rs index 97cd8e0be7..0d66ce2985 100644 --- a/uniffi_bindgen/src/interface/enum_.rs +++ b/uniffi_bindgen/src/interface/enum_.rs @@ -175,7 +175,7 @@ pub struct Enum { pub(super) name: String, pub(super) module_path: String, pub(super) discr_type: Option, - pub(crate) variants: Vec, + pub(super) variants: Vec, pub(super) shape: EnumShape, pub(super) non_exhaustive: bool, #[checksum_ignore] @@ -290,7 +290,7 @@ impl AsType for Enum { pub struct Variant { pub(super) name: String, pub(super) discr: Option, - pub(crate) fields: Vec, + pub(super) fields: Vec, #[checksum_ignore] pub(super) docstring: Option, } diff --git a/uniffi_bindgen/src/interface/ffi.rs b/uniffi_bindgen/src/interface/ffi.rs index d644647c51..1fe90c85fd 100644 --- a/uniffi_bindgen/src/interface/ffi.rs +++ b/uniffi_bindgen/src/interface/ffi.rs @@ -297,12 +297,12 @@ impl FfiArgument { &self.name } - pub fn type_(&self) -> FfiType { - self.type_.clone() + pub fn rename(&mut self, new_name: String) { + self.name = new_name; } - pub fn rename(&mut self, name: String) { - self.name = name; + pub fn type_(&self) -> FfiType { + self.type_.clone() } } @@ -344,7 +344,7 @@ impl FfiCallbackFunction { #[derive(Debug, Default, Clone)] pub struct FfiStruct { pub(super) name: String, - pub(crate) fields: Vec, + pub(super) fields: Vec, } impl FfiStruct { diff --git a/uniffi_bindgen/src/interface/function.rs b/uniffi_bindgen/src/interface/function.rs index 850677abe2..69f4f153dc 100644 --- a/uniffi_bindgen/src/interface/function.rs +++ b/uniffi_bindgen/src/interface/function.rs @@ -49,7 +49,7 @@ pub struct Function { pub(super) name: String, pub(super) module_path: String, pub(super) is_async: bool, - pub(crate) arguments: Vec, + pub(super) arguments: Vec, pub(super) return_type: Option, // We don't include the FFIFunc in the hash calculation, because: // - it is entirely determined by the other fields, @@ -73,6 +73,8 @@ impl Function { &self.name } + // Note: Don't recalculate the checksum. In order to have consistent checksums, + // we use the Rust names as input. pub fn rename(&mut self, new_name: String) { self.name = new_name; } diff --git a/uniffi_bindgen/src/interface/mod.rs b/uniffi_bindgen/src/interface/mod.rs index d8c0896952..1e90b8a097 100644 --- a/uniffi_bindgen/src/interface/mod.rs +++ b/uniffi_bindgen/src/interface/mod.rs @@ -87,13 +87,13 @@ pub struct ComponentInterface { // anyway, so it's safe to ignore it. pub(super) types: TypeUniverse, /// The high-level API provided by the component. - pub(crate) enums: BTreeMap, - pub(crate) records: BTreeMap, - pub(crate) functions: Vec, - pub(crate) objects: Vec, - pub(crate) callback_interfaces: Vec, + enums: BTreeMap, + records: BTreeMap, + functions: Vec, + objects: Vec, + callback_interfaces: Vec, // Type names which were seen used as an error. - pub(crate) errors: HashSet, + errors: HashSet, // Types which were seen used as callback interface error. callback_interface_throws_types: BTreeSet, } diff --git a/uniffi_bindgen/src/interface/object.rs b/uniffi_bindgen/src/interface/object.rs index 8c0dab7502..2a9ee0b907 100644 --- a/uniffi_bindgen/src/interface/object.rs +++ b/uniffi_bindgen/src/interface/object.rs @@ -85,8 +85,8 @@ pub struct Object { /// How this object is implemented in Rust pub(super) imp: ObjectImpl, pub(super) module_path: String, - pub(crate) constructors: Vec, - pub(crate) methods: Vec, + pub(super) constructors: Vec, + pub(super) methods: Vec, // The "trait" methods - they have a (presumably "well known") name, and // a regular method (albeit with a generated name) // XXX - this should really be a HashSet, but not enough transient types support hash to make it worthwhile now. @@ -360,7 +360,7 @@ pub struct Constructor { pub(super) object_name: String, pub(super) object_module_path: String, pub(super) is_async: bool, - pub(crate) arguments: Vec, + pub(super) arguments: Vec, // We don't include the FFIFunc in the hash calculation, because: // - it is entirely determined by the other fields, // so excluding it is safe. @@ -477,7 +477,7 @@ pub struct Method { pub(super) object_module_path: String, pub(super) is_async: bool, pub(super) object_impl: ObjectImpl, - pub(crate) arguments: Vec, + pub(super) arguments: Vec, pub(super) return_type: Option, // We don't include the FFIFunc in the hash calculation, because: // - it is entirely determined by the other fields, diff --git a/uniffi_bindgen/src/interface/record.rs b/uniffi_bindgen/src/interface/record.rs index bb5d8a4136..3af6495c99 100644 --- a/uniffi_bindgen/src/interface/record.rs +++ b/uniffi_bindgen/src/interface/record.rs @@ -59,7 +59,7 @@ use super::{AsType, Type, TypeIterator}; pub struct Record { pub(super) name: String, pub(super) module_path: String, - pub(crate) fields: Vec, + pub(super) fields: Vec, #[checksum_ignore] pub(super) docstring: Option, } diff --git a/uniffi_bindgen/src/interface/universe.rs b/uniffi_bindgen/src/interface/universe.rs index 64613679fa..9a78b3bf34 100644 --- a/uniffi_bindgen/src/interface/universe.rs +++ b/uniffi_bindgen/src/interface/universe.rs @@ -28,9 +28,9 @@ pub(crate) struct TypeUniverse { pub namespace_docstring: Option, // Named type definitions (including aliases). - pub type_definitions: HashMap, + pub(super) type_definitions: HashMap, // All the types in the universe, by canonical type name, in a well-defined order. - pub all_known_types: BTreeSet, + pub(super) all_known_types: BTreeSet, } impl TypeUniverse { diff --git a/uniffi_bindgen/src/lib.rs b/uniffi_bindgen/src/lib.rs index 565499041a..fb16cd6bbf 100644 --- a/uniffi_bindgen/src/lib.rs +++ b/uniffi_bindgen/src/lib.rs @@ -110,9 +110,13 @@ pub mod scaffolding; #[cfg(feature = "cargo-metadata")] pub mod cargo_metadata; -use crate::interface::Object; +use crate::interface::{ + Argument, Constructor, Enum, FfiArgument, FfiField, Field, Function, Method, Object, Record, + Variant, +}; pub use interface::ComponentInterface; use scaffolding::RustScaffolding; +use uniffi_meta::Type; /// The options used when creating bindings. Named such /// it doesn't cause confusion that it's settings specific to @@ -169,32 +173,40 @@ pub trait BindingGenerator: Sized { pub trait VisitMut { /// Go through each `Record` of a [`ComponentInterface`] and /// adjust it to language specific naming conventions. - fn visit_record(&self, ci: &mut ComponentInterface); + fn visit_record(&self, record: &mut Record); + + fn visit_object(&self, object: &mut Object); + + fn visit_record_key(&self, key: &str) -> String; + + fn visit_field(&self, field: &mut Field); + + fn visit_ffi_field(&self, ffi_field: &mut FfiField); + + fn visit_ffi_argument(&self, ffi_argument: &mut FfiArgument); /// Go through each `Enum` of a [`ComponentInterface`] and /// adjust it to language specific naming conventions. - fn visit_enum(&self, ci: &mut ComponentInterface); + fn visit_enum(&self, is_error: bool, enum_: &mut Enum); + + fn visit_enum_key(&self, key: &str) -> String; + + fn visit_variant(&self, is_error: bool, variant: &mut Variant); /// Go through each `Type` in the `TypeUniverse` of /// a [`ComponentInterface`] and adjust it to language specific /// naming conventions. - fn visit_type(&self, ci: &mut ComponentInterface); + fn visit_type(&self, type_: &mut Type); - /// Go through each 'Object` of a [`ComponentInterface`] and - /// adjust it to language specific naming conventions. - fn visit_object(&self, ci: &mut ComponentInterface); + fn visit_method(&self, method: &mut Method); - /// Go through each `Function`] of a [`ComponentInterface`] and - /// adjust it to language specific naming conventions. - fn visit_function(&self, ci: &mut ComponentInterface); + fn visit_argument(&self, argument: &mut Argument); - /// Go through each `CallbackInterface` of a [`ComponentInterface`] and - /// adjust it to language specific naming conventions. - fn visit_callback_interface(&self, ci: &mut ComponentInterface); + fn visit_constructor(&self, constructor: &mut Constructor); - /// Go through each FfiDefinition of a [`ComponentInterface`] and + /// Go through each `Function`] of a [`ComponentInterface`] and /// adjust it to language specific naming conventions. - fn visit_ffi_defitinion(&self, ci: &mut ComponentInterface); + fn visit_function(&self, function: &mut Function); } /// Everything needed to generate a ComponentInterface. diff --git a/uniffi_meta/src/types.rs b/uniffi_meta/src/types.rs index 71cfd57a9e..605efab72b 100644 --- a/uniffi_meta/src/types.rs +++ b/uniffi_meta/src/types.rs @@ -150,7 +150,7 @@ impl Type { Type::Object { name, .. } => Some(name.to_string()), Type::Record { name, .. } => Some(name.to_string()), Type::Enum { name, .. } => Some(name.to_string()), - Type::CallbackInterface { name, .. } => Some(name.to_string()), + // Type::CallbackInterface { name, .. } => Some(name.to_string()), Type::Custom { name, .. } => Some(name.to_string()), Type::External { name, .. } => Some(name.to_string()), Type::Optional { inner_type } | Type::Sequence { inner_type } => { @@ -160,23 +160,45 @@ impl Type { None } } - Type::Map { value_type, .. } => { - if value_type.name().is_some() { - Some(value_type.name().unwrap()) - } else { - None - } - } + Type::Map { + value_type, + key_type, + } => key_type.name().or_else(|| value_type.name()), _ => None, } } - // Currently we just change the `import_name`. Different languages have slightly different - // naming patterns. We don't change the name of the external type, because the original - // names are used to find them in the `ComponentInterface`. pub fn rename(&mut self, new_name: String) { - if let Type::External { import_name, .. } = self { - *import_name = new_name + match self { + Type::Object { name, .. } => *name = new_name, + Type::Record { name, .. } => *name = new_name, + Type::Enum { name, .. } => *name = new_name, + // Type::CallbackInterface { name, .. } => *name = new_name, + Type::Custom { name, .. } => *name = new_name, + Type::External { + import_name, name, .. + } => { + *import_name = new_name.clone(); + *name = new_name; + } + Type::Optional { inner_type } | Type::Sequence { inner_type } => { + if inner_type.name().is_some() { + inner_type.rename(new_name); + } + } + Type::Map { + value_type, + key_type, + } => { + if value_type.name().is_some() { + value_type.rename(new_name.clone()); + } + + if key_type.name().is_some() { + key_type.rename(new_name); + } + } + _ => {} } } } From e9b2520726e2fdf410b2db8db45c0918cccf7a55 Mon Sep 17 00:00:00 2001 From: Bastian Gruber Date: Wed, 31 Jul 2024 21:35:47 -0300 Subject: [PATCH 50/62] fix: cleanup --- .../src/bindings/kotlin/templates/Types.kt | 2 +- .../bindings/python/templates/ExternalTemplate.py | 2 +- .../src/bindings/python/templates/Types.py | 2 +- uniffi_bindgen/src/interface/callbacks.rs | 4 ---- uniffi_meta/src/group.rs | 13 ++++--------- uniffi_meta/src/types.rs | 7 +------ uniffi_udl/src/finder.rs | 3 +-- 7 files changed, 9 insertions(+), 24 deletions(-) diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/Types.kt b/uniffi_bindgen/src/bindings/kotlin/templates/Types.kt index cfe0c42a0c..c27121b701 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/Types.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/Types.kt @@ -124,7 +124,7 @@ object NoPointer {%- when Type::Custom { module_path, name, builtin } %} {% include "CustomTypeTemplate.kt" %} -{%- when Type::External { module_path, name, import_name, namespace, kind, tagged } %} +{%- when Type::External { module_path, name, namespace, kind, tagged } %} {% include "ExternalTypeTemplate.kt" %} {%- else %} diff --git a/uniffi_bindgen/src/bindings/python/templates/ExternalTemplate.py b/uniffi_bindgen/src/bindings/python/templates/ExternalTemplate.py index fb41d7ab02..26e4639e19 100644 --- a/uniffi_bindgen/src/bindings/python/templates/ExternalTemplate.py +++ b/uniffi_bindgen/src/bindings/python/templates/ExternalTemplate.py @@ -3,7 +3,7 @@ # External type {{name}} is in namespace "{{namespace}}", crate {{module_path}} {%- let ffi_converter_name = "_UniffiConverterType{}"|format(name) %} {{ self.add_import_of(module, ffi_converter_name) }} -{{ self.add_import_of(module, import_name) }} {#- import the type alias itself -#} +{{ self.add_import_of(module, name) }} {#- import the type alias itself -#} {%- let rustbuffer_local_name = "_UniffiRustBuffer{}"|format(name) %} {{ self.add_import_of_as(module, "_UniffiRustBuffer", rustbuffer_local_name) }} diff --git a/uniffi_bindgen/src/bindings/python/templates/Types.py b/uniffi_bindgen/src/bindings/python/templates/Types.py index acfd43fae9..29f66fe886 100644 --- a/uniffi_bindgen/src/bindings/python/templates/Types.py +++ b/uniffi_bindgen/src/bindings/python/templates/Types.py @@ -91,7 +91,7 @@ {%- when Type::Custom { name, module_path, builtin } %} {%- include "CustomType.py" %} -{%- when Type::External { name, import_name, module_path, namespace, kind, tagged } %} +{%- when Type::External { name, module_path, namespace, kind, tagged } %} {%- include "ExternalTemplate.py" %} {%- else %} diff --git a/uniffi_bindgen/src/interface/callbacks.rs b/uniffi_bindgen/src/interface/callbacks.rs index 779258db6d..b0e978e8b4 100644 --- a/uniffi_bindgen/src/interface/callbacks.rs +++ b/uniffi_bindgen/src/interface/callbacks.rs @@ -64,10 +64,6 @@ impl CallbackInterface { &self.name } - pub fn rename(&mut self, new_name: String) { - self.name = new_name; - } - pub fn methods(&self) -> Vec<&Method> { self.methods.iter().collect() } diff --git a/uniffi_meta/src/group.rs b/uniffi_meta/src/group.rs index cadb0044e6..a41776bf8a 100644 --- a/uniffi_meta/src/group.rs +++ b/uniffi_meta/src/group.rs @@ -181,8 +181,7 @@ impl<'a> ExternalTypeConverter<'a> { Type::External { namespace: self.crate_to_namespace(&module_path), module_path, - name: name.clone(), - import_name: name, + name, kind: ExternalKind::DataClass, tagged: false, } @@ -195,8 +194,7 @@ impl<'a> ExternalTypeConverter<'a> { Type::External { namespace: self.crate_to_namespace(&module_path), module_path, - name: name.clone(), - import_name: name, + name, kind: ExternalKind::DataClass, tagged: false, } @@ -206,8 +204,7 @@ impl<'a> ExternalTypeConverter<'a> { } if self.is_module_path_external(&module_path) => Type::External { namespace: self.crate_to_namespace(&module_path), module_path, - name: name.clone(), - import_name: name, + name, kind: ExternalKind::Interface, tagged: false, }, @@ -245,7 +242,6 @@ impl<'a> ExternalTypeConverter<'a> { namespace, module_path, name, - import_name, kind, tagged, } => { @@ -253,8 +249,7 @@ impl<'a> ExternalTypeConverter<'a> { Type::External { namespace: self.crate_to_namespace(&module_path), module_path, - name: name.clone(), - import_name, + name, kind, tagged, } diff --git a/uniffi_meta/src/types.rs b/uniffi_meta/src/types.rs index 605efab72b..61efd4749c 100644 --- a/uniffi_meta/src/types.rs +++ b/uniffi_meta/src/types.rs @@ -116,7 +116,6 @@ pub enum Type { External { module_path: String, name: String, - import_name: String, #[checksum_ignore] // The namespace is not known generating scaffolding. namespace: String, kind: ExternalKind, @@ -150,7 +149,6 @@ impl Type { Type::Object { name, .. } => Some(name.to_string()), Type::Record { name, .. } => Some(name.to_string()), Type::Enum { name, .. } => Some(name.to_string()), - // Type::CallbackInterface { name, .. } => Some(name.to_string()), Type::Custom { name, .. } => Some(name.to_string()), Type::External { name, .. } => Some(name.to_string()), Type::Optional { inner_type } | Type::Sequence { inner_type } => { @@ -175,10 +173,7 @@ impl Type { Type::Enum { name, .. } => *name = new_name, // Type::CallbackInterface { name, .. } => *name = new_name, Type::Custom { name, .. } => *name = new_name, - Type::External { - import_name, name, .. - } => { - *import_name = new_name.clone(); + Type::External { name, .. } => { *name = new_name; } Type::Optional { inner_type } | Type::Sequence { inner_type } => { diff --git a/uniffi_udl/src/finder.rs b/uniffi_udl/src/finder.rs index c1e12643a5..259557ad07 100644 --- a/uniffi_udl/src/finder.rs +++ b/uniffi_udl/src/finder.rs @@ -155,8 +155,7 @@ impl TypeFinder for weedle::TypedefDefinition<'_> { let kind = attrs.external_kind().expect("External missing kind"); let tagged = attrs.external_tagged().expect("External missing tagged"); Type::External { - name: name.clone(), - import_name: name, + name, namespace: "".to_string(), // we don't know this yet module_path: attrs.get_crate_name(), kind, From ca3593262ce7a74890c922df40b91dd771f4e5a5 Mon Sep 17 00:00:00 2001 From: Bastian Gruber Date: Wed, 31 Jul 2024 22:04:54 -0300 Subject: [PATCH 51/62] fix: add doc comments --- uniffi_bindgen/src/lib.rs | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/uniffi_bindgen/src/lib.rs b/uniffi_bindgen/src/lib.rs index fb16cd6bbf..561fefa903 100644 --- a/uniffi_bindgen/src/lib.rs +++ b/uniffi_bindgen/src/lib.rs @@ -175,22 +175,38 @@ pub trait VisitMut { /// adjust it to language specific naming conventions. fn visit_record(&self, record: &mut Record); + /// Change the name of an `Object` of a [`ComponentInterface` + /// to language specific naming conventions. fn visit_object(&self, object: &mut Object); + /// Change the index key of a `Record` entry in the [`ComponentInterface`] + /// so it adheres to the name the associated `Record` + /// based on language specific naming conventions. fn visit_record_key(&self, key: &str) -> String; + /// Change the name of a `Field` of an `Enum` `Variant` + /// to language specific naming conventions. fn visit_field(&self, field: &mut Field); + /// Change the name of a `FfiField` inside a `FfiStruct` + /// to language specific naming conventions. fn visit_ffi_field(&self, ffi_field: &mut FfiField); + /// Change the `Arugment` of a `FfiFunction` in the [`ComponentInterface`] + /// to language specific naming conventions. fn visit_ffi_argument(&self, ffi_argument: &mut FfiArgument); /// Go through each `Enum` of a [`ComponentInterface`] and /// adjust it to language specific naming conventions. fn visit_enum(&self, is_error: bool, enum_: &mut Enum); + /// Change the naming of the key in the [`ComponentInterface`] + /// `BTreeMap` where all `Enum`s are stored to reflect the changed + /// name of an `Enum`. fn visit_enum_key(&self, key: &str) -> String; + /// Go through each `Variant` of an `Enum` and + /// adjust it to language specific naming conventions. fn visit_variant(&self, is_error: bool, variant: &mut Variant); /// Go through each `Type` in the `TypeUniverse` of @@ -198,13 +214,19 @@ pub trait VisitMut { /// naming conventions. fn visit_type(&self, type_: &mut Type); + /// Go through each `Method` of an `Object` and + /// adjust it to language specific naming conventions. fn visit_method(&self, method: &mut Method); + /// Go through each `Argument` of a `Function` and + /// adjust it to language specific naming conventions. fn visit_argument(&self, argument: &mut Argument); + /// Go through each `Constructor` of a [`ComponentInterface`] and + /// adjust it to language specific naming conventions. fn visit_constructor(&self, constructor: &mut Constructor); - /// Go through each `Function`] of a [`ComponentInterface`] and + /// Go through each `Function` of a [`ComponentInterface`] and /// adjust it to language specific naming conventions. fn visit_function(&self, function: &mut Function); } From 8db50e6ca0f41d086435581b481c3b95248a0de3 Mon Sep 17 00:00:00 2001 From: Bastian Gruber Date: Wed, 31 Jul 2024 22:08:34 -0300 Subject: [PATCH 52/62] fix: cleanup --- uniffi_bindgen/src/bindings/python/gen_python/mod.rs | 2 +- uniffi_meta/src/types.rs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/uniffi_bindgen/src/bindings/python/gen_python/mod.rs b/uniffi_bindgen/src/bindings/python/gen_python/mod.rs index 1c1bbff627..e92f3ae270 100644 --- a/uniffi_bindgen/src/bindings/python/gen_python/mod.rs +++ b/uniffi_bindgen/src/bindings/python/gen_python/mod.rs @@ -303,7 +303,7 @@ impl<'a> PythonWrapper<'a> { ci.visit_mut(&PythonCodeOracle); let type_renderer = TypeRenderer::new(&config, ci); - let type_helper_code = type_renderer.render().map_err(|e| println!("{e}")).unwrap(); + let type_helper_code = type_renderer.render().unwrap(); let type_imports = type_renderer.imports.into_inner(); Self { diff --git a/uniffi_meta/src/types.rs b/uniffi_meta/src/types.rs index 61efd4749c..fb89e8e9b1 100644 --- a/uniffi_meta/src/types.rs +++ b/uniffi_meta/src/types.rs @@ -171,7 +171,6 @@ impl Type { Type::Object { name, .. } => *name = new_name, Type::Record { name, .. } => *name = new_name, Type::Enum { name, .. } => *name = new_name, - // Type::CallbackInterface { name, .. } => *name = new_name, Type::Custom { name, .. } => *name = new_name, Type::External { name, .. } => { *name = new_name; From d3c55fa69f40a8abcfec45cb769c934b4988f14f Mon Sep 17 00:00:00 2001 From: Bastian Gruber Date: Thu, 1 Aug 2024 10:35:42 -0300 Subject: [PATCH 53/62] fix: fix missing return type name change, clippy --- .../src/bindings/python/gen_python/mod.rs | 15 +++--- .../python/templates/ObjectTemplate.py | 1 - uniffi_bindgen/src/interface/conventions.rs | 16 +++++- uniffi_bindgen/src/lib.rs | 4 +- uniffi_meta/src/types.rs | 52 +++++++++---------- 5 files changed, 51 insertions(+), 37 deletions(-) diff --git a/uniffi_bindgen/src/bindings/python/gen_python/mod.rs b/uniffi_bindgen/src/bindings/python/gen_python/mod.rs index e92f3ae270..fdaa10f212 100644 --- a/uniffi_bindgen/src/bindings/python/gen_python/mod.rs +++ b/uniffi_bindgen/src/bindings/python/gen_python/mod.rs @@ -450,7 +450,7 @@ impl VisitMut for PythonCodeOracle { object.rename(self.class_name(object.name())); } - fn visit_record_key(&self, key: &str) -> String { + fn visit_record_key(&self, key: &mut String) -> String { self.class_name(key) } @@ -474,7 +474,7 @@ impl VisitMut for PythonCodeOracle { } } - fn visit_enum_key(&self, key: &str) -> String { + fn visit_enum_key(&self, key: &mut String) -> String { self.class_name(key) } @@ -493,10 +493,13 @@ impl VisitMut for PythonCodeOracle { } fn visit_type(&self, type_: &mut Type) { - // Applying changes to the TypeUniverse - if type_.name().is_some() { - type_.rename(self.class_name(&type_.name().unwrap())); - } + // Renaming Types is a special case. We have simple types with names like + // an Object, but we also have types which have inner_types and builtin types. + // Which in turn have a different name. Therefore we pass the patterns as a + // function down to the renaming operation of the type itself, which can apply it + // to all its nested names if needed. + let name_transformer = |name: &str| self.class_name(name); + type_.rename_recursive(&name_transformer); } fn visit_method(&self, method: &mut Method) { diff --git a/uniffi_bindgen/src/bindings/python/templates/ObjectTemplate.py b/uniffi_bindgen/src/bindings/python/templates/ObjectTemplate.py index 5772ad24db..86029beea6 100644 --- a/uniffi_bindgen/src/bindings/python/templates/ObjectTemplate.py +++ b/uniffi_bindgen/src/bindings/python/templates/ObjectTemplate.py @@ -49,7 +49,6 @@ def _make_instance_(cls, pointer): return inst {%- for cons in obj.alternate_constructors() %} - @classmethod {%- if cons.is_async() %} async def {{ cons.name() }}(cls, {% call py::arg_list_decl(cons) %}): diff --git a/uniffi_bindgen/src/interface/conventions.rs b/uniffi_bindgen/src/interface/conventions.rs index b7ba2db4b1..48aa530015 100644 --- a/uniffi_bindgen/src/interface/conventions.rs +++ b/uniffi_bindgen/src/interface/conventions.rs @@ -29,7 +29,7 @@ impl ComponentInterface { let mut updated_enums: BTreeMap = BTreeMap::new(); let errors_clone = self.errors.clone(); for (enum_name, enum_item) in self.enums.iter_mut() { - let updated_key = visitor.visit_enum_key(enum_name); + let updated_key = visitor.visit_enum_key(&mut enum_name.clone()); let is_error = errors_clone.contains(enum_item.name()); visitor.visit_enum(is_error, enum_item); @@ -58,6 +58,12 @@ impl ComponentInterface { for function in self.functions.iter_mut() { visitor.visit_function(function); + if function.clone().return_type.is_some() { + let mut return_type = function.clone().return_type.unwrap(); + visitor.visit_type(&mut return_type); + function.return_type = Some(return_type); + } + for argument in function.arguments.iter_mut() { visitor.visit_argument(argument); visitor.visit_type(&mut argument.type_); @@ -74,6 +80,12 @@ impl ComponentInterface { visitor.visit_argument(argument); visitor.visit_type(&mut argument.type_); } + + if method.clone().return_type.is_some() { + let mut return_type = method.clone().return_type.unwrap(); + visitor.visit_type(&mut return_type); + method.return_type = Some(return_type); + } } for constructor in object.constructors.iter_mut() { @@ -124,7 +136,7 @@ impl ComponentInterface { let mut new_records: BTreeMap = BTreeMap::new(); for (key, record) in self.records.iter() { - new_records.insert(visitor.visit_record_key(key), record.clone()); + new_records.insert(visitor.visit_record_key(&mut key.clone()), record.clone()); } self.records = new_records; diff --git a/uniffi_bindgen/src/lib.rs b/uniffi_bindgen/src/lib.rs index 561fefa903..2e5afe5456 100644 --- a/uniffi_bindgen/src/lib.rs +++ b/uniffi_bindgen/src/lib.rs @@ -182,7 +182,7 @@ pub trait VisitMut { /// Change the index key of a `Record` entry in the [`ComponentInterface`] /// so it adheres to the name the associated `Record` /// based on language specific naming conventions. - fn visit_record_key(&self, key: &str) -> String; + fn visit_record_key(&self, key: &mut String) -> String; /// Change the name of a `Field` of an `Enum` `Variant` /// to language specific naming conventions. @@ -203,7 +203,7 @@ pub trait VisitMut { /// Change the naming of the key in the [`ComponentInterface`] /// `BTreeMap` where all `Enum`s are stored to reflect the changed /// name of an `Enum`. - fn visit_enum_key(&self, key: &str) -> String; + fn visit_enum_key(&self, key: &mut String) -> String; /// Go through each `Variant` of an `Enum` and /// adjust it to language specific naming conventions. diff --git a/uniffi_meta/src/types.rs b/uniffi_meta/src/types.rs index fb89e8e9b1..5f62776c4d 100644 --- a/uniffi_meta/src/types.rs +++ b/uniffi_meta/src/types.rs @@ -149,48 +149,48 @@ impl Type { Type::Object { name, .. } => Some(name.to_string()), Type::Record { name, .. } => Some(name.to_string()), Type::Enum { name, .. } => Some(name.to_string()), - Type::Custom { name, .. } => Some(name.to_string()), Type::External { name, .. } => Some(name.to_string()), - Type::Optional { inner_type } | Type::Sequence { inner_type } => { - if inner_type.name().is_some() { - Some(inner_type.name().unwrap()) - } else { - None - } - } - Type::Map { - value_type, - key_type, - } => key_type.name().or_else(|| value_type.name()), + Type::Custom { name, .. } => Some(name.to_string()), + Type::Optional { inner_type } | Type::Sequence { inner_type } => inner_type.name(), _ => None, } } - pub fn rename(&mut self, new_name: String) { + fn rename(&mut self, new_name: String) { match self { Type::Object { name, .. } => *name = new_name, Type::Record { name, .. } => *name = new_name, Type::Enum { name, .. } => *name = new_name, + Type::External { name, .. } => *name = new_name, Type::Custom { name, .. } => *name = new_name, - Type::External { name, .. } => { - *name = new_name; + Type::Optional { inner_type } | Type::Sequence { inner_type } => { + inner_type.rename(new_name); } + _ => {} + } + } + + pub fn rename_recursive(&mut self, name_transformer: &impl Fn(&str) -> String) { + // Rename the current type if it has a name + if let Some(name) = self.name() { + self.rename(name_transformer(&name)); + } + + // Recursively rename nested types + match self { Type::Optional { inner_type } | Type::Sequence { inner_type } => { - if inner_type.name().is_some() { - inner_type.rename(new_name); - } + inner_type.rename_recursive(name_transformer); } Type::Map { - value_type, key_type, + value_type, + .. } => { - if value_type.name().is_some() { - value_type.rename(new_name.clone()); - } - - if key_type.name().is_some() { - key_type.rename(new_name); - } + key_type.rename_recursive(name_transformer); + value_type.rename_recursive(name_transformer); + } + Type::Custom { builtin, .. } => { + builtin.rename_recursive(name_transformer); } _ => {} } From 3cb19d86003c120825564f72bf63cf434169a3e1 Mon Sep 17 00:00:00 2001 From: Murph Murphy Date: Thu, 1 Aug 2024 08:06:22 -0600 Subject: [PATCH 54/62] Provide more external type information to `FfiType::RustBuffer` (#2195) * Thread `ExternalType` metadata into RustBuffer In service of Java bindgen being able to generate fully qualified `RustBuffer`s when necessary. * Switch to `test --no-run` from `build` for cdylib `uniffi-bindgen-java` is external to the uniffi repo, so the fixtures/examples are all `dev-dependencies`, which aren't built on a call to `cargo build`. `cargo test --no-run` causes them to be built but doesn't cause a run of tests in place. --- .../src/bindings/kotlin/gen_kotlin/mod.rs | 7 ++++--- .../src/bindings/python/gen_python/mod.rs | 10 ++++++---- uniffi_bindgen/src/interface/ffi.rs | 17 +++++++++++++++-- uniffi_testing/src/lib.rs | 3 ++- 4 files changed, 27 insertions(+), 10 deletions(-) diff --git a/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/mod.rs b/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/mod.rs index 2b99d8be3b..92f2f39c33 100644 --- a/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/mod.rs +++ b/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/mod.rs @@ -431,9 +431,10 @@ impl KotlinCodeOracle { FfiType::Float64 => "Double".to_string(), FfiType::Handle => "Long".to_string(), FfiType::RustArcPtr(_) => "Pointer".to_string(), - FfiType::RustBuffer(maybe_suffix) => { - format!("RustBuffer{}", maybe_suffix.as_deref().unwrap_or_default()) - } + FfiType::RustBuffer(maybe_external) => match maybe_external { + Some(external_meta) => format!("RustBuffer{}", external_meta.name), + None => "RustBuffer".to_string(), + }, FfiType::RustCallStatus => "UniffiRustCallStatus.ByValue".to_string(), FfiType::ForeignBytes => "ForeignBytes.ByValue".to_string(), FfiType::Callback(name) => self.ffi_callback_name(name), diff --git a/uniffi_bindgen/src/bindings/python/gen_python/mod.rs b/uniffi_bindgen/src/bindings/python/gen_python/mod.rs index 61ab04166e..58dda3c16b 100644 --- a/uniffi_bindgen/src/bindings/python/gen_python/mod.rs +++ b/uniffi_bindgen/src/bindings/python/gen_python/mod.rs @@ -377,8 +377,8 @@ impl PythonCodeOracle { FfiType::Float64 => "ctypes.c_double".to_string(), FfiType::Handle => "ctypes.c_uint64".to_string(), FfiType::RustArcPtr(_) => "ctypes.c_void_p".to_string(), - FfiType::RustBuffer(maybe_suffix) => match maybe_suffix { - Some(suffix) => format!("_UniffiRustBuffer{suffix}"), + FfiType::RustBuffer(maybe_external) => match maybe_external { + Some(external_meta) => format!("_UniffiRustBuffer{}", external_meta.name), None => "_UniffiRustBuffer".to_string(), }, FfiType::RustCallStatus => "_UniffiRustCallStatus".to_string(), @@ -407,8 +407,10 @@ impl PythonCodeOracle { | FfiType::Int64 => "0".to_owned(), FfiType::Float32 | FfiType::Float64 => "0.0".to_owned(), FfiType::RustArcPtr(_) => "ctypes.c_void_p()".to_owned(), - FfiType::RustBuffer(maybe_suffix) => match maybe_suffix { - Some(suffix) => format!("_UniffiRustBuffer{suffix}.default()"), + FfiType::RustBuffer(maybe_external) => match maybe_external { + Some(external_meta) => { + format!("_UniffiRustBuffer{}.default()", external_meta.name) + } None => "_UniffiRustBuffer.default()".to_owned(), }, _ => unimplemented!("FFI return type: {t:?}"), diff --git a/uniffi_bindgen/src/interface/ffi.rs b/uniffi_bindgen/src/interface/ffi.rs index a1dc29713a..91343e1759 100644 --- a/uniffi_bindgen/src/interface/ffi.rs +++ b/uniffi_bindgen/src/interface/ffi.rs @@ -43,7 +43,7 @@ pub enum FfiType { /// or pass it to someone that will. /// If the inner option is Some, it is the name of the external type. The bindings may need /// to use this name to import the correct RustBuffer for that type. - RustBuffer(Option), + RustBuffer(Option), /// A borrowed reference to some raw bytes owned by foreign language code. /// The provider of this reference must keep it alive for the duration of the receiving call. ForeignBytes, @@ -144,13 +144,26 @@ impl From<&Type> for FfiType { Type::External { name, kind: ExternalKind::DataClass, + module_path, + namespace, .. - } => FfiType::RustBuffer(Some(name.clone())), + } => FfiType::RustBuffer(Some(ExternalFfiMetadata { + name: name.clone(), + module_path: module_path.clone(), + namespace: namespace.clone(), + })), Type::Custom { builtin, .. } => FfiType::from(builtin.as_ref()), } } } +#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +pub struct ExternalFfiMetadata { + pub name: String, + pub module_path: String, + pub namespace: String, +} + // Needed for rust scaffolding askama template impl From for FfiType { fn from(ty: Type) -> Self { diff --git a/uniffi_testing/src/lib.rs b/uniffi_testing/src/lib.rs index 79c95bb023..444a7f8146 100644 --- a/uniffi_testing/src/lib.rs +++ b/uniffi_testing/src/lib.rs @@ -163,7 +163,8 @@ fn get_cargo_metadata() -> Metadata { fn get_cargo_build_messages() -> Vec { let mut child = Command::new(env!("CARGO")) - .arg("build") + .arg("test") + .arg("--no-run") .arg("--message-format=json") .stdout(Stdio::piped()) .spawn() From 1f0e0f7867d82dab3081a908886c4eefc9997258 Mon Sep 17 00:00:00 2001 From: Bastian Gruber Date: Thu, 1 Aug 2024 12:32:09 -0300 Subject: [PATCH 55/62] fix: pr review cleanup --- uniffi_bindgen/src/bindings/python/gen_python/mod.rs | 4 ---- uniffi_bindgen/src/interface/mod.rs | 2 +- .../src/interface/{conventions.rs => visit_mut.rs} | 10 ++++++---- uniffi_bindgen/src/lib.rs | 5 ----- uniffi_meta/src/types.rs | 3 --- 5 files changed, 7 insertions(+), 17 deletions(-) rename uniffi_bindgen/src/interface/{conventions.rs => visit_mut.rs} (94%) diff --git a/uniffi_bindgen/src/bindings/python/gen_python/mod.rs b/uniffi_bindgen/src/bindings/python/gen_python/mod.rs index fdaa10f212..81a3c4f3d3 100644 --- a/uniffi_bindgen/src/bindings/python/gen_python/mod.rs +++ b/uniffi_bindgen/src/bindings/python/gen_python/mod.rs @@ -450,10 +450,6 @@ impl VisitMut for PythonCodeOracle { object.rename(self.class_name(object.name())); } - fn visit_record_key(&self, key: &mut String) -> String { - self.class_name(key) - } - fn visit_field(&self, field: &mut Field) { field.rename(self.var_name(field.name())); } diff --git a/uniffi_bindgen/src/interface/mod.rs b/uniffi_bindgen/src/interface/mod.rs index 1e90b8a097..3ea3030ff4 100644 --- a/uniffi_bindgen/src/interface/mod.rs +++ b/uniffi_bindgen/src/interface/mod.rs @@ -66,7 +66,7 @@ pub use object::{Constructor, Method, Object, UniffiTrait}; mod record; pub use record::{Field, Record}; -mod conventions; +mod visit_mut; pub mod ffi; pub use ffi::{ FfiArgument, FfiCallbackFunction, FfiDefinition, FfiField, FfiFunction, FfiStruct, FfiType, diff --git a/uniffi_bindgen/src/interface/conventions.rs b/uniffi_bindgen/src/interface/visit_mut.rs similarity index 94% rename from uniffi_bindgen/src/interface/conventions.rs rename to uniffi_bindgen/src/interface/visit_mut.rs index 48aa530015..76cefd7bb2 100644 --- a/uniffi_bindgen/src/interface/conventions.rs +++ b/uniffi_bindgen/src/interface/visit_mut.rs @@ -4,6 +4,8 @@ use std::collections::{BTreeMap, BTreeSet}; use uniffi_meta::Type; impl ComponentInterface { + /// A generic interface for mutating items in the [`ComponentInterface`]. + /// /// Walk down the [`ComponentInterface`] and adjust the names of each type /// based on the naming conventions of the supported languages. /// @@ -24,7 +26,6 @@ impl ComponentInterface { self.types.all_known_types = all_known_types_altered; - self.fix_keys_after_rename(visitor); let mut updated_enums: BTreeMap = BTreeMap::new(); let errors_clone = self.errors.clone(); @@ -54,6 +55,7 @@ impl ComponentInterface { visitor.visit_type(&mut field.type_); } } + self.fix_record_keys_after_rename(); for function in self.functions.iter_mut() { visitor.visit_function(function); @@ -132,11 +134,11 @@ impl ComponentInterface { } } - fn fix_keys_after_rename(&mut self, visitor: &V) { + fn fix_record_keys_after_rename(&mut self) { let mut new_records: BTreeMap = BTreeMap::new(); - for (key, record) in self.records.iter() { - new_records.insert(visitor.visit_record_key(&mut key.clone()), record.clone()); + for record in self.records.values() { + new_records.insert(record.name().to_string(), record.clone()); } self.records = new_records; diff --git a/uniffi_bindgen/src/lib.rs b/uniffi_bindgen/src/lib.rs index 2e5afe5456..a248a617c2 100644 --- a/uniffi_bindgen/src/lib.rs +++ b/uniffi_bindgen/src/lib.rs @@ -179,11 +179,6 @@ pub trait VisitMut { /// to language specific naming conventions. fn visit_object(&self, object: &mut Object); - /// Change the index key of a `Record` entry in the [`ComponentInterface`] - /// so it adheres to the name the associated `Record` - /// based on language specific naming conventions. - fn visit_record_key(&self, key: &mut String) -> String; - /// Change the name of a `Field` of an `Enum` `Variant` /// to language specific naming conventions. fn visit_field(&self, field: &mut Field); diff --git a/uniffi_meta/src/types.rs b/uniffi_meta/src/types.rs index 5f62776c4d..7fe55d81ac 100644 --- a/uniffi_meta/src/types.rs +++ b/uniffi_meta/src/types.rs @@ -163,9 +163,6 @@ impl Type { Type::Enum { name, .. } => *name = new_name, Type::External { name, .. } => *name = new_name, Type::Custom { name, .. } => *name = new_name, - Type::Optional { inner_type } | Type::Sequence { inner_type } => { - inner_type.rename(new_name); - } _ => {} } } From 2d16dd49cb16072f059c9fe4844022bf2aa70a0d Mon Sep 17 00:00:00 2001 From: Bastian Gruber Date: Thu, 1 Aug 2024 12:34:53 -0300 Subject: [PATCH 56/62] fix: cargo fmt --- uniffi_bindgen/src/interface/mod.rs | 2 +- uniffi_bindgen/src/interface/visit_mut.rs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/uniffi_bindgen/src/interface/mod.rs b/uniffi_bindgen/src/interface/mod.rs index 3ea3030ff4..ec61f20cd7 100644 --- a/uniffi_bindgen/src/interface/mod.rs +++ b/uniffi_bindgen/src/interface/mod.rs @@ -66,8 +66,8 @@ pub use object::{Constructor, Method, Object, UniffiTrait}; mod record; pub use record::{Field, Record}; -mod visit_mut; pub mod ffi; +mod visit_mut; pub use ffi::{ FfiArgument, FfiCallbackFunction, FfiDefinition, FfiField, FfiFunction, FfiStruct, FfiType, }; diff --git a/uniffi_bindgen/src/interface/visit_mut.rs b/uniffi_bindgen/src/interface/visit_mut.rs index 76cefd7bb2..31d03ae0fc 100644 --- a/uniffi_bindgen/src/interface/visit_mut.rs +++ b/uniffi_bindgen/src/interface/visit_mut.rs @@ -26,7 +26,6 @@ impl ComponentInterface { self.types.all_known_types = all_known_types_altered; - let mut updated_enums: BTreeMap = BTreeMap::new(); let errors_clone = self.errors.clone(); for (enum_name, enum_item) in self.enums.iter_mut() { From 4f97413c73a2c5cb4b564ed3600fcd9c16688f65 Mon Sep 17 00:00:00 2001 From: Ben Dean-Kawamura Date: Wed, 31 Jul 2024 10:52:07 -0400 Subject: [PATCH 57/62] Adding changelog entries for merges since 0.28.0 I'm hoping to do a 0.28.1 release and these would be good to have in it. I also considered adding an entry for the work that mgeisler has been doing, but I couldn't think of a good wording for that. --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a1b47eed4..0b6ceaf3bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,15 @@ ## [[UnreleasedUniFFIVersion]] (backend crates: [[UnreleasedBackendVersion]]) - (_[[ReleaseDate]]_) +### What's new? + +- Lift errors will not cause an abort when `panic=abort` is set. +- Added the `cargo_metadata` feature, which is on by default. In some cases, this can be disabled + for better compatibility with projects that don't use cargo. + +### What's changed? +- Kotlin will use the more efficient Enum.entries property instead of Enum.values() when possible + [All changes in [[UnreleasedUniFFIVersion]]](https://github.com/mozilla/uniffi-rs/compare/v0.28.0...HEAD). ## v0.28.0 (backend crates: v0.28.0) - (_2024-06-11_) From 7ff7584ce06a2d5160d598b319689d0f691c9ddf Mon Sep 17 00:00:00 2001 From: Mark Hammond Date: Fri, 2 Aug 2024 16:18:45 -0400 Subject: [PATCH 58/62] Allow UDL to avoid the `[Rust=...]` attribute by using a plain-old typedef (#2199) --- CHANGELOG.md | 8 +- docs/manual/src/udl/ext_types.md | 22 ++- .../src/proc-macro.udl | 5 +- fixtures/proc-macro/src/proc-macro.udl | 19 +- uniffi_udl/src/finder.rs | 185 ++++++++++++++---- 5 files changed, 176 insertions(+), 63 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 85db71c41b..a60edca0f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,10 @@ - Lift errors will not cause an abort when `panic=abort` is set. - Added the `cargo_metadata` feature, which is on by default. In some cases, this can be disabled for better compatibility with projects that don't use cargo. +- A new bindgen command line option `--metadata-no-deps` is available to avoid processing + cargo_metadata for all dependencies. +- In UDL it's now possible (and preferred) to remove the `[Rust=]` attribute and use a plain-old typedef. + See [the manual page for this](https://mozilla.github.io/uniffi-rs/next/udl/ext_types.html#types-from-procmacros-in-this-crate). ### What's changed? - Kotlin will use the more efficient Enum.entries property instead of Enum.values() when possible @@ -20,10 +24,6 @@ ## v0.28.0 (backend crates: v0.28.0) - (_2024-06-11_) ### What's new? - -- A new bindgen command line option `--metadata-no-deps` is available to avoid processing - cargo_metadata for all dependencies. - - Objects error types can now be as `Result<>` error type without wrapping them in `Arc<>`. - Swift errors now provide `localizedDescription` ([#2116](https://github.com/mozilla/uniffi-rs/pull/2116)) diff --git a/docs/manual/src/udl/ext_types.md b/docs/manual/src/udl/ext_types.md index 4ad4139c5e..8e25c1aa70 100644 --- a/docs/manual/src/udl/ext_types.md +++ b/docs/manual/src/udl/ext_types.md @@ -14,8 +14,7 @@ giving more detail. ## Types from procmacros in this crate. If your crate has types defined via `#[uniffi::export]` etc you can make them available -to the UDL file in your own crate via a `typedef` with a `[Rust=]` attribute. Eg, your Rust -might have: +to the UDL file in your own crate via a `typedef` describing the concrete type. ```rust #[derive(uniffi::Record)] @@ -26,8 +25,7 @@ pub struct One { you can use it in your UDL: ```idl -[Rust="record"] -typedef extern One; +typedef record One; namespace app { // use the procmacro type. @@ -37,6 +35,16 @@ namespace app { ``` Supported values: -* "enum", "trait", "callback", "trait_with_foreign" -* For records, either "record" or "dictionary" -* For objects, either "object" or "interface" +* "enum", "trait", "callback", "trait_with_foreign" +* For records, either "record", "dictionary" or "struct" +* For objects, either "object", "impl" or "interface" + +eg: +``` +typedef enum MyEnum; +typedef interface MyObject; +``` +etc. + +Note that in 0.28 and prior, we also supported this capability with a `[Rust=]` attribute. +This attribute is deprecated and may be removed in a later version. diff --git a/fixtures/proc-macro-no-implicit-prelude/src/proc-macro.udl b/fixtures/proc-macro-no-implicit-prelude/src/proc-macro.udl index 9896f2e633..8eeafe13cd 100644 --- a/fixtures/proc-macro-no-implicit-prelude/src/proc-macro.udl +++ b/fixtures/proc-macro-no-implicit-prelude/src/proc-macro.udl @@ -1,9 +1,10 @@ -// Use this to test that types defined in the UDL can be used in the proc-macros +// Like our proc-macro fixture, but tests everything works without Rust `std::` type preludes. dictionary Zero { string inner; }; -// And all of these for the opposite - proc-macro types used in UDL. +// NOTE: `[Rust=..]` is deprecated and this test hasn't migrated. +// This helps testing the attribute, so don't remove them unless you are removing support entirely! [Rust="record"] typedef extern One; diff --git a/fixtures/proc-macro/src/proc-macro.udl b/fixtures/proc-macro/src/proc-macro.udl index 9896f2e633..84f096ca66 100644 --- a/fixtures/proc-macro/src/proc-macro.udl +++ b/fixtures/proc-macro/src/proc-macro.udl @@ -4,20 +4,11 @@ dictionary Zero { }; // And all of these for the opposite - proc-macro types used in UDL. -[Rust="record"] -typedef extern One; - -[Rust="enum"] -typedef extern MaybeBool; - -[Rust="interface"] -typedef extern Object; - -[Rust="trait"] -typedef extern Trait; - -[Rust="trait_with_foreign"] -typedef extern TraitWithForeign; +typedef record One; +typedef enum MaybeBool; +typedef interface Object; +typedef trait Trait; +typedef trait_with_foreign TraitWithForeign; // Then stuff defined here but referencing the imported types. dictionary Externals { diff --git a/uniffi_udl/src/finder.rs b/uniffi_udl/src/finder.rs index 259557ad07..6a2b6084b8 100644 --- a/uniffi_udl/src/finder.rs +++ b/uniffi_udl/src/finder.rs @@ -129,43 +129,89 @@ impl TypeFinder for weedle::TypedefDefinition<'_> { }, ) } else { + let typedef_type = match &self.type_.type_ { + weedle::types::Type::Single(weedle::types::SingleType::NonAny( + weedle::types::NonAnyType::Identifier(weedle::types::MayBeNull { + type_: i, + .. + }), + )) => i.0, + _ => bail!("Failed to get typedef type: {:?}", self), + }; + let module_path = types.module_path(); let name = self.identifier.0.to_string(); - let ty = match attrs.rust_kind() { - Some(RustKind::Object) => Type::Object { - module_path, - name, - imp: ObjectImpl::Struct, - }, - Some(RustKind::Trait) => Type::Object { - module_path, - name, - imp: ObjectImpl::Trait, - }, - Some(RustKind::CallbackTrait) => Type::Object { - module_path, - name, - imp: ObjectImpl::CallbackTrait, - }, - Some(RustKind::Record) => Type::Record { module_path, name }, - Some(RustKind::Enum) => Type::Enum { module_path, name }, - Some(RustKind::CallbackInterface) => Type::CallbackInterface { module_path, name }, - // must be external + + let ty = match attrs.external_tagged() { None => { - let kind = attrs.external_kind().expect("External missing kind"); - let tagged = attrs.external_tagged().expect("External missing tagged"); - Type::External { - name, - namespace: "".to_string(), // we don't know this yet - module_path: attrs.get_crate_name(), - kind, - tagged, + // Not external, not custom, not Rust - so we basically + // pretend it is Rust, thus soft-deprecating it. + // We use `type_` + match typedef_type { + "dictionary" | "record" | "struct" => Type::Record { + module_path, + name, + }, + "enum" => Type::Enum { + module_path, + name, + }, + "custom" => panic!("don't know builtin"), + "interface" | "impl" => Type::Object { + module_path, + name, + imp: ObjectImpl::Struct, + }, + "trait" => Type::Object { + module_path, + name, + imp: ObjectImpl::Trait, + }, + "callback" | "trait_with_foreign" => Type::Object { + module_path, + name, + imp: ObjectImpl::CallbackTrait, + }, + _ => bail!("Can't work out the type - no attributes and unknown extern type '{typedef_type}'"), + } + } + Some(tagged) => { + // Must be either `[Rust..]` or `[Extern..]` + match attrs.rust_kind() { + Some(RustKind::Object) => Type::Object { + module_path, + name, + imp: ObjectImpl::Struct, + }, + Some(RustKind::Trait) => Type::Object { + module_path, + name, + imp: ObjectImpl::Trait, + }, + Some(RustKind::CallbackTrait) => Type::Object { + module_path, + name, + imp: ObjectImpl::CallbackTrait, + }, + Some(RustKind::Record) => Type::Record { module_path, name }, + Some(RustKind::Enum) => Type::Enum { module_path, name }, + Some(RustKind::CallbackInterface) => { + Type::CallbackInterface { module_path, name } + } + // must be external + None => { + let kind = attrs.external_kind().expect("External missing kind"); + Type::External { + name, + namespace: "".to_string(), // we don't know this yet + module_path: attrs.get_crate_name(), + kind, + tagged, + } + } } } }; - // A crate which can supply an `FfiConverter`. - // We don't reference `self._type`, so ideally we could insist on it being - // the literal 'extern' but that's tricky types.add_type_definition(self.identifier.0, ty) } } @@ -190,7 +236,7 @@ impl TypeFinder for weedle::CallbackInterfaceDefinition<'_> { #[cfg(test)] mod test { use super::*; - use uniffi_meta::ExternalKind; + use uniffi_meta::{ExternalKind, ObjectImpl}; // A helper to take valid UDL and a closure to check what's in it. fn test_a_finding(udl: &str, tester: F) @@ -289,6 +335,74 @@ mod test { ); } + #[test] + fn test_extern_local_types() { + // should test more, but these are already deprecated + test_a_finding( + r#" + typedef interface Interface; + typedef impl Interface2; + typedef trait Trait; + typedef callback Callback; + + typedef dictionary R1; + typedef record R2; + typedef record R3; + typedef enum Enum; + "#, + |types| { + assert!(matches!( + types.get_type_definition("Interface").unwrap(), + Type::Object { name, module_path, imp: ObjectImpl::Struct } if name == "Interface" && module_path.is_empty())); + assert!(matches!( + types.get_type_definition("Interface2").unwrap(), + Type::Object { name, module_path, imp: ObjectImpl::Struct } if name == "Interface2" && module_path.is_empty())); + assert!(matches!( + types.get_type_definition("Trait").unwrap(), + Type::Object { name, module_path, imp: ObjectImpl::Trait } if name == "Trait" && module_path.is_empty())); + assert!(matches!( + types.get_type_definition("Callback").unwrap(), + Type::Object { name, module_path, imp: ObjectImpl::CallbackTrait } if name == "Callback" && module_path.is_empty())); + assert!(matches!( + types.get_type_definition("R1").unwrap(), + Type::Record { name, module_path } if name == "R1" && module_path.is_empty())); + assert!(matches!( + types.get_type_definition("R2").unwrap(), + Type::Record { name, module_path } if name == "R2" && module_path.is_empty())); + assert!(matches!( + types.get_type_definition("R3").unwrap(), + Type::Record { name, module_path } if name == "R3" && module_path.is_empty())); + assert!(matches!( + types.get_type_definition("Enum").unwrap(), + Type::Enum { name, module_path } if name == "Enum" && module_path.is_empty())); + }, + ); + } + + #[test] + fn test_rust_attr_types() { + // should test more, but these are already deprecated + test_a_finding( + r#" + [Rust="interface"] + typedef extern LocalInterface; + + [Rust="dictionary"] + typedef extern Dict; + "#, + |types| { + assert!( + matches!(types.get_type_definition("LocalInterface").unwrap(), Type::Object { name, module_path, imp: ObjectImpl::Struct } + if name == "LocalInterface" && module_path.is_empty()) + ); + assert!( + matches!(types.get_type_definition("Dict").unwrap(), Type::Record { name, module_path } + if name == "Dict" && module_path.is_empty()) + ); + }, + ); + } + fn get_err(udl: &str) -> String { let parsed = weedle::parse(udl).unwrap(); let mut types = TypeCollector::default(); @@ -299,9 +413,8 @@ mod test { } #[test] - #[should_panic] - fn test_typedef_error_on_no_attr() { - // Sorry, still working out what we want for non-imported typedefs.. - get_err("typedef string Custom;"); + fn test_local_type_unknown_typedef() { + let e = get_err("typedef xyz Foo;"); + assert!(e.contains("unknown extern type 'xyz'")); } } From 33a24e6cfe016b1f018a58fa1f2ea85848850b5f Mon Sep 17 00:00:00 2001 From: Mark Hammond Date: Wed, 7 Aug 2024 09:28:28 -0400 Subject: [PATCH 59/62] Future cancellation docs and other minor doc tweaks. (#2206) --- docs/manual/src/futures.md | 10 ++++++++++ .../src/tutorial/foreign_language_bindings.md | 13 ++++++------- docs/manual/src/udl/ext_types.md | 8 ++++---- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/docs/manual/src/futures.md b/docs/manual/src/futures.md index 81d88b2a18..b2a5163d57 100644 --- a/docs/manual/src/futures.md +++ b/docs/manual/src/futures.md @@ -94,3 +94,13 @@ Use `uniffi_set_event_loop()` to handle this case. It should be called before the Rust code makes the async call and passed an eventloop to use. Note that `uniffi_set_event_loop` cannot be glob-imported because it's not part of the library's `__all__`. + +## Cancelling async code. + +We don't directly support cancellation in UniFFI even when the underlying platforms do. +You should build your cancellation in a separate, library specific channel; for example, exposing a `cancel()` method that sets a flag that the library checks periodically. + +Cancellation can then be exposed in the API and be mapped to one of the error variants, or None/empty-vec/whatever makes sense. +There's no builtin way to cancel a future, nor to cause/raise a platform native async cancellation error (eg, a swift `CancellationError`). + +See also https://github.com/mozilla/uniffi-rs/pull/1768. diff --git a/docs/manual/src/tutorial/foreign_language_bindings.md b/docs/manual/src/tutorial/foreign_language_bindings.md index b2ab9d3b5a..53a9f47f53 100644 --- a/docs/manual/src/tutorial/foreign_language_bindings.md +++ b/docs/manual/src/tutorial/foreign_language_bindings.md @@ -8,9 +8,9 @@ The next step is to have UniFFI generate source code for your foreign language. First, make sure you have installed all the [prerequisites](./Prerequisites.md). -Ideally you would then run the `uniffi-bindgen` binary from the `uniffi` crate to generate your bindings. However, this -is only available with [Cargo nightly](https://doc.rust-lang.org/cargo/reference/unstable.html#artifact-dependencies). -To work around this, you need to create a binary in your project that does the same thing. +Ideally you would then run the `uniffi-bindgen` binary from the `uniffi` crate to generate your bindings, +but if not on [Cargo nightly](https://doc.rust-lang.org/cargo/reference/unstable.html#artifact-dependencies), +you need to create a binary in your project that does the same thing. Add the following to your `Cargo.toml`: @@ -32,11 +32,10 @@ You can now run `uniffi-bindgen` from your project using `cargo run --features=u ### Multi-crate workspaces -If your project consists of multiple crates in a Cargo workspace, then the process outlined above would require you -creating a binary for each crate that uses UniFFI. You can avoid this by creating a separate crate for running `uniffi-bindgen`: - - Name the crate `uniffi-bindgen` +In a multiple crates workspace, you can create a separate crate for running `uniffi-bindgen`: + - Name the crate `uniffi-bindgen`, add it to your workspace. - Add this dependency to `Cargo.toml`: `uniffi = {version = "0.XX.0", features = ["cli"] }` - - Follow the steps from the previous section to add the `uniffi-bindgen` binary target + - As above, add the `uniffi-bindgen` binary target Then your can run `uniffi-bindgen` from any crate in your project using `cargo run -p uniffi-bindgen [args]` diff --git a/docs/manual/src/udl/ext_types.md b/docs/manual/src/udl/ext_types.md index 8e25c1aa70..65bfb66937 100644 --- a/docs/manual/src/udl/ext_types.md +++ b/docs/manual/src/udl/ext_types.md @@ -35,16 +35,16 @@ namespace app { ``` Supported values: -* "enum", "trait", "callback", "trait_with_foreign" -* For records, either "record", "dictionary" or "struct" -* For objects, either "object", "impl" or "interface" +* Enums: `enum` +* Records: `record`, `dictionary` or `struct` +* Objects: `object`, `impl` or `interface` +* Traits: `trait`, `callback` or `trait_with_foreign` eg: ``` typedef enum MyEnum; typedef interface MyObject; ``` -etc. Note that in 0.28 and prior, we also supported this capability with a `[Rust=]` attribute. This attribute is deprecated and may be removed in a later version. From 06f10d09dad4c304889de05e706fcaf26d7511c5 Mon Sep 17 00:00:00 2001 From: Ben Dean-Kawamura Date: Tue, 6 Aug 2024 13:50:52 -0400 Subject: [PATCH 60/62] Expose the find_components function The generate_bindings docs mentioned wanting to expose a function that just finds ComponentInterface and config tables for each component and leaves the rest to the external bindings generator. This is exactly what I want to use for uniffi-bindgen-gecko-js. The `find_components` function was pretty much exactly what we wanted. I made some minor changes to it and exposed it as a pub function. --- uniffi_bindgen/src/lib.rs | 7 +++--- uniffi_bindgen/src/library_mode.rs | 34 +++++++++++++++++++----------- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/uniffi_bindgen/src/lib.rs b/uniffi_bindgen/src/lib.rs index a248a617c2..6d32bd4784 100644 --- a/uniffi_bindgen/src/lib.rs +++ b/uniffi_bindgen/src/lib.rs @@ -115,6 +115,7 @@ use crate::interface::{ Variant, }; pub use interface::ComponentInterface; +pub use library_mode::find_components; use scaffolding::RustScaffolding; use uniffi_meta::Type; @@ -297,7 +298,8 @@ pub fn generate_external_bindings( let config = { let crate_config = load_toml_file(Some(&crate_root.join("uniffi.toml"))) .context("failed to load {crate_root}/uniffi.toml")?; - let toml_value = overridden_config_value(crate_config, config_file_override)?; + let toml_value = + overridden_config_value(crate_config.unwrap_or_default(), config_file_override)?; binding_generator.new_config(&toml_value)? }; @@ -497,10 +499,9 @@ fn load_toml_file(source: Option<&Utf8Path>) -> Result, + mut config: toml::value::Table, config_file_override: Option<&Utf8Path>, ) -> Result { - let mut config = config.unwrap_or_default(); let override_config = load_toml_file(config_file_override).context("override config")?; if let Some(override_config) = override_config { merge_toml(&mut config, override_config); diff --git a/uniffi_bindgen/src/library_mode.rs b/uniffi_bindgen/src/library_mode.rs index b8e919bfbf..3a6a3f0133 100644 --- a/uniffi_bindgen/src/library_mode.rs +++ b/uniffi_bindgen/src/library_mode.rs @@ -22,17 +22,18 @@ use crate::{ use anyhow::bail; use camino::Utf8Path; use std::{collections::HashMap, fs}; +use toml::value::Table as TomlTable; use uniffi_meta::{ create_metadata_groups, fixup_external_type, group_metadata, Metadata, MetadataGroup, }; /// Generate foreign bindings /// +/// This replicates the current process used for generating the builtin bindings. +/// External bindings authors should consider using [find_components], which provides a simpler +/// interface and allows for more flexibility in how the external bindings are generated. +/// /// Returns the list of sources used to generate the bindings, in no particular order. -// XXX - we should consider killing this function and replace it with a function -// which just locates the `Components` and returns them, leaving the filtering -// and actual generation to the callers, which also would allow removing the potentially -// confusing crate_name param. pub fn generate_bindings( library_path: &Utf8Path, crate_name: Option, @@ -42,11 +43,10 @@ pub fn generate_bindings( out_dir: &Utf8Path, try_format_code: bool, ) -> Result>> { - let mut components = find_components(config_supplier, library_path)? + let mut components = find_components(library_path, config_supplier)? .into_iter() - .map(|ci| { - let crate_toml = config_supplier.get_toml(ci.crate_name())?; - let toml_value = overridden_config_value(crate_toml, config_file_override)?; + .map(|Component { ci, config }| { + let toml_value = overridden_config_value(config, config_file_override)?; let config = binding_generator.new_config(&toml_value)?; Ok(Component { ci, config }) }) @@ -90,10 +90,17 @@ pub fn calc_cdylib_name(library_path: &Utf8Path) -> Option<&str> { None } -fn find_components( - config_supplier: &dyn BindgenCrateConfigSupplier, +/// Find UniFFI components from a shared library file +/// +/// This method inspects the library file and creates [ComponentInterface] instances for each +/// component used to build it. It parses the UDL files from `uniffi::include_scaffolding!` macro +/// calls. +/// +/// `config_supplier` is used to find UDL files on disk and load config data. +pub fn find_components( library_path: &Utf8Path, -) -> Result> { + config_supplier: &dyn BindgenCrateConfigSupplier, +) -> Result>> { let items = macro_metadata::extract_from_library(library_path)?; let mut metadata_groups = create_metadata_groups(&items); group_metadata(&mut metadata_groups, items)?; @@ -128,7 +135,10 @@ fn find_components( ci.add_metadata(metadata)?; }; ci.add_metadata(group)?; - Ok(ci) + let config = config_supplier + .get_toml(ci.crate_name())? + .unwrap_or_default(); + Ok(Component { ci, config }) }) .collect() } From e31172fb75fabb06bba88b1592860ff7a8f4ca55 Mon Sep 17 00:00:00 2001 From: Ben Dean-Kawamura Date: Fri, 9 Aug 2024 15:29:51 -0400 Subject: [PATCH 61/62] chore: Release --- CHANGELOG.md | 6 +++++- Cargo.lock | 16 ++++++++-------- uniffi/Cargo.toml | 8 ++++---- uniffi_bindgen/Cargo.toml | 8 ++++---- uniffi_build/Cargo.toml | 4 ++-- uniffi_checksum_derive/Cargo.toml | 2 +- uniffi_core/Cargo.toml | 2 +- uniffi_macros/Cargo.toml | 6 +++--- uniffi_meta/Cargo.toml | 4 ++-- uniffi_testing/Cargo.toml | 2 +- uniffi_udl/Cargo.toml | 6 +++--- 11 files changed, 34 insertions(+), 30 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a60edca0f8..41f27ebf67 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,11 @@ -## [[UnreleasedUniFFIVersion]] (backend crates: [[UnreleasedBackendVersion]]) - (_[[ReleaseDate]]_) +## [[NextUnreleasedUniFFIVersion]] (backend crates: [[UnreleasedBackendVersion]]) - (_[[ReleaseDate]]_) + +[All changes in [[NextUnreleasedUniFFIVersion]]](https://github.com/mozilla/uniffi-rs/compare/v0.28.1...NEXT_HEAD). + +## [[UnreleasedUniFFIVersion]] (backend crates: v0.28.1) - (_2024-08-09_) ### What's new? diff --git a/Cargo.lock b/Cargo.lock index f135e335a8..8a5d39f851 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1925,7 +1925,7 @@ dependencies = [ [[package]] name = "uniffi_bindgen" -version = "0.28.0" +version = "0.28.1" dependencies = [ "anyhow", "askama", @@ -1947,7 +1947,7 @@ dependencies = [ [[package]] name = "uniffi_build" -version = "0.28.0" +version = "0.28.1" dependencies = [ "anyhow", "camino", @@ -1956,7 +1956,7 @@ dependencies = [ [[package]] name = "uniffi_checksum_derive" -version = "0.28.0" +version = "0.28.1" dependencies = [ "quote", "syn", @@ -1964,7 +1964,7 @@ dependencies = [ [[package]] name = "uniffi_core" -version = "0.28.0" +version = "0.28.1" dependencies = [ "anyhow", "async-compat", @@ -1978,7 +1978,7 @@ dependencies = [ [[package]] name = "uniffi_macros" -version = "0.28.0" +version = "0.28.1" dependencies = [ "bincode", "camino", @@ -1995,7 +1995,7 @@ dependencies = [ [[package]] name = "uniffi_meta" -version = "0.28.0" +version = "0.28.1" dependencies = [ "anyhow", "bytes", @@ -2005,7 +2005,7 @@ dependencies = [ [[package]] name = "uniffi_testing" -version = "0.28.0" +version = "0.28.1" dependencies = [ "anyhow", "camino", @@ -2016,7 +2016,7 @@ dependencies = [ [[package]] name = "uniffi_udl" -version = "0.28.0" +version = "0.28.1" dependencies = [ "anyhow", "textwrap", diff --git a/uniffi/Cargo.toml b/uniffi/Cargo.toml index 72f4918ccd..d26dfefa78 100644 --- a/uniffi/Cargo.toml +++ b/uniffi/Cargo.toml @@ -15,10 +15,10 @@ keywords = ["ffi", "bindgen"] readme = "../README.md" [dependencies] -uniffi_bindgen = { path = "../uniffi_bindgen", version = "=0.28.0", optional = true } -uniffi_build = { path = "../uniffi_build", version = "=0.28.0", optional = true } -uniffi_core = { path = "../uniffi_core", version = "=0.28.0" } -uniffi_macros = { path = "../uniffi_macros", version = "=0.28.0" } +uniffi_bindgen = { path = "../uniffi_bindgen", version = "=0.28.1", optional = true } +uniffi_build = { path = "../uniffi_build", version = "=0.28.1", optional = true } +uniffi_core = { path = "../uniffi_core", version = "=0.28.1" } +uniffi_macros = { path = "../uniffi_macros", version = "=0.28.1" } anyhow = "1" camino = { version = "1.0.8", optional = true } cargo_metadata = { version = "0.15", optional = true } diff --git a/uniffi_bindgen/Cargo.toml b/uniffi_bindgen/Cargo.toml index 640e5f0eba..2e253e5f80 100644 --- a/uniffi_bindgen/Cargo.toml +++ b/uniffi_bindgen/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uniffi_bindgen" -version = "0.28.0" +version = "0.28.1" authors = ["Firefox Sync Team "] description = "a multi-language bindings generator for rust (codegen and cli tooling)" documentation = "https://mozilla.github.io/uniffi-rs" @@ -29,9 +29,9 @@ once_cell = "1.12" paste = "1.0" serde = { version = "1", features = ["derive"] } toml = "0.5" -uniffi_meta = { path = "../uniffi_meta", version = "=0.28.0" } -uniffi_testing = { path = "../uniffi_testing", version = "=0.28.0", optional = true } -uniffi_udl = { path = "../uniffi_udl", version = "=0.28.0" } +uniffi_meta = { path = "../uniffi_meta", version = "=0.28.1" } +uniffi_testing = { path = "../uniffi_testing", version = "=0.28.1", optional = true } +uniffi_udl = { path = "../uniffi_udl", version = "=0.28.1" } # Don't include the `unicode-linebreak` or `unicode-width` since that functionality isn't needed for # docstrings. textwrap = { version = "0.16", features=["smawk"], default-features = false } diff --git a/uniffi_build/Cargo.toml b/uniffi_build/Cargo.toml index 799957928e..e03823837b 100644 --- a/uniffi_build/Cargo.toml +++ b/uniffi_build/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uniffi_build" -version = "0.28.0" +version = "0.28.1" authors = ["Firefox Sync Team "] description = "a multi-language bindings generator for rust (build script helpers)" documentation = "https://mozilla.github.io/uniffi-rs" @@ -14,7 +14,7 @@ readme = "../README.md" [dependencies] anyhow = "1" camino = "1.0.8" -uniffi_bindgen = { path = "../uniffi_bindgen", default-features = false, version = "=0.28.0" } +uniffi_bindgen = { path = "../uniffi_bindgen", default-features = false, version = "=0.28.1" } [features] default = [] diff --git a/uniffi_checksum_derive/Cargo.toml b/uniffi_checksum_derive/Cargo.toml index f68affd79b..a2c93c3ade 100644 --- a/uniffi_checksum_derive/Cargo.toml +++ b/uniffi_checksum_derive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uniffi_checksum_derive" -version = "0.28.0" +version = "0.28.1" authors = ["Firefox Sync Team "] description = "a multi-language bindings generator for rust (checksum custom derive)" documentation = "https://mozilla.github.io/uniffi-rs" diff --git a/uniffi_core/Cargo.toml b/uniffi_core/Cargo.toml index 0273575923..ce67b7f754 100644 --- a/uniffi_core/Cargo.toml +++ b/uniffi_core/Cargo.toml @@ -4,7 +4,7 @@ description = "a multi-language bindings generator for rust (runtime support cod documentation = "https://mozilla.github.io/uniffi-rs" homepage = "https://mozilla.github.io/uniffi-rs" repository = "https://github.com/mozilla/uniffi-rs" -version = "0.28.0" +version = "0.28.1" authors = ["Firefox Sync Team "] license = "MPL-2.0" edition = "2021" diff --git a/uniffi_macros/Cargo.toml b/uniffi_macros/Cargo.toml index 518b7c441a..6825c4de51 100644 --- a/uniffi_macros/Cargo.toml +++ b/uniffi_macros/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uniffi_macros" -version = "0.28.0" +version = "0.28.1" authors = ["Firefox Sync Team "] description = "a multi-language bindings generator for rust (convenience macros)" documentation = "https://mozilla.github.io/uniffi-rs" @@ -24,8 +24,8 @@ quote = "1.0" serde = { version = "1.0.136", features = ["derive"] } syn = { version = "2.0", features = ["full", "visit-mut"] } toml = "0.5.9" -uniffi_build = { path = "../uniffi_build", version = "=0.28.0", optional = true } -uniffi_meta = { path = "../uniffi_meta", version = "=0.28.0" } +uniffi_build = { path = "../uniffi_build", version = "=0.28.1", optional = true } +uniffi_meta = { path = "../uniffi_meta", version = "=0.28.1" } [features] default = [] diff --git a/uniffi_meta/Cargo.toml b/uniffi_meta/Cargo.toml index ed87fcb10c..92b58470f3 100644 --- a/uniffi_meta/Cargo.toml +++ b/uniffi_meta/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uniffi_meta" -version = "0.28.0" +version = "0.28.1" edition = "2021" description = "uniffi_meta" homepage = "https://mozilla.github.io/uniffi-rs" @@ -13,4 +13,4 @@ readme = "../README.md" anyhow = "1" bytes = "1.3" siphasher = "0.3" -uniffi_checksum_derive = { version = "0.28.0", path = "../uniffi_checksum_derive" } +uniffi_checksum_derive = { version = "0.28.1", path = "../uniffi_checksum_derive" } diff --git a/uniffi_testing/Cargo.toml b/uniffi_testing/Cargo.toml index ce4e6ba144..992c919370 100644 --- a/uniffi_testing/Cargo.toml +++ b/uniffi_testing/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uniffi_testing" -version = "0.28.0" +version = "0.28.1" authors = ["Firefox Sync Team "] description = "a multi-language bindings generator for rust (testing helpers)" documentation = "https://mozilla.github.io/uniffi-rs" diff --git a/uniffi_udl/Cargo.toml b/uniffi_udl/Cargo.toml index 6aab799090..beaba92245 100644 --- a/uniffi_udl/Cargo.toml +++ b/uniffi_udl/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "uniffi_udl" -version = "0.28.0" +version = "0.28.1" description = "udl parsing for the uniffi project" documentation = "https://mozilla.github.io/uniffi-rs" homepage = "https://mozilla.github.io/uniffi-rs" @@ -16,5 +16,5 @@ weedle2 = { version = "5.0.0", path = "../weedle2" } # Don't include the `unicode-linebreak` or `unicode-width` since that functionality isn't needed for # docstrings. textwrap = { version = "0.16", features=["smawk"], default-features = false } -uniffi_meta = { path = "../uniffi_meta", version = "=0.28.0" } -uniffi_testing = { path = "../uniffi_testing", version = "=0.28.0" } +uniffi_meta = { path = "../uniffi_meta", version = "=0.28.1" } +uniffi_testing = { path = "../uniffi_testing", version = "=0.28.1" } From 1cdf56b98bc091a04cfba1a1bca3ea1a3cfc9be8 Mon Sep 17 00:00:00 2001 From: Ben Dean-Kawamura Date: Fri, 9 Aug 2024 15:30:23 -0400 Subject: [PATCH 62/62] chore: Release --- CHANGELOG.md | 8 ++++---- Cargo.lock | 2 +- uniffi/Cargo.toml | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 41f27ebf67..432d8242d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,11 +4,11 @@ -## [[NextUnreleasedUniFFIVersion]] (backend crates: [[UnreleasedBackendVersion]]) - (_[[ReleaseDate]]_) +## [[UnreleasedUniFFIVersion]] (backend crates: [[UnreleasedBackendVersion]]) - (_[[ReleaseDate]]_) -[All changes in [[NextUnreleasedUniFFIVersion]]](https://github.com/mozilla/uniffi-rs/compare/v0.28.1...NEXT_HEAD). +[All changes in [[UnreleasedUniFFIVersion]]](https://github.com/mozilla/uniffi-rs/compare/v0.28.1...HEAD). -## [[UnreleasedUniFFIVersion]] (backend crates: v0.28.1) - (_2024-08-09_) +## v0.28.1 (backend crates: v0.28.1) - (_2024-08-09_) ### What's new? @@ -23,7 +23,7 @@ ### What's changed? - Kotlin will use the more efficient Enum.entries property instead of Enum.values() when possible -[All changes in [[UnreleasedUniFFIVersion]]](https://github.com/mozilla/uniffi-rs/compare/v0.28.0...HEAD). +[All changes in v0.28.1](https://github.com/mozilla/uniffi-rs/compare/v0.28.0...v0.28.1). ## v0.28.0 (backend crates: v0.28.0) - (_2024-06-11_) diff --git a/Cargo.lock b/Cargo.lock index 8a5d39f851..18c01bc4a5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1471,7 +1471,7 @@ dependencies = [ [[package]] name = "uniffi" -version = "0.28.0" +version = "0.28.1" dependencies = [ "anyhow", "camino", diff --git a/uniffi/Cargo.toml b/uniffi/Cargo.toml index d26dfefa78..9a4eb1c370 100644 --- a/uniffi/Cargo.toml +++ b/uniffi/Cargo.toml @@ -7,7 +7,7 @@ repository = "https://github.com/mozilla/uniffi-rs" # Incrementing the minor version here means a breaking change to consumers. # * See `docs/uniffi-versioning.md` for guidance on when to increment this # * Make sure to also update `uniffi_bindgen::UNIFFI_CONTRACT_VERSION" -version = "0.28.0" +version = "0.28.1" authors = ["Firefox Sync Team "] license = "MPL-2.0" edition = "2021"