use std::collections::HashMap; use std::sync::Arc; use cdk_sqlite::wallet::WalletSqliteDatabase as CdkWalletSqliteDatabase; use cdk_sqlite::SqliteConnectionManager; use crate::{ CurrencyUnit, FfiError, FfiWalletSQLDatabase, Id, KeySetInfo, Keys, MeltQuote, MintInfo, MintQuote, MintUrl, ProofInfo, ProofState, PublicKey, SpendingConditions, Transaction, TransactionDirection, TransactionId, WalletDatabase, }; /// FFI-compatible WalletSqliteDatabase implementation that implements the WalletDatabaseFfi trait #[derive(uniffi::Object)] pub struct WalletSqliteDatabase { inner: Arc>, } #[uniffi::export] impl WalletSqliteDatabase { /// Create a new WalletSqliteDatabase with the given work directory #[uniffi::constructor] pub fn new(file_path: String) -> Result, FfiError> { let db = match tokio::runtime::Handle::try_current() { Ok(handle) => tokio::task::block_in_place(|| { handle .block_on(async move { CdkWalletSqliteDatabase::new(file_path.as_str()).await }) }), Err(_) => { // No current runtime, create a new one tokio::runtime::Runtime::new() .map_err(|e| FfiError::Database { msg: format!("Failed to create runtime: {}", e), })? .block_on(async move { CdkWalletSqliteDatabase::new(file_path.as_str()).await }) } } .map_err(|e| FfiError::Database { msg: e.to_string() })?; Ok(Arc::new(Self { inner: FfiWalletSQLDatabase::new(db), })) } /// Create an in-memory database #[uniffi::constructor] pub fn new_in_memory() -> Result, FfiError> { let db = match tokio::runtime::Handle::try_current() { Ok(handle) => tokio::task::block_in_place(|| { handle.block_on(async move { cdk_sqlite::wallet::memory::empty().await }) }), Err(_) => { // No current runtime, create a new one tokio::runtime::Runtime::new() .map_err(|e| FfiError::Database { msg: format!("Failed to create runtime: {}", e), })? .block_on(async move { cdk_sqlite::wallet::memory::empty().await }) } } .map_err(|e| FfiError::Database { msg: e.to_string() })?; Ok(Arc::new(Self { inner: FfiWalletSQLDatabase::new(db), })) } } #[uniffi::export(async_runtime = "tokio")] #[async_trait::async_trait] impl WalletDatabase for WalletSqliteDatabase { async fn begin_db_transaction( &self, ) -> Result, FfiError> { self.inner.begin_db_transaction().await } async fn get_mint(&self, mint_url: MintUrl) -> Result, FfiError> { self.inner.get_mint(mint_url).await } async fn get_mints(&self) -> Result>, FfiError> { self.inner.get_mints().await } async fn get_mint_keysets( &self, mint_url: MintUrl, ) -> Result>, FfiError> { self.inner.get_mint_keysets(mint_url).await } async fn get_keyset_by_id(&self, keyset_id: Id) -> Result, FfiError> { self.inner.get_keyset_by_id(keyset_id).await } async fn get_mint_quote(&self, quote_id: String) -> Result, FfiError> { self.inner.get_mint_quote(quote_id).await } async fn get_mint_quotes(&self) -> Result, FfiError> { self.inner.get_mint_quotes().await } async fn get_unissued_mint_quotes(&self) -> Result, FfiError> { self.inner.get_unissued_mint_quotes().await } async fn get_melt_quote(&self, quote_id: String) -> Result, FfiError> { self.inner.get_melt_quote(quote_id).await } async fn get_melt_quotes(&self) -> Result, FfiError> { self.inner.get_melt_quotes().await } async fn get_keys(&self, id: Id) -> Result, FfiError> { self.inner.get_keys(id).await } async fn get_proofs( &self, mint_url: Option, unit: Option, state: Option>, spending_conditions: Option>, ) -> Result, FfiError> { self.inner .get_proofs(mint_url, unit, state, spending_conditions) .await } async fn get_proofs_by_ys(&self, ys: Vec) -> Result, FfiError> { self.inner.get_proofs_by_ys(ys).await } async fn get_balance( &self, mint_url: Option, unit: Option, state: Option>, ) -> Result { self.inner.get_balance(mint_url, unit, state).await } async fn get_transaction( &self, transaction_id: TransactionId, ) -> Result, FfiError> { self.inner.get_transaction(transaction_id).await } async fn list_transactions( &self, mint_url: Option, direction: Option, unit: Option, ) -> Result, FfiError> { self.inner .list_transactions(mint_url, direction, unit) .await } }