//! Gestion de la configuration TOML use anyhow::{Context, Result}; use serde::{Deserialize, Serialize}; use std::path::PathBuf; /// Configuration complète d'imagod-web #[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct Config { #[serde(default)] pub imago: ImagoConfig, #[serde(default)] pub watcher: WatcherConfig, #[serde(default)] pub dedup: DedupConfig, #[serde(default)] pub ui: UiConfig, #[serde(default)] pub logging: LoggingConfig, } #[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct ImagoConfig { #[serde(default = "default_imago_url")] pub url: String, #[serde(default)] pub api_key: String, } fn default_imago_url() -> String { "http://imago.dracodev.net:8000".into() } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct WatcherConfig { #[serde(default = "default_true")] pub capture_images: bool, #[serde(default = "default_poll_interval")] pub poll_interval_ms: u64, /// Dossier à surveiller pour les nouveaux screenshots (Linux/macOS) /// Si vide, utilise le clipboard (Windows uniquement) #[serde(default)] pub watch_dir: Option, #[serde(default)] pub filters: WatcherFilters, } impl Default for WatcherConfig { fn default() -> Self { Self { capture_images: true, poll_interval_ms: 500, watch_dir: None, filters: WatcherFilters::default(), } } } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct WatcherFilters { #[serde(default)] pub only_apps: Vec, #[serde(default = "default_exclude_apps")] pub exclude_apps: Vec, } impl Default for WatcherFilters { fn default() -> Self { Self { only_apps: vec![], exclude_apps: vec!["mstsc".into()], } } } fn default_exclude_apps() -> Vec { vec!["mstsc".into()] } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct DedupConfig { #[serde(default = "default_dedup_method")] pub method: String, #[serde(default = "default_cache_size")] pub cache_size: usize, } impl Default for DedupConfig { fn default() -> Self { Self { method: default_dedup_method(), cache_size: default_cache_size(), } } } fn default_dedup_method() -> String { "sha256".into() } fn default_cache_size() -> usize { 10000 } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct UiConfig { #[serde(default = "default_true")] pub show_notifications: bool, #[serde(default = "default_true")] pub show_tray_icon: bool, #[serde(default = "default_notification_timeout")] pub notification_timeout_sec: u64, } impl Default for UiConfig { fn default() -> Self { Self { show_notifications: true, show_tray_icon: true, notification_timeout_sec: default_notification_timeout(), } } } fn default_notification_timeout() -> u64 { 3 } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct LoggingConfig { #[serde(default = "default_log_level")] pub level: String, } impl Default for LoggingConfig { fn default() -> Self { Self { level: default_log_level(), } } } fn default_log_level() -> String { "info".into() } fn default_true() -> bool { true } fn default_poll_interval() -> u64 { 500 } impl Config { /// Charge la configuration depuis `%APPDATA%/imagod-web/imagod.toml` pub fn load() -> Result { let path = Self::config_path()?; if path.exists() { let content = std::fs::read_to_string(&path) .with_context(|| format!("Impossible de lire {}", path.display()))?; let config: Config = toml::from_str(&content) .with_context(|| "Erreur de parsing du fichier de configuration")?; Ok(config) } else { let config = Config::default(); config.save()?; Ok(config) } } /// Sauvegarde la configuration pub fn save(&self) -> Result<()> { let path = Self::config_path()?; if let Some(parent) = path.parent() { std::fs::create_dir_all(parent)?; } let content = toml::to_string_pretty(self)?; std::fs::write(&path, content)?; Ok(()) } /// Chemin du fichier de configuration fn config_path() -> Result { let base = if cfg!(target_os = "windows") { let appdata = std::env::var("APPDATA").context("Variable APPDATA non définie")?; PathBuf::from(appdata) } else { let home = std::env::var("HOME").context("Variable HOME non définie")?; PathBuf::from(home).join(".config") }; Ok(base.join("imagod-web").join("imagod.toml")) } }