sbol_db_sqlite/repo/
config.rs1use async_trait::async_trait;
8use chrono::{DateTime, Utc};
9use sbol_db_core::{ConfigEntry, DomainError};
10use sbol_db_storage::ConfigStore;
11use serde_json::Value;
12use sqlx::Row;
13
14use crate::pool::db_err;
15use crate::SqlitePool;
16
17#[derive(Clone)]
18pub struct SqliteConfigStore {
19 pool: SqlitePool,
20}
21
22impl SqliteConfigStore {
23 pub fn new(pool: SqlitePool) -> Self {
24 Self { pool }
25 }
26}
27
28fn parse_value(text: &str) -> Result<Value, DomainError> {
31 serde_json::from_str(text)
32 .map_err(|e| DomainError::Database(format!("config value is not valid JSON: {e}")))
33}
34
35#[async_trait]
36impl ConfigStore for SqliteConfigStore {
37 async fn get(&self, key: &str) -> Result<Option<Value>, DomainError> {
38 let row = sqlx::query("SELECT value FROM sbh_app_config WHERE key = ?")
39 .bind(key)
40 .fetch_optional(&self.pool)
41 .await
42 .map_err(db_err)?;
43 match row {
44 Some(r) => Ok(Some(parse_value(
45 &r.try_get::<String, _>("value").map_err(db_err)?,
46 )?)),
47 None => Ok(None),
48 }
49 }
50
51 async fn set(&self, key: &str, value: &Value) -> Result<(), DomainError> {
52 let text = serde_json::to_string(value)?;
53 sqlx::query(
54 "INSERT INTO sbh_app_config (key, value, updated_at) VALUES (?, ?, ?) \
55 ON CONFLICT (key) DO UPDATE SET value = excluded.value, updated_at = excluded.updated_at",
56 )
57 .bind(key)
58 .bind(text)
59 .bind(Utc::now())
60 .execute(&self.pool)
61 .await
62 .map_err(db_err)?;
63 Ok(())
64 }
65
66 async fn get_all(&self) -> Result<Vec<ConfigEntry>, DomainError> {
67 let rows = sqlx::query("SELECT key, value, updated_at FROM sbh_app_config")
68 .fetch_all(&self.pool)
69 .await
70 .map_err(db_err)?;
71 let mut out = Vec::with_capacity(rows.len());
72 for r in rows {
73 out.push(ConfigEntry {
74 key: r.try_get("key").map_err(db_err)?,
75 value: parse_value(&r.try_get::<String, _>("value").map_err(db_err)?)?,
76 updated_at: r
77 .try_get::<DateTime<Utc>, _>("updated_at")
78 .map_err(db_err)?,
79 });
80 }
81 Ok(out)
82 }
83
84 async fn delete(&self, key: &str) -> Result<(), DomainError> {
85 sqlx::query("DELETE FROM sbh_app_config WHERE key = ?")
86 .bind(key)
87 .execute(&self.pool)
88 .await
89 .map_err(db_err)?;
90 Ok(())
91 }
92}