diff --git a/Cargo.lock b/Cargo.lock index 0f672765b80..88a5555799d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3136,6 +3136,7 @@ dependencies = [ "mas-oidc-client", "matrix-sdk-base", "matrix-sdk-common", + "matrix-sdk-ffi-macros", "matrix-sdk-indexeddb", "matrix-sdk-sqlite", "matrix-sdk-test", @@ -3293,6 +3294,7 @@ dependencies = [ "js_int", "matrix-sdk-common", "matrix-sdk-crypto", + "matrix-sdk-ffi-macros", "matrix-sdk-sqlite", "pbkdf2", "rand", @@ -3323,6 +3325,7 @@ dependencies = [ "language-tags", "log-panics", "matrix-sdk", + "matrix-sdk-ffi-macros", "matrix-sdk-ui", "mime", "once_cell", @@ -3344,6 +3347,15 @@ dependencies = [ "zeroize", ] +[[package]] +name = "matrix-sdk-ffi-macros" +version = "0.7.0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.79", +] + [[package]] name = "matrix-sdk-indexeddb" version = "0.7.0" diff --git a/Cargo.toml b/Cargo.toml index 456d92b226a..448a450312f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -85,6 +85,7 @@ matrix-sdk = { path = "crates/matrix-sdk", version = "0.7.0", default-features = matrix-sdk-base = { path = "crates/matrix-sdk-base", version = "0.7.0" } matrix-sdk-common = { path = "crates/matrix-sdk-common", version = "0.7.0" } matrix-sdk-crypto = { path = "crates/matrix-sdk-crypto", version = "0.7.0" } +matrix-sdk-ffi-macros = { path = "testing/matrix-sdk-ffi-macros", version = "0.7.0" } matrix-sdk-indexeddb = { path = "crates/matrix-sdk-indexeddb", version = "0.7.0", default-features = false } matrix-sdk-qrcode = { path = "crates/matrix-sdk-qrcode", version = "0.7.0" } matrix-sdk-sqlite = { path = "crates/matrix-sdk-sqlite", version = "0.7.0", default-features = false } diff --git a/bindings/matrix-sdk-crypto-ffi/Cargo.toml b/bindings/matrix-sdk-crypto-ffi/Cargo.toml index 50dcaf137fe..3432c965ef8 100644 --- a/bindings/matrix-sdk-crypto-ffi/Cargo.toml +++ b/bindings/matrix-sdk-crypto-ffi/Cargo.toml @@ -26,6 +26,7 @@ futures-util = { workspace = true } hmac = "0.12.1" http = { workspace = true } matrix-sdk-common = { workspace = true, features = ["uniffi"] } +matrix-sdk-ffi-macros = { workspace = true } pbkdf2 = "0.12.2" rand = { workspace = true } ruma = { workspace = true } diff --git a/bindings/matrix-sdk-crypto-ffi/src/backup_recovery_key.rs b/bindings/matrix-sdk-crypto-ffi/src/backup_recovery_key.rs index b9b97062e6a..05c17b8aec5 100644 --- a/bindings/matrix-sdk-crypto-ffi/src/backup_recovery_key.rs +++ b/bindings/matrix-sdk-crypto-ffi/src/backup_recovery_key.rs @@ -69,7 +69,7 @@ impl BackupRecoveryKey { const PBKDF_ROUNDS: i32 = 500_000; } -#[uniffi::export] +#[matrix_sdk_ffi_macros::export] impl BackupRecoveryKey { /// Create a new random [`BackupRecoveryKey`]. #[allow(clippy::new_without_default)] diff --git a/bindings/matrix-sdk-crypto-ffi/src/dehydrated_devices.rs b/bindings/matrix-sdk-crypto-ffi/src/dehydrated_devices.rs index c57d5ae4bbf..585eb7a9be1 100644 --- a/bindings/matrix-sdk-crypto-ffi/src/dehydrated_devices.rs +++ b/bindings/matrix-sdk-crypto-ffi/src/dehydrated_devices.rs @@ -53,7 +53,7 @@ impl Drop for DehydratedDevices { } } -#[uniffi::export] +#[matrix_sdk_ffi_macros::export] impl DehydratedDevices { pub fn create(&self) -> Result, DehydrationError> { let inner = self.runtime.block_on(self.inner.create())?; @@ -107,7 +107,7 @@ impl Drop for RehydratedDevice { } } -#[uniffi::export] +#[matrix_sdk_ffi_macros::export] impl RehydratedDevice { pub fn receive_events(&self, events: String) -> Result<(), crate::CryptoStoreError> { let events: Vec> = serde_json::from_str(&events)?; @@ -133,7 +133,7 @@ impl Drop for DehydratedDevice { } } -#[uniffi::export] +#[matrix_sdk_ffi_macros::export] impl DehydratedDevice { pub fn keys_for_upload( &self, diff --git a/bindings/matrix-sdk-crypto-ffi/src/lib.rs b/bindings/matrix-sdk-crypto-ffi/src/lib.rs index 769c7876fef..125abd99ed3 100644 --- a/bindings/matrix-sdk-crypto-ffi/src/lib.rs +++ b/bindings/matrix-sdk-crypto-ffi/src/lib.rs @@ -196,7 +196,7 @@ impl From for MigrationError { /// /// * `progress_listener` - A callback that can be used to introspect the /// progress of the migration. -#[uniffi::export] +#[matrix_sdk_ffi_macros::export] pub fn migrate( data: MigrationData, path: String, @@ -359,7 +359,7 @@ async fn save_changes( /// /// * `progress_listener` - A callback that can be used to introspect the /// progress of the migration. -#[uniffi::export] +#[matrix_sdk_ffi_macros::export] pub fn migrate_sessions( data: SessionMigrationData, path: String, @@ -532,7 +532,7 @@ fn collect_sessions( /// * `passphrase` - The passphrase that should be used to encrypt the data at /// rest in the Sqlite store. **Warning**, if no passphrase is given, the /// store and all its data will remain unencrypted. -#[uniffi::export] +#[matrix_sdk_ffi_macros::export] pub fn migrate_room_settings( room_settings: HashMap, path: String, @@ -558,7 +558,7 @@ pub fn migrate_room_settings( } /// Callback that will be passed over the FFI to report progress -#[uniffi::export(callback_interface)] +#[matrix_sdk_ffi_macros::export(callback_interface)] pub trait ProgressListener { /// The callback that should be called on the Rust side /// @@ -794,7 +794,7 @@ pub struct BackupKeys { backup_version: String, } -#[uniffi::export] +#[matrix_sdk_ffi_macros::export] impl BackupKeys { /// Get the recovery key that we're holding on to. pub fn recovery_key(&self) -> Arc { @@ -891,7 +891,7 @@ fn parse_user_id(user_id: &str) -> Result { ruma::UserId::parse(user_id).map_err(|e| CryptoStoreError::InvalidUserId(user_id.to_owned(), e)) } -#[uniffi::export] +#[matrix_sdk_ffi_macros::export] fn version_info() -> VersionInfo { VersionInfo { version: matrix_sdk_crypto::VERSION.to_owned(), @@ -915,12 +915,12 @@ pub struct VersionInfo { pub git_description: String, } -#[uniffi::export] +#[matrix_sdk_ffi_macros::export] fn version() -> String { matrix_sdk_crypto::VERSION.to_owned() } -#[uniffi::export] +#[matrix_sdk_ffi_macros::export] fn vodozemac_version() -> String { vodozemac::VERSION.to_owned() } @@ -935,7 +935,7 @@ pub struct PkEncryption { inner: matrix_sdk_crypto::vodozemac::pk_encryption::PkEncryption, } -#[uniffi::export] +#[matrix_sdk_ffi_macros::export] impl PkEncryption { /// Create a new [`PkEncryption`] object from a `Curve25519PublicKey` /// encoded as Base64. diff --git a/bindings/matrix-sdk-crypto-ffi/src/logger.rs b/bindings/matrix-sdk-crypto-ffi/src/logger.rs index 328999ea15a..6b53a352a77 100644 --- a/bindings/matrix-sdk-crypto-ffi/src/logger.rs +++ b/bindings/matrix-sdk-crypto-ffi/src/logger.rs @@ -7,7 +7,7 @@ use tracing_subscriber::{fmt::MakeWriter, EnvFilter}; /// Trait that can be used to forward Rust logs over FFI to a language specific /// logger. -#[uniffi::export(callback_interface)] +#[matrix_sdk_ffi_macros::export(callback_interface)] pub trait Logger: Send { /// Called every time the Rust side wants to post a log line. fn log(&self, log_line: String); @@ -42,7 +42,7 @@ pub struct LoggerWrapper { } /// Set the logger that should be used to forward Rust logs over FFI. -#[uniffi::export] +#[matrix_sdk_ffi_macros::export] pub fn set_logger(logger: Box) { let logger = LoggerWrapper { inner: Arc::new(Mutex::new(logger)) }; diff --git a/bindings/matrix-sdk-crypto-ffi/src/machine.rs b/bindings/matrix-sdk-crypto-ffi/src/machine.rs index 8bb1fedd718..da12075bfe4 100644 --- a/bindings/matrix-sdk-crypto-ffi/src/machine.rs +++ b/bindings/matrix-sdk-crypto-ffi/src/machine.rs @@ -178,7 +178,7 @@ impl From for SignatureVerification { } } -#[uniffi::export] +#[matrix_sdk_ffi_macros::export] impl OlmMachine { /// Create a new `OlmMachine` /// diff --git a/bindings/matrix-sdk-crypto-ffi/src/verification.rs b/bindings/matrix-sdk-crypto-ffi/src/verification.rs index 179200503ba..e8c31c4a3a8 100644 --- a/bindings/matrix-sdk-crypto-ffi/src/verification.rs +++ b/bindings/matrix-sdk-crypto-ffi/src/verification.rs @@ -15,7 +15,7 @@ use crate::{CryptoStoreError, OutgoingVerificationRequest, SignatureUploadReques /// Listener that will be passed over the FFI to report changes to a SAS /// verification. -#[uniffi::export(callback_interface)] +#[matrix_sdk_ffi_macros::export(callback_interface)] pub trait SasListener: Send { /// The callback that should be called on the Rust side /// @@ -82,7 +82,7 @@ pub struct Verification { pub(crate) runtime: Handle, } -#[uniffi::export] +#[matrix_sdk_ffi_macros::export] impl Verification { /// Try to represent the `Verification` as an `Sas` verification object, /// returns `None` if the verification is not a `Sas` verification. @@ -112,7 +112,7 @@ pub struct Sas { pub(crate) runtime: Handle, } -#[uniffi::export] +#[matrix_sdk_ffi_macros::export] impl Sas { /// Get the user id of the other side. pub fn other_user_id(&self) -> String { @@ -276,7 +276,7 @@ impl Sas { /// Listener that will be passed over the FFI to report changes to a QrCode /// verification. -#[uniffi::export(callback_interface)] +#[matrix_sdk_ffi_macros::export(callback_interface)] pub trait QrCodeListener: Send { /// The callback that should be called on the Rust side /// @@ -328,7 +328,7 @@ pub struct QrCode { pub(crate) runtime: Handle, } -#[uniffi::export] +#[matrix_sdk_ffi_macros::export] impl QrCode { /// Get the user id of the other side. pub fn other_user_id(&self) -> String { @@ -522,7 +522,7 @@ pub struct ConfirmVerificationResult { /// Listener that will be passed over the FFI to report changes to a /// verification request. -#[uniffi::export(callback_interface)] +#[matrix_sdk_ffi_macros::export(callback_interface)] pub trait VerificationRequestListener: Send { /// The callback that should be called on the Rust side /// @@ -562,7 +562,7 @@ pub struct VerificationRequest { pub(crate) runtime: Handle, } -#[uniffi::export] +#[matrix_sdk_ffi_macros::export] impl VerificationRequest { /// The id of the other user that is participating in this verification /// request. diff --git a/bindings/matrix-sdk-ffi/Cargo.toml b/bindings/matrix-sdk-ffi/Cargo.toml index abab2bff692..02400423b17 100644 --- a/bindings/matrix-sdk-ffi/Cargo.toml +++ b/bindings/matrix-sdk-ffi/Cargo.toml @@ -28,6 +28,7 @@ eyeball-im = { workspace = true } extension-trait = "1.0.1" futures-util = { workspace = true } log-panics = { version = "2", features = ["with-backtrace"] } +matrix-sdk-ffi-macros = { workspace = true } matrix-sdk-ui = { workspace = true, features = ["uniffi"] } mime = "0.3.16" once_cell = { workspace = true } diff --git a/bindings/matrix-sdk-ffi/src/authentication.rs b/bindings/matrix-sdk-ffi/src/authentication.rs index 37aaff4e8e2..3a22244c519 100644 --- a/bindings/matrix-sdk-ffi/src/authentication.rs +++ b/bindings/matrix-sdk-ffi/src/authentication.rs @@ -29,7 +29,7 @@ pub struct HomeserverLoginDetails { pub(crate) supports_password_login: bool, } -#[uniffi::export] +#[matrix_sdk_ffi_macros::export] impl HomeserverLoginDetails { /// The URL of the currently configured homeserver. pub fn url(&self) -> String { @@ -62,7 +62,7 @@ pub struct SsoHandler { pub(crate) url: String, } -#[uniffi::export(async_runtime = "tokio")] +#[matrix_sdk_ffi_macros::export_async] impl SsoHandler { /// Returns the URL for starting SSO authentication. The URL should be /// opened in a web view. Once the web view succeeds, call `finish` with diff --git a/bindings/matrix-sdk-ffi/src/client.rs b/bindings/matrix-sdk-ffi/src/client.rs index e37cbba6f8b..314b19eab1e 100644 --- a/bindings/matrix-sdk-ffi/src/client.rs +++ b/bindings/matrix-sdk-ffi/src/client.rs @@ -140,25 +140,25 @@ impl From for RumaPushFormat { } } -#[uniffi::export(callback_interface)] +#[matrix_sdk_ffi_macros::export(callback_interface)] pub trait ClientDelegate: Sync + Send { fn did_receive_auth_error(&self, is_soft_logout: bool); fn did_refresh_tokens(&self); } -#[uniffi::export(callback_interface)] +#[matrix_sdk_ffi_macros::export(callback_interface)] pub trait ClientSessionDelegate: Sync + Send { fn retrieve_session_from_keychain(&self, user_id: String) -> Result; fn save_session_in_keychain(&self, session: Session); } -#[uniffi::export(callback_interface)] +#[matrix_sdk_ffi_macros::export(callback_interface)] pub trait ProgressWatcher: Send + Sync { fn transmission_progress(&self, progress: TransmissionProgress); } /// A listener to the global (client-wide) error reporter of the send queue. -#[uniffi::export(callback_interface)] +#[matrix_sdk_ffi_macros::export(callback_interface)] pub trait SendQueueRoomErrorListener: Sync + Send { /// Called every time the send queue has ran into an error for a given room, /// which will disable the send queue for that particular room. @@ -260,7 +260,7 @@ impl Client { } } -#[uniffi::export(async_runtime = "tokio")] +#[matrix_sdk_ffi_macros::export_async] impl Client { /// Information about login options for the client's homeserver. pub async fn homeserver_login_details(&self) -> Arc { @@ -526,7 +526,7 @@ impl Client { } } -#[uniffi::export(async_runtime = "tokio")] +#[matrix_sdk_ffi_macros::export_async] impl Client { /// The sliding sync version. pub fn sliding_sync_version(&self) -> SlidingSyncVersion { @@ -1092,7 +1092,7 @@ impl Client { } } -#[uniffi::export(callback_interface)] +#[matrix_sdk_ffi_macros::export(callback_interface)] pub trait IgnoredUsersListener: Sync + Send { fn call(&self, ignored_user_ids: Vec); } @@ -1649,7 +1649,7 @@ impl From for AccountManagementActionFull { } } -#[uniffi::export] +#[matrix_sdk_ffi_macros::export] fn gen_transaction_id() -> String { TransactionId::new().to_string() } @@ -1667,7 +1667,7 @@ impl MediaFileHandle { } } -#[uniffi::export] +#[matrix_sdk_ffi_macros::export] impl MediaFileHandle { /// Get the media file's path. pub fn path(&self) -> Result { diff --git a/bindings/matrix-sdk-ffi/src/client_builder.rs b/bindings/matrix-sdk-ffi/src/client_builder.rs index ec0522fafd2..9b817dd4f66 100644 --- a/bindings/matrix-sdk-ffi/src/client_builder.rs +++ b/bindings/matrix-sdk-ffi/src/client_builder.rs @@ -47,7 +47,7 @@ pub struct QrCodeData { inner: qrcode::QrCodeData, } -#[uniffi::export] +#[matrix_sdk_ffi_macros::export] impl QrCodeData { /// Attempt to decode a slice of bytes into a [`QrCodeData`] object. /// @@ -159,7 +159,7 @@ pub enum QrLoginProgress { Done, } -#[uniffi::export(callback_interface)] +#[matrix_sdk_ffi_macros::export(callback_interface)] pub trait QrLoginProgressListener: Sync + Send { fn on_update(&self, state: QrLoginProgress); } @@ -270,7 +270,7 @@ pub struct ClientBuilder { request_config: Option, } -#[uniffi::export(async_runtime = "tokio")] +#[matrix_sdk_ffi_macros::export_async] impl ClientBuilder { #[uniffi::constructor] pub fn new() -> Arc { diff --git a/bindings/matrix-sdk-ffi/src/element.rs b/bindings/matrix-sdk-ffi/src/element.rs index 6ff2dff5b53..87722b4d278 100644 --- a/bindings/matrix-sdk-ffi/src/element.rs +++ b/bindings/matrix-sdk-ffi/src/element.rs @@ -16,7 +16,7 @@ pub struct ElementWellKnown { } /// Helper function to parse a string into a ElementWellKnown struct -#[uniffi::export] +#[matrix_sdk_ffi_macros::export] pub fn make_element_well_known(string: String) -> Result { serde_json::from_str(&string).map_err(ClientError::new) } diff --git a/bindings/matrix-sdk-ffi/src/encryption.rs b/bindings/matrix-sdk-ffi/src/encryption.rs index 7f6e35b4aac..cd7c6e94fc1 100644 --- a/bindings/matrix-sdk-ffi/src/encryption.rs +++ b/bindings/matrix-sdk-ffi/src/encryption.rs @@ -23,22 +23,22 @@ pub struct Encryption { pub(crate) _client: Arc, } -#[uniffi::export(callback_interface)] +#[matrix_sdk_ffi_macros::export(callback_interface)] pub trait BackupStateListener: Sync + Send { fn on_update(&self, status: BackupState); } -#[uniffi::export(callback_interface)] +#[matrix_sdk_ffi_macros::export(callback_interface)] pub trait BackupSteadyStateListener: Sync + Send { fn on_update(&self, status: BackupUploadState); } -#[uniffi::export(callback_interface)] +#[matrix_sdk_ffi_macros::export(callback_interface)] pub trait RecoveryStateListener: Sync + Send { fn on_update(&self, status: RecoveryState); } -#[uniffi::export(callback_interface)] +#[matrix_sdk_ffi_macros::export(callback_interface)] pub trait VerificationStateListener: Sync + Send { fn on_update(&self, status: VerificationState); } @@ -162,7 +162,7 @@ impl From for RecoveryState { } } -#[uniffi::export(callback_interface)] +#[matrix_sdk_ffi_macros::export(callback_interface)] pub trait EnableRecoveryProgressListener: Sync + Send { fn on_update(&self, status: EnableRecoveryProgress); } @@ -212,7 +212,7 @@ impl From for VerificationState { } } -#[uniffi::export(async_runtime = "tokio")] +#[matrix_sdk_ffi_macros::export_async] impl Encryption { /// Get the public ed25519 key of our own device. This is usually what is /// called the fingerprint of the device. @@ -432,7 +432,7 @@ pub struct UserIdentity { inner: matrix_sdk::encryption::identities::UserIdentity, } -#[uniffi::export(async_runtime = "tokio")] +#[matrix_sdk_ffi_macros::export_async] impl UserIdentity { /// Remember this identity, ensuring it does not result in a pin violation. /// @@ -468,7 +468,7 @@ pub struct IdentityResetHandle { pub(crate) inner: matrix_sdk::encryption::recovery::IdentityResetHandle, } -#[uniffi::export(async_runtime = "tokio")] +#[matrix_sdk_ffi_macros::export_async] impl IdentityResetHandle { /// Get the underlying [`CrossSigningResetAuthType`] this identity reset /// process is using. diff --git a/bindings/matrix-sdk-ffi/src/event.rs b/bindings/matrix-sdk-ffi/src/event.rs index 3cd4304a281..b5b98d792c8 100644 --- a/bindings/matrix-sdk-ffi/src/event.rs +++ b/bindings/matrix-sdk-ffi/src/event.rs @@ -20,7 +20,7 @@ use crate::{ #[derive(uniffi::Object)] pub struct TimelineEvent(pub(crate) AnySyncTimelineEvent); -#[uniffi::export] +#[matrix_sdk_ffi_macros::export] impl TimelineEvent { pub fn event_id(&self) -> String { self.0.event_id().to_string() diff --git a/bindings/matrix-sdk-ffi/src/lib.rs b/bindings/matrix-sdk-ffi/src/lib.rs index 801887b6fee..0e404a848ff 100644 --- a/bindings/matrix-sdk-ffi/src/lib.rs +++ b/bindings/matrix-sdk-ffi/src/lib.rs @@ -44,7 +44,7 @@ use self::{ uniffi::include_scaffolding!("api"); -#[uniffi::export] +#[matrix_sdk_ffi_macros::export] fn sdk_git_sha() -> String { env!("VERGEN_GIT_SHA").to_owned() } diff --git a/bindings/matrix-sdk-ffi/src/notification.rs b/bindings/matrix-sdk-ffi/src/notification.rs index eda3f6232a1..35d047b94a2 100644 --- a/bindings/matrix-sdk-ffi/src/notification.rs +++ b/bindings/matrix-sdk-ffi/src/notification.rs @@ -88,7 +88,7 @@ pub struct NotificationClient { pub(crate) _client: Arc, } -#[uniffi::export(async_runtime = "tokio")] +#[matrix_sdk_ffi_macros::export_async] impl NotificationClient { /// See also documentation of /// `MatrixNotificationClient::get_notification`. diff --git a/bindings/matrix-sdk-ffi/src/notification_settings.rs b/bindings/matrix-sdk-ffi/src/notification_settings.rs index f758a2fda97..1a01011a889 100644 --- a/bindings/matrix-sdk-ffi/src/notification_settings.rs +++ b/bindings/matrix-sdk-ffi/src/notification_settings.rs @@ -49,7 +49,7 @@ impl From for SdkRoomNotificationMode { } /// Delegate to notify of changes in push rules -#[uniffi::export(callback_interface)] +#[matrix_sdk_ffi_macros::export(callback_interface)] pub trait NotificationSettingsDelegate: Sync + Send { fn settings_did_change(&self); } @@ -98,7 +98,7 @@ impl Drop for NotificationSettings { } } -#[uniffi::export(async_runtime = "tokio")] +#[matrix_sdk_ffi_macros::export_async] impl NotificationSettings { pub fn set_delegate(&self, delegate: Option>) { if let Some(delegate) = delegate { diff --git a/bindings/matrix-sdk-ffi/src/platform.rs b/bindings/matrix-sdk-ffi/src/platform.rs index 7e61b93a2fb..a811474f9f3 100644 --- a/bindings/matrix-sdk-ffi/src/platform.rs +++ b/bindings/matrix-sdk-ffi/src/platform.rs @@ -242,7 +242,7 @@ pub struct TracingConfiguration { write_to_files: Option, } -#[uniffi::export] +#[matrix_sdk_ffi_macros::export] pub fn setup_tracing(config: TracingConfiguration) { log_panics(); diff --git a/bindings/matrix-sdk-ffi/src/room.rs b/bindings/matrix-sdk-ffi/src/room.rs index d12a3f0c945..d5d36e57a19 100644 --- a/bindings/matrix-sdk-ffi/src/room.rs +++ b/bindings/matrix-sdk-ffi/src/room.rs @@ -82,7 +82,7 @@ impl Room { } } -#[uniffi::export(async_runtime = "tokio")] +#[matrix_sdk_ffi_macros::export_async] impl Room { pub fn id(&self) -> String { self.inner.room_id().to_string() @@ -861,7 +861,7 @@ impl Room { } /// Generates a `matrix.to` permalink to the given room alias. -#[uniffi::export] +#[matrix_sdk_ffi_macros::export] pub fn matrix_to_room_alias_permalink( room_alias: String, ) -> std::result::Result { @@ -917,17 +917,17 @@ impl From for RoomPowerLevels { } } -#[uniffi::export(callback_interface)] +#[matrix_sdk_ffi_macros::export(callback_interface)] pub trait RoomInfoListener: Sync + Send { fn call(&self, room_info: RoomInfo); } -#[uniffi::export(callback_interface)] +#[matrix_sdk_ffi_macros::export(callback_interface)] pub trait TypingNotificationsListener: Sync + Send { fn call(&self, typing_user_ids: Vec); } -#[uniffi::export(callback_interface)] +#[matrix_sdk_ffi_macros::export(callback_interface)] pub trait IdentityStatusChangeListener: Sync + Send { fn call(&self, identity_status_change: Vec); } @@ -943,7 +943,7 @@ impl RoomMembersIterator { } } -#[uniffi::export] +#[matrix_sdk_ffi_macros::export] impl RoomMembersIterator { fn len(&self) -> u32 { self.chunk_iterator.len() diff --git a/bindings/matrix-sdk-ffi/src/room_directory_search.rs b/bindings/matrix-sdk-ffi/src/room_directory_search.rs index b3af0058b94..0666b50e3b3 100644 --- a/bindings/matrix-sdk-ffi/src/room_directory_search.rs +++ b/bindings/matrix-sdk-ffi/src/room_directory_search.rs @@ -79,7 +79,7 @@ impl RoomDirectorySearch { } } -#[uniffi::export(async_runtime = "tokio")] +#[matrix_sdk_ffi_macros::export_async] impl RoomDirectorySearch { pub async fn next_page(&self) -> Result<(), ClientError> { let mut inner = self.inner.write().await; @@ -169,7 +169,7 @@ impl From> } } -#[uniffi::export(callback_interface)] +#[matrix_sdk_ffi_macros::export(callback_interface)] pub trait RoomDirectorySearchEntriesListener: Send + Sync + Debug { fn on_update(&self, room_entries_update: Vec); } diff --git a/bindings/matrix-sdk-ffi/src/room_list.rs b/bindings/matrix-sdk-ffi/src/room_list.rs index b9e4dca9623..9f70e9e71f3 100644 --- a/bindings/matrix-sdk-ffi/src/room_list.rs +++ b/bindings/matrix-sdk-ffi/src/room_list.rs @@ -85,7 +85,7 @@ pub struct RoomListService { pub(crate) utd_hook: Option>, } -#[uniffi::export(async_runtime = "tokio")] +#[matrix_sdk_ffi_macros::export_async] impl RoomListService { fn state(&self, listener: Box) -> Arc { let state_stream = self.inner.state(); @@ -162,7 +162,7 @@ pub struct RoomList { inner: Arc, } -#[uniffi::export] +#[matrix_sdk_ffi_macros::export] impl RoomList { fn loading_state( &self, @@ -292,7 +292,7 @@ pub struct RoomListEntriesWithDynamicAdaptersResult { entries_stream: Arc, } -#[uniffi::export] +#[matrix_sdk_ffi_macros::export] impl RoomListEntriesWithDynamicAdaptersResult { fn controller(&self) -> Arc { self.controller.clone() @@ -370,17 +370,17 @@ impl From for RoomListLo } } -#[uniffi::export(callback_interface)] +#[matrix_sdk_ffi_macros::export(callback_interface)] pub trait RoomListServiceStateListener: Send + Sync + Debug { fn on_update(&self, state: RoomListServiceState); } -#[uniffi::export(callback_interface)] +#[matrix_sdk_ffi_macros::export(callback_interface)] pub trait RoomListLoadingStateListener: Send + Sync + Debug { fn on_update(&self, state: RoomListLoadingState); } -#[uniffi::export(callback_interface)] +#[matrix_sdk_ffi_macros::export(callback_interface)] pub trait RoomListServiceSyncIndicatorListener: Send + Sync + Debug { fn on_update(&self, sync_indicator: RoomListServiceSyncIndicator); } @@ -443,7 +443,7 @@ impl RoomListEntriesUpdate { } } -#[uniffi::export(callback_interface)] +#[matrix_sdk_ffi_macros::export(callback_interface)] pub trait RoomListEntriesListener: Send + Sync + Debug { fn on_update(&self, room_entries_update: Vec); } @@ -461,7 +461,7 @@ impl RoomListDynamicEntriesController { } } -#[uniffi::export] +#[matrix_sdk_ffi_macros::export] impl RoomListDynamicEntriesController { fn set_filter(&self, kind: RoomListEntriesDynamicFilterKind) -> bool { self.inner.set_filter(kind.into()) @@ -549,7 +549,7 @@ impl RoomListItem { } } -#[uniffi::export(async_runtime = "tokio")] +#[matrix_sdk_ffi_macros::export_async] impl RoomListItem { fn id(&self) -> String { self.inner.id().to_string() @@ -711,7 +711,7 @@ pub struct UnreadNotificationsCount { notification_count: u32, } -#[uniffi::export] +#[matrix_sdk_ffi_macros::export] impl UnreadNotificationsCount { fn highlight_count(&self) -> u32 { self.highlight_count diff --git a/bindings/matrix-sdk-ffi/src/room_member.rs b/bindings/matrix-sdk-ffi/src/room_member.rs index 7fe579b1154..96aed25a00f 100644 --- a/bindings/matrix-sdk-ffi/src/room_member.rs +++ b/bindings/matrix-sdk-ffi/src/room_member.rs @@ -42,20 +42,20 @@ impl From for Membershi } } -#[uniffi::export] +#[matrix_sdk_ffi_macros::export] pub fn suggested_role_for_power_level(power_level: i64) -> RoomMemberRole { // It's not possible to expose the constructor on the Enum through Uniffi ☹️ RoomMemberRole::suggested_role_for_power_level(power_level) } -#[uniffi::export] +#[matrix_sdk_ffi_macros::export] pub fn suggested_power_level_for_role(role: RoomMemberRole) -> i64 { // It's not possible to expose methods on an Enum through Uniffi ☹️ role.suggested_power_level() } /// Generates a `matrix.to` permalink to the given userID. -#[uniffi::export] +#[matrix_sdk_ffi_macros::export] pub fn matrix_to_user_permalink(user_id: String) -> Result { let user_id = UserId::parse(user_id)?; Ok(user_id.matrix_to_uri().to_string()) diff --git a/bindings/matrix-sdk-ffi/src/ruma.rs b/bindings/matrix-sdk-ffi/src/ruma.rs index 20c00e62025..fa4bdbb95b5 100644 --- a/bindings/matrix-sdk-ffi/src/ruma.rs +++ b/bindings/matrix-sdk-ffi/src/ruma.rs @@ -90,7 +90,7 @@ impl From for ruma::api::client::uiaa::AuthData { /// Parse a matrix entity from a given URI, be it either /// a `matrix.to` link or a `matrix:` URI -#[uniffi::export] +#[matrix_sdk_ffi_macros::export] pub fn parse_matrix_entity_from(uri: String) -> Option { if let Ok(matrix_uri) = RumaMatrixUri::parse(&uri) { return Some(MatrixEntity { @@ -154,33 +154,33 @@ impl From<&RumaMatrixId> for MatrixId { } } -#[uniffi::export] +#[matrix_sdk_ffi_macros::export] pub fn media_source_from_url(url: String) -> Arc { Arc::new(MediaSource::Plain(url.into())) } -#[uniffi::export] +#[matrix_sdk_ffi_macros::export] pub fn message_event_content_new( msgtype: MessageType, ) -> Result, ClientError> { Ok(Arc::new(RoomMessageEventContentWithoutRelation::new(msgtype.try_into()?))) } -#[uniffi::export] +#[matrix_sdk_ffi_macros::export] pub fn message_event_content_from_markdown( md: String, ) -> Arc { Arc::new(RoomMessageEventContentWithoutRelation::new(RumaMessageType::text_markdown(md))) } -#[uniffi::export] +#[matrix_sdk_ffi_macros::export] pub fn message_event_content_from_markdown_as_emote( md: String, ) -> Arc { Arc::new(RoomMessageEventContentWithoutRelation::new(RumaMessageType::emote_markdown(md))) } -#[uniffi::export] +#[matrix_sdk_ffi_macros::export] pub fn message_event_content_from_html( body: String, html_body: String, @@ -190,7 +190,7 @@ pub fn message_event_content_from_html( ))) } -#[uniffi::export] +#[matrix_sdk_ffi_macros::export] pub fn message_event_content_from_html_as_emote( body: String, html_body: String, @@ -918,7 +918,7 @@ impl From for PollKind { /// Creates a [`RoomMessageEventContentWithoutRelation`] given a /// [`MessageContent`] value. -#[uniffi::export] +#[matrix_sdk_ffi_macros::export] pub fn content_without_relation_from_message( message: MessageContent, ) -> Result, ClientError> { diff --git a/bindings/matrix-sdk-ffi/src/session_verification.rs b/bindings/matrix-sdk-ffi/src/session_verification.rs index a34ecfa6aa3..f0cf5437425 100644 --- a/bindings/matrix-sdk-ffi/src/session_verification.rs +++ b/bindings/matrix-sdk-ffi/src/session_verification.rs @@ -20,7 +20,7 @@ pub struct SessionVerificationEmoji { description: String, } -#[uniffi::export] +#[matrix_sdk_ffi_macros::export] impl SessionVerificationEmoji { pub fn symbol(&self) -> String { self.symbol.clone() @@ -37,7 +37,7 @@ pub enum SessionVerificationData { Decimals { values: Vec }, } -#[uniffi::export(callback_interface)] +#[matrix_sdk_ffi_macros::export(callback_interface)] pub trait SessionVerificationControllerDelegate: Sync + Send { fn did_accept_verification_request(&self); fn did_start_sas_verification(&self); @@ -58,7 +58,7 @@ pub struct SessionVerificationController { sas_verification: Arc>>, } -#[uniffi::export(async_runtime = "tokio")] +#[matrix_sdk_ffi_macros::export_async] impl SessionVerificationController { pub async fn is_verified(&self) -> Result { let device = diff --git a/bindings/matrix-sdk-ffi/src/sync_service.rs b/bindings/matrix-sdk-ffi/src/sync_service.rs index 329784c3e1b..3d3d30b0a66 100644 --- a/bindings/matrix-sdk-ffi/src/sync_service.rs +++ b/bindings/matrix-sdk-ffi/src/sync_service.rs @@ -51,7 +51,7 @@ impl From for SyncServiceState { } } -#[uniffi::export(callback_interface)] +#[matrix_sdk_ffi_macros::export(callback_interface)] pub trait SyncServiceStateObserver: Send + Sync + Debug { fn on_update(&self, state: SyncServiceState); } @@ -62,7 +62,7 @@ pub struct SyncService { utd_hook: Option>, } -#[uniffi::export(async_runtime = "tokio")] +#[matrix_sdk_ffi_macros::export_async] impl SyncService { pub fn room_list_service(&self) -> Arc { Arc::new(RoomListService { @@ -110,7 +110,7 @@ impl SyncServiceBuilder { } } -#[uniffi::export(async_runtime = "tokio")] +#[matrix_sdk_ffi_macros::export_async] impl SyncServiceBuilder { pub fn with_cross_process_lock(self: Arc, app_identifier: Option) -> Arc { let this = unwrap_or_clone_arc(self); @@ -153,7 +153,7 @@ impl SyncServiceBuilder { } } -#[uniffi::export(callback_interface)] +#[matrix_sdk_ffi_macros::export(callback_interface)] pub trait UnableToDecryptDelegate: Sync + Send { fn on_utd(&self, info: UnableToDecryptInfo); } diff --git a/bindings/matrix-sdk-ffi/src/task_handle.rs b/bindings/matrix-sdk-ffi/src/task_handle.rs index e1e50bf6fbe..5a593fa55fa 100644 --- a/bindings/matrix-sdk-ffi/src/task_handle.rs +++ b/bindings/matrix-sdk-ffi/src/task_handle.rs @@ -17,7 +17,7 @@ impl TaskHandle { } } -#[uniffi::export] +#[matrix_sdk_ffi_macros::export] impl TaskHandle { // Cancel a task handle. pub fn cancel(&self) { diff --git a/bindings/matrix-sdk-ffi/src/timeline/content.rs b/bindings/matrix-sdk-ffi/src/timeline/content.rs index 3705e3ccd59..e39f2c0798d 100644 --- a/bindings/matrix-sdk-ffi/src/timeline/content.rs +++ b/bindings/matrix-sdk-ffi/src/timeline/content.rs @@ -195,7 +195,7 @@ impl InReplyToDetails { } } -#[uniffi::export] +#[matrix_sdk_ffi_macros::export] impl InReplyToDetails { pub fn event_id(&self) -> String { self.event_id.clone() diff --git a/bindings/matrix-sdk-ffi/src/timeline/mod.rs b/bindings/matrix-sdk-ffi/src/timeline/mod.rs index 8ca950f5c02..9182fcf55fb 100644 --- a/bindings/matrix-sdk-ffi/src/timeline/mod.rs +++ b/bindings/matrix-sdk-ffi/src/timeline/mod.rs @@ -142,7 +142,7 @@ impl Timeline { } } -#[uniffi::export(async_runtime = "tokio")] +#[matrix_sdk_ffi_macros::export_async] impl Timeline { pub async fn add_listener(&self, listener: Box) -> Arc { let (timeline_items, timeline_stream) = self.inner.subscribe_batched().await; @@ -688,7 +688,7 @@ pub struct SendHandle { inner: Mutex>, } -#[uniffi::export(async_runtime = "tokio")] +#[matrix_sdk_ffi_macros::export_async] impl SendHandle { /// Try to abort the sending of the current event. /// @@ -723,12 +723,12 @@ pub enum FocusEventError { Other { msg: String }, } -#[uniffi::export(callback_interface)] +#[matrix_sdk_ffi_macros::export(callback_interface)] pub trait TimelineListener: Sync + Send { fn on_update(&self, diff: Vec>); } -#[uniffi::export(callback_interface)] +#[matrix_sdk_ffi_macros::export(callback_interface)] pub trait PaginationStatusListener: Sync + Send { fn on_update(&self, status: LiveBackPaginationStatus); } @@ -778,7 +778,7 @@ impl TimelineDiff { } } -#[uniffi::export] +#[matrix_sdk_ffi_macros::export] impl TimelineDiff { pub fn change(&self) -> TimelineChange { match self { @@ -878,7 +878,7 @@ impl TimelineItem { } } -#[uniffi::export] +#[matrix_sdk_ffi_macros::export] impl TimelineItem { pub fn as_event(self: Arc) -> Option { let event_item = self.0.as_event()?; @@ -1108,7 +1108,7 @@ impl From for Receipt { #[derive(uniffi::Object)] pub struct EventTimelineItemDebugInfoProvider(Arc); -#[uniffi::export] +#[matrix_sdk_ffi_macros::export] impl EventTimelineItemDebugInfoProvider { fn get(&self) -> EventTimelineItemDebugInfo { EventTimelineItemDebugInfo { @@ -1202,7 +1202,7 @@ impl SendAttachmentJoinHandle { } } -#[uniffi::export(async_runtime = "tokio")] +#[matrix_sdk_ffi_macros::export_async] impl SendAttachmentJoinHandle { pub async fn join(&self) -> Result<(), RoomError> { let join_hdl = self.join_hdl.clone(); @@ -1274,7 +1274,7 @@ impl TryFrom for SdkEditedContent { #[derive(Clone, uniffi::Object)] pub struct EventShieldsProvider(Arc); -#[uniffi::export] +#[matrix_sdk_ffi_macros::export] impl EventShieldsProvider { fn get_shields(&self, strict: bool) -> Option { self.0.get_shield(strict).map(Into::into) diff --git a/bindings/matrix-sdk-ffi/src/timeline_event_filter.rs b/bindings/matrix-sdk-ffi/src/timeline_event_filter.rs index 9d6472eaf4a..5da36b9b1e4 100644 --- a/bindings/matrix-sdk-ffi/src/timeline_event_filter.rs +++ b/bindings/matrix-sdk-ffi/src/timeline_event_filter.rs @@ -10,7 +10,7 @@ pub struct TimelineEventTypeFilter { inner: InnerTimelineEventTypeFilter, } -#[uniffi::export] +#[matrix_sdk_ffi_macros::export] impl TimelineEventTypeFilter { #[uniffi::constructor] pub fn include(event_types: Vec) -> Arc { diff --git a/bindings/matrix-sdk-ffi/src/tracing.rs b/bindings/matrix-sdk-ffi/src/tracing.rs index 9e1207d62fb..cc50f14a1e3 100644 --- a/bindings/matrix-sdk-ffi/src/tracing.rs +++ b/bindings/matrix-sdk-ffi/src/tracing.rs @@ -19,7 +19,7 @@ use tracing_core::{identify_callsite, metadata::Kind as MetadataKind}; /// level + target) it is called with. Please make sure that the number of /// different combinations of those parameters this can be called with is /// constant in the final executable. -#[uniffi::export] +#[matrix_sdk_ffi_macros::export] fn log_event(file: String, line: Option, level: LogLevel, target: String, message: String) { static CALLSITES: Mutex> = Mutex::new(BTreeMap::new()); @@ -96,7 +96,7 @@ fn span_or_event_enabled(callsite: &'static DefaultCallsite) -> bool { #[derive(uniffi::Object)] pub struct Span(tracing::Span); -#[uniffi::export] +#[matrix_sdk_ffi_macros::export] impl Span { /// Create a span originating at the given callsite (file, line and column). /// diff --git a/bindings/matrix-sdk-ffi/src/widget.rs b/bindings/matrix-sdk-ffi/src/widget.rs index 9cdd5856033..2e5ca65c8a1 100644 --- a/bindings/matrix-sdk-ffi/src/widget.rs +++ b/bindings/matrix-sdk-ffi/src/widget.rs @@ -15,7 +15,7 @@ pub struct WidgetDriverAndHandle { pub handle: Arc, } -#[uniffi::export] +#[matrix_sdk_ffi_macros::export] pub fn make_widget_driver(settings: WidgetSettings) -> Result { let (driver, handle) = matrix_sdk::widget::WidgetDriver::new(settings.try_into()?); Ok(WidgetDriverAndHandle { @@ -29,7 +29,7 @@ pub fn make_widget_driver(settings: WidgetSettings) -> Result>); -#[uniffi::export(async_runtime = "tokio")] +#[matrix_sdk_ffi_macros::export_async] impl WidgetDriver { pub async fn run( &self, @@ -96,7 +96,7 @@ impl From for WidgetSettings { /// * `room` - A matrix room which is used to query the logged in username /// * `props` - Properties from the client that can be used by a widget to adapt /// to the client. e.g. language, font-scale... -#[uniffi::export(async_runtime = "tokio")] +#[matrix_sdk_ffi_macros::export_async] pub async fn generate_webview_url( widget_settings: WidgetSettings, room: Arc, @@ -241,7 +241,7 @@ impl From for matrix_sdk::widget::VirtualElemen /// /// * `props` - A struct containing the configuration parameters for a element /// call widget. -#[uniffi::export] +#[matrix_sdk_ffi_macros::export] pub fn new_virtual_element_call_widget( props: VirtualElementCallWidgetOptions, ) -> Result { @@ -261,7 +261,7 @@ pub fn new_virtual_element_call_widget( /// Editing and extending the capabilities from this function is also possible, /// but should only be done as temporal workarounds until this function is /// adjusted -#[uniffi::export] +#[matrix_sdk_ffi_macros::export] pub fn get_element_call_required_permissions( own_user_id: String, own_device_id: String, @@ -354,7 +354,7 @@ impl From for matrix_sdk::widget::ClientProperties { #[derive(uniffi::Object)] pub struct WidgetDriverHandle(matrix_sdk::widget::WidgetDriverHandle); -#[uniffi::export(async_runtime = "tokio")] +#[matrix_sdk_ffi_macros::export_async] impl WidgetDriverHandle { /// Receive a message from the widget driver. /// @@ -469,7 +469,7 @@ impl From for WidgetEventFilter { } } -#[uniffi::export(callback_interface)] +#[matrix_sdk_ffi_macros::export(callback_interface)] pub trait WidgetCapabilitiesProvider: Send + Sync { fn acquire_capabilities(&self, capabilities: WidgetCapabilities) -> WidgetCapabilities; } diff --git a/crates/matrix-sdk/Cargo.toml b/crates/matrix-sdk/Cargo.toml index 096ce0250e9..f2dcdf32933 100644 --- a/crates/matrix-sdk/Cargo.toml +++ b/crates/matrix-sdk/Cargo.toml @@ -44,7 +44,7 @@ sso-login = ["dep:axum", "dep:rand", "dep:tower"] image-proc = ["dep:image"] image-rayon = ["image-proc", "image?/rayon"] -uniffi = ["dep:uniffi", "matrix-sdk-base/uniffi"] +uniffi = ["dep:uniffi", "matrix-sdk-base/uniffi", "dep:matrix-sdk-ffi-macros"] experimental-oidc = [ "ruma/unstable-msc2967", @@ -92,6 +92,7 @@ language-tags = { version = "0.3.2", optional = true } mas-oidc-client = { version = "0.10.0", default-features = false, optional = true } matrix-sdk-base = { workspace = true } matrix-sdk-common = { workspace = true } +matrix-sdk-ffi-macros = { workspace = true, optional = true } matrix-sdk-indexeddb = { workspace = true, optional = true } matrix-sdk-sqlite = { workspace = true, optional = true } matrix-sdk-test = { workspace = true, optional = true } diff --git a/crates/matrix-sdk/src/oidc/auth_code_builder.rs b/crates/matrix-sdk/src/oidc/auth_code_builder.rs index 148c6ca7d7d..97b4c799743 100644 --- a/crates/matrix-sdk/src/oidc/auth_code_builder.rs +++ b/crates/matrix-sdk/src/oidc/auth_code_builder.rs @@ -246,7 +246,7 @@ pub struct OidcAuthorizationData { } #[cfg(feature = "uniffi")] -#[uniffi::export] +#[matrix_sdk_ffi_macros::export] impl OidcAuthorizationData { /// The login URL to use for authorization. pub fn login_url(&self) -> String { diff --git a/testing/matrix-sdk-ffi-macros/Cargo.toml b/testing/matrix-sdk-ffi-macros/Cargo.toml new file mode 100644 index 00000000000..54fb31ea7d5 --- /dev/null +++ b/testing/matrix-sdk-ffi-macros/Cargo.toml @@ -0,0 +1,24 @@ +[package] +description = "Helper macros to write FFI bindings" +edition = "2021" +homepage = "https://github.com/matrix-org/matrix-rust-sdk" +keywords = ["matrix", "chat", "messaging", "ruma"] +license = "Apache-2.0" +name = "matrix-sdk-ffi-macros" +readme = "README.md" +repository = "https://github.com/matrix-org/matrix-rust-sdk" +rust-version = { workspace = true } +version = "0.7.0" + +[lib] +proc-macro = true +test = false +doctest = false + +[dependencies] +proc-macro2 = "1.0.86" +quote = "1.0.18" +syn = { version = "2.0.43", features = ["full", "extra-traits"] } + +[lints] +workspace = true diff --git a/testing/matrix-sdk-ffi-macros/README.md b/testing/matrix-sdk-ffi-macros/README.md new file mode 100644 index 00000000000..17c29d67bbf --- /dev/null +++ b/testing/matrix-sdk-ffi-macros/README.md @@ -0,0 +1,14 @@ +[![Build Status](https://img.shields.io/travis/matrix-org/matrix-rust-sdk.svg?style=flat-square)](https://travis-ci.org/matrix-org/matrix-rust-sdk) +[![codecov](https://img.shields.io/codecov/c/github/matrix-org/matrix-rust-sdk/main.svg?style=flat-square)](https://codecov.io/gh/matrix-org/matrix-rust-sdk) +[![License](https://img.shields.io/badge/License-Apache%202.0-yellowgreen.svg?style=flat-square)](https://opensource.org/licenses/Apache-2.0) +[![#matrix-rust-sdk](https://img.shields.io/badge/matrix-%23matrix--rust--sdk-blue?style=flat-square)](https://matrix.to/#/#matrix-rust-sdk:matrix.org) + +# matrix-sdk-ffi-macros + +Internal macros used for the FFI layer (bindings) of the Rust Matrix SDK. + +**NOTE:** These are just macros that help build the matrix-rust-sdk bindings, you're probably +interested in the main [rust-sdk](https://github.com/matrix-org/matrix-rust-sdk/) crate. + +[Matrix]: https://matrix.org/ +[Rust]: https://www.rust-lang.org/ diff --git a/testing/matrix-sdk-ffi-macros/src/lib.rs b/testing/matrix-sdk-ffi-macros/src/lib.rs new file mode 100644 index 00000000000..6c55aa0c797 --- /dev/null +++ b/testing/matrix-sdk-ffi-macros/src/lib.rs @@ -0,0 +1,78 @@ +// Copyright 2024 The Matrix.org Foundation C.I.C. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use proc_macro::TokenStream; +use quote::quote; +use syn::{spanned::Spanned as _, ImplItem, Item}; + +/// Attribute to always specify the async runtime parameter for the `uniffi` +/// export macros. +#[proc_macro_attribute] +pub fn export_async(_attr: TokenStream, item: TokenStream) -> TokenStream { + let item = proc_macro2::TokenStream::from(item); + + quote! { + #[uniffi::export(async_runtime = "tokio")] + #item + } + .into() +} + +/// Attribute to always specify the async runtime parameter for the `uniffi` +/// export macros. +#[proc_macro_attribute] +pub fn export(attr: TokenStream, item: TokenStream) -> TokenStream { + let run_checks = || { + let item: Item = syn::parse(item.clone())?; + if let Item::Fn(fun) = &item { + // Fail compilation if the function is async. + if fun.sig.asyncness.is_some() { + let error = syn::Error::new( + fun.span(), + "async function must be exported with #[export_async]", + ); + return Err(error); + } + } else if let Item::Impl(blk) = &item { + // Fail compilation if at least one function in the impl block is async. + for item in &blk.items { + if let ImplItem::Fn(fun) = item { + if fun.sig.asyncness.is_some() { + let error = syn::Error::new( + blk.span(), + "impl block with async functions must be exported with #[export_async]", + ); + return Err(error); + } + } + } + } + + Ok(()) + }; + + let maybe_error = + if let Err(err) = run_checks() { Some(err.into_compile_error()) } else { None }; + + let item = proc_macro2::TokenStream::from(item); + let attr = proc_macro2::TokenStream::from(attr); + + quote! { + #maybe_error + + #[uniffi::export(#attr)] + #item + } + .into() +}