use std::path::PathBuf; use std::sync::Mutex; use serde_json::Value; use tauri::AppHandle; use tracing::{error, info}; pub struct SettingsManager { app_handle: AppHandle, settings: Mutex, config_path: PathBuf, } impl SettingsManager { pub fn new(app_handle: AppHandle) -> Self { let config_path = app_handle .path_resolver() .app_config_dir() .expect("Failed to get config dir") .join("settings.json"); let settings = Self::load_settings(&config_path); Self { app_handle, settings: Mutex::new(settings), config_path, } } fn load_settings(path: &PathBuf) -> Value { if !path.exists() { info!("Settings file not found, using defaults"); return Self::default_settings(); } match std::fs::read_to_string(path) { Ok(content) => { match serde_json::from_str(&content) { Ok(settings) => settings, Err(e) => { error!("Failed to parse settings: {}", e); Self::default_settings() } } } Err(e) => { error!("Failed to read settings: {}", e); Self::default_settings() } } } fn default_settings() -> Value { serde_json::json!({ "general": { "start_on_boot": false, "minimize_to_tray": true, "theme": "dark" }, "overlay": { "opacity": 0.9, "click_through": true, "snap_to_edges": true, "auto_hide_delay": 3000 }, "hotkeys": { "overlay_toggle": "Ctrl+Shift+U", "overlay_hide": "Ctrl+Shift+H", "search_universal": "Ctrl+Shift+F", "search_nexus": "Ctrl+Shift+N", "calculator": "Ctrl+Shift+C", "tracker_loot": "Ctrl+Shift+L" }, "plugins": { "auto_activate": [], "disabled": [] }, "log_reader": { "log_path": "", "auto_detect": true }, "nexus": { "cache_enabled": true, "cache_ttl": 3600 } }) } pub fn get(&self, key: &str) -> Result { let settings = self.settings.lock().unwrap(); let keys: Vec<&str> = key.split('.').collect(); let mut current = &*settings; for k in keys { current = current .get(k) .ok_or_else(|| format!("Key not found: {}", key))?; } Ok(current.clone()) } pub fn set( &self, key: &str, value: Value ) -> Result<(), String> { let mut settings = self.settings.lock().unwrap(); let keys: Vec<&str> = key.split('.').collect(); let mut current = settings.as_object_mut() .ok_or("Settings is not an object")?; for (i, k) in keys.iter().enumerate() { if i == keys.len() - 1 { current.insert(k.to_string(), value); } else { current = current .entry(k.to_string()) .or_insert_with(|| serde_json::json!({})) .as_object_mut() .ok_or("Invalid settings structure")?; } } drop(settings); self.save()?; Ok(()) } pub fn get_all(&self ) -> Result { let settings = self.settings.lock().unwrap(); Ok(settings.clone()) } fn save(&self ) -> Result<(), String> { let settings = self.settings.lock().unwrap(); // Ensure directory exists if let Some(parent) = self.config_path.parent() { std::fs::create_dir_all(parent) .map_err(|e| e.to_string())?; } let json = serde_json::to_string_pretty(&*settings) .map_err(|e| e.to_string())?; std::fs::write(&self.config_path, json) .map_err(|e| e.to_string())?; info!("Settings saved to {:?}", self.config_path); Ok(()) } }