Skip to main content

store_sqlite/
config_store.rs

1//! `SqliteConfigStore`: thin key-value config persistence for gateway management.
2
3use std::sync::Mutex;
4
5use chrono::Utc;
6use rusqlite::{params, Connection};
7use serde::Serialize;
8
9use crate::error::StoreError;
10use crate::schema;
11
12/// A key-value config entry.
13#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
14pub struct ConfigEntry {
15    /// Config key.
16    pub key: String,
17    /// Config value (opaque string).
18    pub value: String,
19}
20
21/// SQLite-backed config store for gateway management endpoints.
22pub struct SqliteConfigStore {
23    conn: Mutex<Connection>,
24}
25
26impl SqliteConfigStore {
27    /// Open (or create) a store at the given file path.
28    pub fn open(path: &str) -> Result<Self, StoreError> {
29        let conn = Connection::open(path)?;
30        schema::init(&conn)?;
31        Ok(Self {
32            conn: Mutex::new(conn),
33        })
34    }
35
36    /// Open a transient in-memory store (useful in tests).
37    pub fn open_in_memory() -> Result<Self, StoreError> {
38        let conn = Connection::open_in_memory()?;
39        schema::init(&conn)?;
40        Ok(Self {
41            conn: Mutex::new(conn),
42        })
43    }
44
45    /// Upsert a config key.
46    pub fn set(&self, key: &str, value: &str) -> Result<ConfigEntry, StoreError> {
47        let updated_at = Utc::now().to_rfc3339();
48        let conn = self.conn.lock().unwrap();
49        conn.execute(
50            "INSERT INTO gateway_config (key, value, updated_at) VALUES (?1, ?2, ?3)
51             ON CONFLICT(key) DO UPDATE SET value = excluded.value, updated_at = excluded.updated_at",
52            params![key, value, updated_at],
53        )?;
54        Ok(ConfigEntry {
55            key: key.to_string(),
56            value: value.to_string(),
57        })
58    }
59
60    /// Fetch a config key, if present.
61    pub fn get(&self, key: &str) -> Result<Option<ConfigEntry>, StoreError> {
62        let conn = self.conn.lock().unwrap();
63        let mut stmt = conn.prepare("SELECT key, value FROM gateway_config WHERE key = ?1")?;
64        let mut rows = stmt.query(params![key])?;
65        if let Some(row) = rows.next()? {
66            Ok(Some(ConfigEntry {
67                key: row.get(0)?,
68                value: row.get(1)?,
69            }))
70        } else {
71            Ok(None)
72        }
73    }
74
75    /// Delete a config key. Returns true if a row was removed.
76    pub fn delete(&self, key: &str) -> Result<bool, StoreError> {
77        let conn = self.conn.lock().unwrap();
78        let n = conn.execute("DELETE FROM gateway_config WHERE key = ?1", params![key])?;
79        Ok(n > 0)
80    }
81
82    /// List all config entries ordered by key.
83    pub fn list(&self) -> Result<Vec<ConfigEntry>, StoreError> {
84        let conn = self.conn.lock().unwrap();
85        let mut stmt = conn.prepare("SELECT key, value FROM gateway_config ORDER BY key")?;
86        let rows = stmt.query_map([], |row| {
87            Ok(ConfigEntry {
88                key: row.get(0)?,
89                value: row.get(1)?,
90            })
91        })?;
92        rows.collect::<Result<Vec<_>, _>>()
93            .map_err(StoreError::from)
94    }
95}