use serde::Deserialize; use std::path::{Path, PathBuf}; #[derive(Debug, Clone, Deserialize)] pub struct Config { pub storage_root: PathBuf, #[serde(default = "default_admin_token")] pub admin_token: String, #[serde(default = "default_true")] pub enable_thumbnail_cache: bool, #[serde(default = "default_rebuild_threshold")] pub rebuild_error_threshold: u32, #[serde(default = "default_verify_interval")] pub verify_interval_hours: u64, } fn default_admin_token() -> String { "changeme".to_string() } fn default_true() -> bool { true } fn default_rebuild_threshold() -> u32 { 50 } fn default_verify_interval() -> u64 { 12 } impl Config { pub fn load(path: &Path) -> anyhow::Result { let contents = std::fs::read_to_string(path)?; let config: Config = serde_yaml::from_str(&contents)?; Ok(config) } pub fn db_path(&self) -> PathBuf { self.storage_root.join(".can.db") } pub fn trash_dir(&self) -> PathBuf { self.storage_root.join(".trash") } pub fn thumbs_dir(&self) -> PathBuf { self.storage_root.join(".thumbs") } pub fn ensure_dirs(&self) -> anyhow::Result<()> { std::fs::create_dir_all(&self.storage_root)?; std::fs::create_dir_all(self.trash_dir())?; if self.enable_thumbnail_cache { std::fs::create_dir_all(self.thumbs_dir())?; } Ok(()) } } #[cfg(test)] mod tests { use super::*; use tempfile::TempDir; #[test] fn test_load_config() { let dir = TempDir::new().unwrap(); let config_path = dir.path().join("config.yaml"); std::fs::write( &config_path, r#" storage_root: "/tmp/can_test" admin_token: "test_token" enable_thumbnail_cache: false rebuild_error_threshold: 10 verify_interval_hours: 6 "#, ) .unwrap(); let config = Config::load(&config_path).unwrap(); assert_eq!(config.storage_root, PathBuf::from("/tmp/can_test")); assert_eq!(config.admin_token, "test_token"); assert!(!config.enable_thumbnail_cache); assert_eq!(config.rebuild_error_threshold, 10); assert_eq!(config.verify_interval_hours, 6); } #[test] fn test_config_defaults() { let dir = TempDir::new().unwrap(); let config_path = dir.path().join("config.yaml"); std::fs::write(&config_path, "storage_root: /tmp/test\n").unwrap(); let config = Config::load(&config_path).unwrap(); assert_eq!(config.admin_token, "changeme"); assert!(config.enable_thumbnail_cache); assert_eq!(config.rebuild_error_threshold, 50); assert_eq!(config.verify_interval_hours, 12); } #[test] fn test_derived_paths() { let dir = TempDir::new().unwrap(); let config_path = dir.path().join("config.yaml"); std::fs::write(&config_path, "storage_root: /data/can\n").unwrap(); let config = Config::load(&config_path).unwrap(); assert_eq!(config.db_path(), PathBuf::from("/data/can/.can.db")); assert_eq!(config.trash_dir(), PathBuf::from("/data/can/.trash")); assert_eq!(config.thumbs_dir(), PathBuf::from("/data/can/.thumbs")); } }