feat: 添加应用配置管理功能,支持加载和保存配置

This commit is contained in:
Vixalie
2026-03-29 22:12:06 +08:00
parent eefbe42f2b
commit 2707e9ecdb
4 changed files with 139 additions and 1 deletions

View File

@@ -24,4 +24,7 @@ serde = { version = "1", features = ["derive"] }
serde_json = "1"
tauri-plugin-fs = "2"
tauri-plugin-dialog = "2"
blake3 = { version = "1.8.3", features = ["serde", "digest", "pure"] }
anyhow = "1.0.102"
thiserror = "2.0.18"

View File

@@ -1,3 +1,5 @@
use crate::config::{AppConfig, AppConfigState};
use tauri::State;
use tauri::{AppHandle, WebviewWindow};
#[tauri::command]
@@ -12,3 +14,22 @@ pub fn set_window_title(
.unwrap_or(app_name);
window.set_title(&new_title).map_err(|e| e.to_string())
}
#[tauri::command]
pub fn load_app_config(state: State<'_, AppConfigState>) -> Result<AppConfig, String> {
state.get().map_err(|e| format!("读取应用配置失败: {e}"))
}
#[tauri::command]
pub fn save_app_config(
app_handle: AppHandle,
state: State<'_, AppConfigState>,
config: AppConfig,
) -> Result<(), String> {
config
.save(&app_handle)
.map_err(|e| format!("保存应用配置失败: {e}"))?;
state
.set(config)
.map_err(|e| format!("更新应用配置状态失败: {e}"))
}

101
src-tauri/src/config.rs Normal file
View File

@@ -0,0 +1,101 @@
use serde::{Deserialize, Serialize};
use std::{fs, path::PathBuf, sync::RwLock};
use tauri::{path::BaseDirectory, AppHandle, Manager};
#[derive(Clone, Deserialize, Serialize)]
pub enum ProxyKind {
Http,
Socks5,
}
impl Default for ProxyKind {
fn default() -> Self {
Self::Http
}
}
#[derive(Clone, Deserialize, Serialize, Default)]
#[serde(rename_all = "camelCase")]
pub struct ProxyConfig {
pub enabled: bool,
pub kind: ProxyKind,
pub host: Option<String>,
pub port: Option<u16>,
pub username: Option<String>,
pub password: Option<String>,
}
#[derive(Clone, Deserialize, Serialize, Default)]
#[serde(rename_all = "camelCase")]
pub struct AppConfig {
pub proxy: Option<ProxyConfig>,
}
pub struct AppConfigState {
inner: RwLock<AppConfig>,
}
impl AppConfigState {
pub fn new(config: AppConfig) -> Self {
Self {
inner: RwLock::new(config),
}
}
pub fn get(&self) -> anyhow::Result<AppConfig> {
let guard = self
.inner
.read()
.map_err(|_| anyhow::anyhow!("读取应用配置状态失败"))?;
Ok(guard.clone())
}
pub fn set(&self, config: AppConfig) -> anyhow::Result<()> {
let mut guard = self
.inner
.write()
.map_err(|_| anyhow::anyhow!("写入应用配置状态失败"))?;
*guard = config;
Ok(())
}
}
impl AppConfig {
fn config_path(app_handle: &AppHandle) -> anyhow::Result<PathBuf> {
Ok(app_handle
.path()
.resolve("config.json", BaseDirectory::AppData)?)
}
pub fn save(&self, app_handle: &AppHandle) -> anyhow::Result<()> {
let config_path = Self::config_path(app_handle)?;
if let Some(parent_dir) = config_path.parent() {
fs::create_dir_all(parent_dir)?;
}
let content = serde_json::to_string_pretty(self)?;
fs::write(config_path, content)?;
Ok(())
}
pub fn load(app_handle: &AppHandle) -> anyhow::Result<Self> {
let config_path = Self::config_path(app_handle)?;
if !config_path.exists() {
return Ok(Self::default());
}
let content = fs::read_to_string(config_path)?;
Ok(serde_json::from_str(&content)?)
}
pub fn load_or_init(app_handle: &AppHandle) -> anyhow::Result<Self> {
let config_path = Self::config_path(app_handle)?;
if !config_path.exists() {
let default_config = Self::default();
default_config.save(app_handle)?;
return Ok(default_config);
}
Self::load(app_handle)
}
}

View File

@@ -1,12 +1,25 @@
mod commands;
mod config;
use tauri::Manager;
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.setup(|app| {
let app_handle = app.handle().clone();
let config = config::AppConfig::load_or_init(&app_handle)?;
app.manage(config::AppConfigState::new(config));
Ok(())
})
.plugin(tauri_plugin_dialog::init())
.plugin(tauri_plugin_fs::init())
.plugin(tauri_plugin_opener::init())
.invoke_handler(tauri::generate_handler![commands::set_window_title])
.invoke_handler(tauri::generate_handler![
commands::set_window_title,
commands::load_app_config,
commands::save_app_config
])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}