Skip to main content

orbok_db/repo/
settings.rs

1//! App settings repository (RFC-002 ยง7.1). Values are JSON; typed
2//! accessors keep call sites honest. Settings are persistent catalog
3//! data and survive every cleanup except an explicit full reset.
4
5use crate::catalog::{Catalog, db_err};
6use orbok_core::{OrbokError, OrbokResult, now_iso8601};
7use rusqlite::params;
8use serde::Serialize;
9use serde::de::DeserializeOwned;
10
11pub struct SettingsRepository<'a> {
12    catalog: &'a Catalog,
13}
14
15impl<'a> SettingsRepository<'a> {
16    pub fn new(catalog: &'a Catalog) -> Self {
17        Self { catalog }
18    }
19
20    /// Store a typed setting under `key`.
21    pub fn set<T: Serialize>(&self, key: &str, value: &T) -> OrbokResult<()> {
22        let json = serde_json::to_string(value)
23            .map_err(|e| OrbokError::Database(format!("settings serialize: {e}")))?;
24        let conn = self.catalog.lock();
25        conn.execute(
26            "INSERT INTO app_settings (key, value_json, updated_at) VALUES (?1, ?2, ?3) \
27             ON CONFLICT(key) DO UPDATE SET value_json = ?2, updated_at = ?3",
28            params![key, json, now_iso8601()],
29        )
30        .map_err(db_err)?;
31        Ok(())
32    }
33
34    /// Read a typed setting; `None` when unset.
35    pub fn get<T: DeserializeOwned>(&self, key: &str) -> OrbokResult<Option<T>> {
36        let conn = self.catalog.lock();
37        let json: Option<String> = conn
38            .query_row(
39                "SELECT value_json FROM app_settings WHERE key = ?1",
40                params![key],
41                |row| row.get(0),
42            )
43            .map(Some)
44            .or_else(|e| match e {
45                rusqlite::Error::QueryReturnedNoRows => Ok(None),
46                other => Err(db_err(other)),
47            })?;
48        match json {
49            None => Ok(None),
50            Some(json) => serde_json::from_str(&json)
51                .map(Some)
52                .map_err(|e| OrbokError::Database(format!("settings deserialize {key}: {e}"))),
53        }
54    }
55}