use std::io::ErrorKind; use std::path::{Path, PathBuf}; use serde::Deserialize; use tracing::{debug, info}; use url::Url; #[derive(Debug, thiserror::Error)] pub enum Error { #[error("Unable to read configuration file: {0}")] Io(#[from] std::io::Error), #[error("Failed to parse configuration file: {0}")] Toml(#[from] toml::de::Error), } #[derive(Debug, Deserialize)] pub struct EtcdConfig { #[serde(default = "default_etcd_hosts")] pub hosts: Vec, pub client_cert: Option, pub client_key: Option, pub ca_cert: Option, } #[derive(Debug, Deserialize)] pub struct Configuration { #[serde(default = "default_namespace")] pub namespace: String, #[serde(default)] pub path_prefix: PathBuf, pub etcd: EtcdConfig, } impl Default for EtcdConfig { fn default() -> Self { Self { hosts: default_etcd_hosts(), client_cert: None, client_key: None, ca_cert: None, } } } impl Default for Configuration { fn default() -> Self { Self { namespace: default_namespace(), path_prefix: Default::default(), etcd: Default::default(), } } } impl Configuration { pub fn load(path: Option>) -> Result { let path = path .map(|p| p.as_ref().into()) .unwrap_or_else(Self::default_path); debug!("Loading configuration from {}", path.display()); let config = match std::fs::read_to_string(&path) { Ok(d) => toml::from_str(&d)?, Err(e) if e.kind() == ErrorKind::NotFound => { debug!("{}: {}", path.display(), e); Default::default() } Err(e) => return Err(e.into()), }; match std::env::var("DUMP_CONFIG") { Ok(s) if s == "1" => info!("{:?}", config), Ok(_) | Err(_) => (), }; Ok(config) } pub fn default_path() -> PathBuf { match std::env::var_os(format!( "{}_CONFIG", env!("CARGO_PKG_NAME").to_uppercase() )) { Some(v) => PathBuf::from(v), None => match std::env::var_os("XDG_CONFIG_HOME") { Some(v) => { let mut p = PathBuf::from(v); p.push(env!("CARGO_PKG_NAME")); p.push("config.toml"); p } None => match std::env::var_os("HOME") { Some(v) => { let mut p = PathBuf::from(v); p.push(".config"); p.push(env!("CARGO_PKG_NAME")); p.push("config.toml"); p } None => PathBuf::from("config.toml"), }, }, } } } fn default_namespace() -> String { env!("CARGO_PKG_NAME").into() } fn default_etcd_hosts() -> Vec { vec![Url::parse("http://localhost:2379").unwrap()] }