Files
yaoayanui/src-tauri/src/local_config.rs
fengchuanhn@gmail.com 9dfeaa9e18 11
2026-05-17 21:13:45 +08:00

131 lines
3.9 KiB
Rust

//! SQLite 本地键值配置(`app_data_dir/local_config.db`)。
use std::collections::HashMap;
use std::path::PathBuf;
use std::sync::Mutex;
use rusqlite::{params, Connection};
use thiserror::Error;
#[derive(Debug, Error)]
pub enum LocalConfigError {
#[error("{0}")]
Db(#[from] rusqlite::Error),
#[error("{0}")]
Message(String),
}
#[derive(Debug, Clone, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct LocalConfigItem {
pub name: String,
pub value: String,
pub mark: Option<String>,
pub updated_at: i64,
}
#[derive(Debug)]
pub struct LocalConfigDb {
conn: Mutex<Connection>,
}
impl LocalConfigDb {
pub fn open(path: PathBuf) -> Result<Self, LocalConfigError> {
if let Some(parent) = path.parent() {
std::fs::create_dir_all(parent)
.map_err(|e| LocalConfigError::Message(format!("创建配置目录失败: {e}")))?;
}
let conn = Connection::open(path)?;
conn.execute_batch(
r#"
PRAGMA journal_mode = WAL;
CREATE TABLE IF NOT EXISTS local_configs (
name TEXT PRIMARY KEY NOT NULL,
value TEXT NOT NULL,
mark TEXT,
updated_at INTEGER NOT NULL
);
"#,
)?;
Ok(Self {
conn: Mutex::new(conn),
})
}
pub fn list_all_map(&self) -> Result<HashMap<String, String>, LocalConfigError> {
let conn = self.conn.lock().map_err(|_| {
LocalConfigError::Message("本地数据库锁异常".into())
})?;
let mut stmt = conn.prepare(
"SELECT name, value FROM local_configs ORDER BY name ASC",
)?;
let rows = stmt.query_map([], |row| {
Ok((row.get::<_, String>(0)?, row.get::<_, String>(1)?))
})?;
let mut map = HashMap::new();
for row in rows {
let (k, v) = row?;
map.insert(k, v);
}
Ok(map)
}
pub fn list_items(&self) -> Result<Vec<LocalConfigItem>, LocalConfigError> {
let conn = self.conn.lock().map_err(|_| {
LocalConfigError::Message("本地数据库锁异常".into())
})?;
let mut stmt = conn.prepare(
"SELECT name, value, mark, updated_at FROM local_configs ORDER BY name ASC",
)?;
let rows = stmt.query_map([], |row| {
Ok(LocalConfigItem {
name: row.get(0)?,
value: row.get(1)?,
mark: row.get(2)?,
updated_at: row.get(3)?,
})
})?;
let mut items = Vec::new();
for row in rows {
items.push(row?);
}
Ok(items)
}
pub fn upsert(
&self,
name: &str,
value: &str,
mark: Option<&str>,
) -> Result<(), LocalConfigError> {
let name = name.trim();
if name.is_empty() {
return Err(LocalConfigError::Message("配置键名不能为空".into()));
}
let now = chrono::Utc::now().timestamp();
let conn = self.conn.lock().map_err(|_| {
LocalConfigError::Message("本地数据库锁异常".into())
})?;
conn.execute(
r#"
INSERT INTO local_configs (name, value, mark, updated_at)
VALUES (?1, ?2, ?3, ?4)
ON CONFLICT(name) DO UPDATE SET
value = excluded.value,
mark = excluded.mark,
updated_at = excluded.updated_at
"#,
params![name, value, mark, now],
)?;
Ok(())
}
pub fn delete(&self, name: &str) -> Result<(), LocalConfigError> {
let conn = self.conn.lock().map_err(|_| {
LocalConfigError::Message("本地数据库锁异常".into())
})?;
conn.execute("DELETE FROM local_configs WHERE name = ?1", params![name])?;
Ok(())
}
}