Skip to main content

rustvello_postgres/
client_data_store.rs

1//! PostgreSQL-backed [`ClientDataStore`] implementation.
2
3use std::sync::Arc;
4
5use async_trait::async_trait;
6
7use rustvello_core::client_data_store::ClientDataStore;
8use rustvello_core::error::RustvelloResult;
9
10use crate::db::{pg_err, Database};
11
12/// PostgreSQL-backed client data store.
13///
14/// Stores large serialized values in a `client_data` table.
15/// Persists across process restarts.
16pub struct PostgresClientDataStore {
17    db: Arc<Database>,
18}
19
20impl PostgresClientDataStore {
21    pub fn new(db: Arc<Database>) -> Self {
22        Self { db }
23    }
24}
25
26#[async_trait]
27impl ClientDataStore for PostgresClientDataStore {
28    async fn store(&self, key: &str, value: &str) -> RustvelloResult<()> {
29        let client = self.db.conn().await?;
30        client
31            .execute(
32                "INSERT INTO client_data (data_key, data_value) VALUES ($1, $2)
33                 ON CONFLICT (data_key) DO UPDATE SET data_value = $2",
34                &[&key, &value],
35            )
36            .await
37            .map_err(pg_err)?;
38        Ok(())
39    }
40
41    async fn retrieve(&self, key: &str) -> RustvelloResult<String> {
42        let client = self.db.conn().await?;
43        let row = client
44            .query_opt(
45                "SELECT data_value FROM client_data WHERE data_key = $1",
46                &[&key],
47            )
48            .await
49            .map_err(pg_err)?
50            .ok_or_else(|| {
51                rustvello_core::error::RustvelloError::state_backend(format!(
52                    "key not found: {key}"
53                ))
54            })?;
55        Ok(row.get(0))
56    }
57
58    async fn purge(&self) -> RustvelloResult<()> {
59        let client = self.db.conn().await?;
60        client
61            .execute("DELETE FROM client_data", &[])
62            .await
63            .map_err(pg_err)?;
64        Ok(())
65    }
66}