11
This commit is contained in:
130
src-tauri/src/local_config.rs
Normal file
130
src-tauri/src/local_config.rs
Normal file
@@ -0,0 +1,130 @@
|
||||
//! 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(())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user