use std::path::PathBuf; pub fn get_config_path(cli_override: Option<&str>) -> PathBuf { if let Some(path) = cli_override { return PathBuf::from(path); } if let Ok(path) = std::env::var("LORE_CONFIG_PATH") { return PathBuf::from(path); } let xdg_path = get_xdg_config_dir().join("lore").join("config.json"); if xdg_path.exists() { return xdg_path; } let local_path = PathBuf::from("lore.config.json"); if local_path.exists() { return local_path; } xdg_path } pub fn get_data_dir() -> PathBuf { get_xdg_data_dir().join("lore") } pub fn get_db_path(config_override: Option<&str>) -> PathBuf { if let Some(path) = config_override { return PathBuf::from(path); } get_data_dir().join("lore.db") } pub fn get_log_dir(config_override: Option<&str>) -> PathBuf { if let Some(path) = config_override { return PathBuf::from(path); } get_data_dir().join("logs") } pub fn get_cursor_path(username: &str) -> PathBuf { let safe_username: String = username .chars() .map(|ch| { if ch.is_ascii_alphanumeric() || matches!(ch, '_' | '-' | '.') { ch } else { '_' } }) .collect(); get_data_dir().join(format!("me_cursor_{safe_username}.json")) } pub fn get_backup_dir(config_override: Option<&str>) -> PathBuf { if let Some(path) = config_override { return PathBuf::from(path); } get_data_dir().join("backups") } fn get_xdg_config_dir() -> PathBuf { std::env::var("XDG_CONFIG_HOME") .map(PathBuf::from) .unwrap_or_else(|_| { dirs::home_dir() .unwrap_or_else(|| PathBuf::from(".")) .join(".config") }) } fn get_xdg_data_dir() -> PathBuf { std::env::var("XDG_DATA_HOME") .map(PathBuf::from) .unwrap_or_else(|_| { dirs::home_dir() .unwrap_or_else(|| PathBuf::from(".")) .join(".local") .join("share") }) } /// Enforce restrictive permissions (0600) on the config file. /// Warns to stderr if permissions were too open, then tightens them. #[cfg(unix)] pub fn ensure_config_permissions(path: &std::path::Path) { use std::os::unix::fs::MetadataExt; let Ok(meta) = std::fs::metadata(path) else { return; }; let mode = meta.mode() & 0o777; if mode != 0o600 { eprintln!( "Warning: config file permissions were {mode:04o}, tightening to 0600: {}", path.display() ); let _ = set_permissions_600(path); } } #[cfg(unix)] fn set_permissions_600(path: &std::path::Path) -> std::io::Result<()> { use std::os::unix::fs::PermissionsExt; let perms = std::fs::Permissions::from_mode(0o600); std::fs::set_permissions(path, perms) } /// No-op on non-Unix platforms. #[cfg(not(unix))] pub fn ensure_config_permissions(_path: &std::path::Path) {} #[cfg(test)] mod tests { use super::*; #[test] fn test_cli_override_takes_precedence() { let path = get_config_path(Some("/custom/path.json")); assert_eq!(path, PathBuf::from("/custom/path.json")); } }