use std::path::PathBuf; use config::{Config, ConfigError, Environment}; use directories::ProjectDirs; use serde::Deserialize; use thiserror::Error; use tracing::debug; #[derive(Error, Debug)] pub enum AppConfigError { #[error("No valid home directory found.")] HomePathError, #[error("Configuration error.")] ConfigError(#[from] ConfigError), } fn default_server_address() -> String { String::from("[::1]:3000") } fn default_feeds() -> Vec { Vec::new() } #[derive(Clone, Debug, Default, Deserialize)] pub struct PenguinConfig { #[serde(default = "default_server_address")] pub server_address: String, #[serde(default = "default_feeds")] pub feeds: Vec, } impl PenguinConfig { /// Find the correct project directory for the respective operating system. fn get_project_dirs() -> Result { let project_dirs = ProjectDirs::from("ch", "vanwa", "findpenguins-feed") .ok_or(AppConfigError::HomePathError)?; Ok(project_dirs) } /// Find the correct configuration file for the respective operating system. /// - linux: `$XDG_CONFIG_HOME/stray/config.toml` or `$HOME/.config/stray/config.toml` /// - macOS: `$HOME/Library/Preferences/ch.fhnw.stray/config.toml` /// - windows: `{FOLDERID_RoamingAppData}/stray/config/config.toml` fn config_path() -> Result { let project_dirs = Self::get_project_dirs()?; let config_path = project_dirs.config_dir(); let config_file = config_path.join("config.toml"); Ok(config_file) } /// Read the server configuration. /// See [config_path](struct.AppConfig.html#method.config_path) for the different configuration file paths. /// Everything in the config file can be overridden at runtime by setting the respective ENV variables. /// Use `__` (double underscore) as a separator between hierarchies. /// For example `WOWEB__INITIAL_REMOTES="http://[::1]:3002,http://[::1]:3003"` /// This is necessary for configuration keys to have single underscores. pub fn read() -> Result { let mut config_builder = Config::builder(); if let Ok(config_path) = Self::config_path() { debug!("trying config file at {:#?}...", config_path); config_builder = config_builder.add_source(config::File::from(config_path).required(false)); } debug!("merging settings from environment variables..."); config_builder = config_builder.add_source( Environment::with_prefix("findpenguins_feed") .separator("__") .list_separator(",") .with_list_parse_key("feeds") .try_parsing(true), ); Ok(config_builder.build()?.try_deserialize()?) } }