use anyhow::Result; use directories::ProjectDirs; use keyring_core::Entry; use serde::{Deserialize, Serialize}; use std::path::PathBuf; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Config { pub api_url: String, } const KEYRING_SERVICE: &str = "movie-tui"; const KEYRING_USER: &str = "jwt-token"; fn config_path() -> Option { ProjectDirs::from("com", "movies", "movie-tui") .map(|dirs| dirs.config_dir().join("config.json")) } impl Config { pub fn load() -> Option { let path = config_path()?; let content = std::fs::read_to_string(path).ok()?; serde_json::from_str(&content).ok() } pub fn save(&self) -> Result<()> { let path = config_path().ok_or_else(|| anyhow::anyhow!("no config dir"))?; std::fs::create_dir_all(path.parent().unwrap())?; let content = serde_json::to_string_pretty(self)?; std::fs::write(path, content)?; Ok(()) } #[allow(unreachable_code)] pub fn init_keyring() -> Result<()> { #[cfg(feature = "macos")] { use apple_native_keyring_store::keychain::Store; keyring_core::set_default_store(Store::new()?); return Ok(()); } #[cfg(feature = "linux-zbus")] { keyring_core::set_default_store(zbus_secret_service_keyring_store::Store::new()?); return Ok(()); } #[cfg(feature = "windows")] { keyring_core::set_default_store(windows_native_keyring_store::Store::new()?); return Ok(()); } anyhow::bail!( "no keyring backend compiled in — build with --features macos|linux-zbus|windows" ) } pub fn load_token() -> Option { Entry::new(KEYRING_SERVICE, KEYRING_USER) .ok() .and_then(|e| e.get_password().ok()) } pub fn save_token(token: &str) -> Result<()> { let entry = Entry::new(KEYRING_SERVICE, KEYRING_USER)?; entry.set_password(token)?; Ok(()) } pub fn clear_token() -> Result<()> { let entry = Entry::new(KEYRING_SERVICE, KEYRING_USER)?; let _ = entry.delete_credential(); Ok(()) } } #[cfg(test)] #[path = "tests/config.rs"] mod tests;