core_crypto_ffi/core_crypto/command/
mod.rs

1#[cfg(not(target_family = "wasm"))]
2pub(crate) mod transaction_helper;
3
4use crate::{CoreCrypto, CoreCryptoContext, CoreCryptoResult};
5use std::sync::Arc;
6#[cfg(target_family = "wasm")]
7use wasm_bindgen::prelude::*;
8
9#[cfg(not(target_family = "wasm"))]
10#[uniffi::export(with_foreign)]
11#[async_trait::async_trait]
12pub trait CoreCryptoCommand: Send + Sync {
13    /// Will be called inside a transaction in CoreCrypto
14    async fn execute(&self, context: Arc<CoreCryptoContext>) -> CoreCryptoResult<()>;
15}
16
17#[cfg(not(target_family = "wasm"))]
18#[async_trait::async_trait]
19impl<F, Fut> CoreCryptoCommand for F
20where
21    F: Fn(Arc<CoreCryptoContext>) -> Fut + Send + Sync,
22    Fut: Future<Output = CoreCryptoResult<()>> + Send,
23{
24    async fn execute(&self, context: Arc<CoreCryptoContext>) -> CoreCryptoResult<()> {
25        self(context).await
26    }
27}
28
29#[cfg(target_family = "wasm")]
30#[wasm_bindgen]
31extern "C" {
32    pub type CoreCryptoCommand;
33
34    #[wasm_bindgen(structural, method, catch)]
35    pub async fn execute(this: &CoreCryptoCommand, ctx: CoreCryptoContext) -> Result<(), JsValue>;
36}
37
38/// In uniffi, a Command is an Arc wrapping a dyn trait object
39#[cfg(not(target_family = "wasm"))]
40type Command = Arc<dyn CoreCryptoCommand>;
41
42/// In wasm, a Command is a duck-typed JsValue that someone has promised implements the appropriate interface.
43#[cfg(target_family = "wasm")]
44type Command = CoreCryptoCommand;
45
46#[cfg_attr(target_family = "wasm", wasm_bindgen)]
47#[cfg_attr(not(target_family = "wasm"), uniffi::export)]
48impl CoreCrypto {
49    /// Starts a new transaction in Core Crypto. If the callback succeeds, it will be committed,
50    /// otherwise, every operation performed with the context will be discarded.
51    ///
52    /// When calling this function from within Rust, async functions accepting a context
53    /// implement `CoreCryptoCommand`, so operations can be defined inline as follows:
54    ///
55    /// ```ignore
56    /// core_crypto.transaction(Arc::new(async |context| {
57    ///     // your implementation here
58    ///     Ok(())
59    /// }))?;
60    /// ```
61    pub async fn transaction(&self, command: Command) -> CoreCryptoResult<()> {
62        let inner_context = Arc::new(self.inner.new_transaction().await?);
63
64        let context = CoreCryptoContext {
65            inner: inner_context.clone(),
66        };
67
68        // We need one more layer of Arc-wrapping in uniffi. It's kind of silly, given the
69        // also-mandatory Arc-wrapping internally, but that's the price we have to pay in order
70        // to reuse the code in both target contexts.
71        #[cfg(not(target_family = "wasm"))]
72        let context = Arc::new(context);
73
74        let result = command.execute(context).await;
75        match result {
76            Ok(()) => {
77                inner_context.finish().await?;
78                Ok(())
79            }
80            Err(err) => {
81                inner_context.abort().await?;
82
83                // In wasm only, we are required to manually convert the error type. Uniffi does it for us.
84                #[cfg(target_family = "wasm")]
85                let err = crate::error::core_crypto::CoreCryptoError::TransactionFailed(err).into();
86
87                Err(err)
88            }
89        }
90    }
91}