use anyhow::anyhow; use std::{fs::File, io::Read, path::PathBuf, time::Duration}; use toml::Value; #[derive(Debug, Clone, Copy)] pub enum DialogBackend { Gtk3, } impl DialogBackend { pub fn binary_name(&self) -> &str { match self { Self::Gtk3 => "bscreensaver-dialog-gtk3", } } } impl TryFrom<&str> for DialogBackend { type Error = anyhow::Error; fn try_from(value: &str) -> Result { match value { "gtk3" => Ok(Self::Gtk3), other => Err(anyhow!("'{}' is not a valid dialog backend (valid: 'gtk3')", other)), } } } #[derive(Debug, Clone)] pub struct Configuration { pub lock_timeout: Duration, pub blank_before_locking: Duration, pub dialog_backend: DialogBackend, pub new_login_command: Option, } impl Configuration { pub fn load() -> anyhow::Result { use humantime::parse_duration; let config_tomls = xdg::BaseDirectories::new()? .find_config_files("bscreensaver/bscreensaver.toml") .map(|config_path| parse_config_toml(&config_path)) .collect::, _>>()?; let mut config = Configuration::default(); for config_toml in config_tomls { config.lock_timeout = match config_toml.get("lock-timeout") { None => config.lock_timeout, Some(val) => parse_duration(val.as_str().ok_or(anyhow!("'lock-timeout' must be a duration string like '10m' or '90s'"))?)?, }; config.blank_before_locking = match config_toml.get("blank-before-locking") { None => config.blank_before_locking, Some(val) => parse_duration(val.as_str().ok_or(anyhow!("'blank-before-locking' must be a duration string like '10m' or '90s'"))?)?, }; config.dialog_backend = match config_toml.get("dialog-backend") { None => config.dialog_backend, Some(val) => DialogBackend::try_from(val.as_str().ok_or(anyhow!("'dialog-backend' must be a string"))?)?, }; config.new_login_command = match config_toml.get("new-login-command") { None => config.new_login_command, Some(val) => val.as_str().map(|s| s.to_string()), }; } if config.blank_before_locking >= config.lock_timeout { Err(anyhow!("'blank-before-locking' cannot be greater than 'lock-timeout'")) } else { Ok(config) } } } fn parse_config_toml(config_path: &PathBuf) -> anyhow::Result { let mut f = File::open(config_path)?; let mut config = String::new(); f.read_to_string(&mut config)?; drop(f); let config_toml = config.parse::()?; Ok(config_toml) } impl Default for Configuration { fn default() -> Self { Self { lock_timeout: Duration::from_secs(60 * 10), blank_before_locking: Duration::ZERO, dialog_backend: DialogBackend::Gtk3, new_login_command: None, } } }