Skip to main content

cdk_ffi/
postgres.rs

1use std::sync::Arc;
2
3use cdk_postgres::PgConnectionPool;
4
5use crate::{
6    CurrencyUnit, FfiError, FfiWalletSQLDatabase, Id, KeySet, KeySetInfo, Keys, MeltQuote,
7    MintInfo, MintQuote, MintUrl, ProofInfo, ProofState, PublicKey, SpendingConditions,
8    Transaction, TransactionDirection, TransactionId, WalletDatabase,
9};
10
11#[derive(uniffi::Object)]
12pub struct WalletPostgresDatabase {
13    inner: Arc<FfiWalletSQLDatabase<PgConnectionPool>>,
14}
15
16// Keep a long-lived Tokio runtime for Postgres-created resources so that
17// background tasks (e.g., tokio-postgres connection drivers spawned during
18// construction) are not tied to a short-lived, ad-hoc runtime.
19#[cfg(feature = "postgres")]
20static PG_RUNTIME: once_cell::sync::OnceCell<tokio::runtime::Runtime> =
21    once_cell::sync::OnceCell::new();
22
23#[cfg(feature = "postgres")]
24fn pg_runtime() -> &'static tokio::runtime::Runtime {
25    PG_RUNTIME.get_or_init(|| {
26        tokio::runtime::Builder::new_multi_thread()
27            .enable_all()
28            .thread_name("cdk-ffi-pg")
29            .build()
30            .expect("failed to build pg runtime")
31    })
32}
33
34#[uniffi::export]
35impl WalletPostgresDatabase {
36    /// Create a new Postgres-backed wallet database
37    /// Requires cdk-ffi to be built with feature "postgres".
38    /// Example URL:
39    ///  "host=localhost user=test password=test dbname=testdb port=5433 schema=wallet sslmode=prefer"
40    #[cfg(feature = "postgres")]
41    #[uniffi::constructor]
42    pub fn new(url: String) -> Result<Arc<Self>, FfiError> {
43        let inner = match tokio::runtime::Handle::try_current() {
44            Ok(handle) => tokio::task::block_in_place(|| {
45                handle.block_on(
46                    async move { cdk_postgres::new_wallet_pg_database(url.as_str()).await },
47                )
48            }),
49            // Important: use a process-long runtime so background connection tasks stay alive.
50            Err(_) => pg_runtime()
51                .block_on(async move { cdk_postgres::new_wallet_pg_database(url.as_str()).await }),
52        }
53        .map_err(FfiError::internal)?;
54        Ok(Arc::new(WalletPostgresDatabase {
55            inner: FfiWalletSQLDatabase::new(inner),
56        }))
57    }
58}
59
60// Use macro to implement WalletDatabase trait - delegates all methods to inner
61crate::impl_ffi_wallet_database!(WalletPostgresDatabase);