Skip to main content

rustauth_tokio_postgres/
connection.rs

1use std::fmt;
2use std::sync::Arc;
3
4use rustauth_core::error::RustAuthError;
5use tokio::sync::RwLock;
6use tokio_postgres::{Client, NoTls};
7
8/// Shared `tokio-postgres` client handle and transaction gate.
9///
10/// Both [`crate::TokioPostgresAdapter`] and
11/// [`crate::TokioPostgresRateLimitStore`] must use the same connection bundle
12/// when they operate on the same physical Postgres connection. The gate
13/// serializes explicit transactions, schema migrations, and rate-limit
14/// consume operations so they cannot interleave on the shared client.
15#[derive(Clone)]
16pub struct TokioPostgresConnection {
17    pub(crate) client: Arc<Client>,
18    pub(crate) tx_gate: Arc<RwLock<()>>,
19}
20
21impl fmt::Debug for TokioPostgresConnection {
22    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
23        formatter
24            .debug_struct("TokioPostgresConnection")
25            .finish_non_exhaustive()
26    }
27}
28
29impl TokioPostgresConnection {
30    /// Wraps an application-owned client and a fresh transaction gate.
31    pub fn from_client(client: Client) -> Self {
32        Self {
33            client: Arc::new(client),
34            tx_gate: Arc::new(RwLock::new(())),
35        }
36    }
37
38    /// Connects to Postgres and spawns the `tokio-postgres` connection driver.
39    pub async fn connect(database_url: &str) -> Result<Self, RustAuthError> {
40        let (client, connection) = tokio_postgres::connect(database_url, NoTls)
41            .await
42            .map_err(crate::errors::postgres_error)?;
43        tokio::spawn(async move {
44            let _connection_result = connection.await;
45        });
46        Ok(Self::from_client(client))
47    }
48
49    /// Reuses the client handle with a fresh transaction gate.
50    ///
51    /// This exists to demonstrate incorrect wiring in tests. Production code
52    /// should share one connection bundle instead of duplicating the gate.
53    #[doc(hidden)]
54    pub fn duplicate_client_unshared_gate(connection: &Self) -> Self {
55        Self {
56            client: Arc::clone(&connection.client),
57            tx_gate: Arc::new(RwLock::new(())),
58        }
59    }
60}