sbol_db_postgres/repo/
config.rs1use async_trait::async_trait;
8use sbol_db_core::{ConfigEntry, DomainError};
9use sbol_db_storage::ConfigStore;
10use serde_json::Value;
11use sqlx::Row;
12
13use crate::repo::db_err;
14use crate::PgPool;
15
16#[derive(Clone)]
17pub struct PgConfigStore {
18 pool: PgPool,
19}
20
21impl PgConfigStore {
22 pub fn new(pool: PgPool) -> Self {
23 Self { pool }
24 }
25}
26
27#[async_trait]
28impl ConfigStore for PgConfigStore {
29 async fn get(&self, key: &str) -> Result<Option<Value>, DomainError> {
30 let row = sqlx::query("SELECT value FROM sbh_app_config WHERE key = $1")
31 .bind(key)
32 .fetch_optional(&self.pool)
33 .await
34 .map_err(db_err)?;
35 match row {
36 Some(r) => Ok(Some(r.try_get::<Value, _>("value").map_err(db_err)?)),
37 None => Ok(None),
38 }
39 }
40
41 async fn set(&self, key: &str, value: &Value) -> Result<(), DomainError> {
42 sqlx::query(
43 "INSERT INTO sbh_app_config (key, value, updated_at) VALUES ($1, $2, now()) \
44 ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value, updated_at = now()",
45 )
46 .bind(key)
47 .bind(value)
48 .execute(&self.pool)
49 .await
50 .map_err(db_err)?;
51 Ok(())
52 }
53
54 async fn get_all(&self) -> Result<Vec<ConfigEntry>, DomainError> {
55 let rows = sqlx::query("SELECT key, value, updated_at FROM sbh_app_config")
56 .fetch_all(&self.pool)
57 .await
58 .map_err(db_err)?;
59 let mut out = Vec::with_capacity(rows.len());
60 for r in rows {
61 out.push(ConfigEntry {
62 key: r.try_get("key").map_err(db_err)?,
63 value: r.try_get("value").map_err(db_err)?,
64 updated_at: r.try_get("updated_at").map_err(db_err)?,
65 });
66 }
67 Ok(out)
68 }
69
70 async fn delete(&self, key: &str) -> Result<(), DomainError> {
71 sqlx::query("DELETE FROM sbh_app_config WHERE key = $1")
72 .bind(key)
73 .execute(&self.pool)
74 .await
75 .map_err(db_err)?;
76 Ok(())
77 }
78}