mithril_client_cli/
configuration.rsuse serde::Deserialize;
use std::collections::HashMap;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum ConfigError {
#[error("Parameter '{0}' is mandatory.")]
Required(String),
#[error("Parameter '{0}' cannot be converted to string.")]
Conversion(String),
}
#[derive(Debug, Default, PartialEq, Deserialize)]
#[serde(default)]
pub struct ConfigParameters {
parameters: HashMap<String, String>,
}
impl ConfigParameters {
pub fn new(parameters: HashMap<String, String>) -> Self {
Self { parameters }
}
#[cfg(test)]
pub fn build(parameters: &[(&str, &str)]) -> Self {
let parameters = parameters
.iter()
.map(|(k, v)| (k.to_string(), v.to_string()))
.collect();
Self::new(parameters)
}
#[cfg(test)]
pub fn add_parameter(&mut self, name: &str, value: &str) -> &mut Self {
let _ = self.parameters.insert(name.to_string(), value.to_string());
self
}
pub fn add_source(mut self, source: &impl ConfigSource) -> Result<Self, ConfigError> {
let extra = source.collect()?;
self.parameters.extend(extra);
Ok(self)
}
pub fn get(&self, name: &str) -> Option<String> {
self.parameters.get(name).cloned()
}
pub fn get_or(&self, name: &str, default: &str) -> String {
self.get(name).unwrap_or(default.to_string())
}
pub fn require(&self, name: &str) -> Result<String, ConfigError> {
self.get(name)
.ok_or_else(|| ConfigError::Required(name.to_string()))
}
}
pub trait ConfigSource {
fn collect(&self) -> Result<HashMap<String, String>, ConfigError>;
}
#[cfg(test)]
mod tests {
use super::*;
struct TestSource {
params: HashMap<String, String>,
}
impl<const N: usize> From<[(&str, &str); N]> for TestSource {
fn from(arr: [(&str, &str); N]) -> Self {
TestSource {
params: arr
.into_iter()
.map(|(k, v)| (k.to_string(), v.to_string()))
.collect(),
}
}
}
impl ConfigSource for TestSource {
fn collect(&self) -> Result<HashMap<String, String>, ConfigError> {
Ok(self.params.clone())
}
}
#[test]
fn test_config_constructor() {
let config = ConfigParameters::build(&[("pika", "chu")]);
assert_eq!(
ConfigParameters {
parameters: [("pika".to_string(), "chu".to_string())]
.into_iter()
.collect()
},
config
);
}
#[test]
fn test_config_set() {
let mut config = ConfigParameters::default();
config.add_parameter("pika", "chu");
assert_eq!(
ConfigParameters {
parameters: [("pika".to_string(), "chu".to_string())]
.into_iter()
.collect()
},
config
);
}
#[test]
fn test_config_get() {
let mut config = ConfigParameters::default();
config.add_parameter("pika", "chu");
assert_eq!("chu".to_string(), config.get("pika").unwrap());
assert!(config.get("whatever").is_none());
}
#[test]
fn test_config_default() {
let mut config = ConfigParameters::default();
config.add_parameter("pika", "chu");
assert_eq!("chu".to_string(), config.get("pika").unwrap());
assert_eq!("default".to_string(), config.get_or("whatever", "default"));
}
#[test]
fn test_config_require() {
let mut config = ConfigParameters::default();
config.add_parameter("pika", "chu");
assert_eq!("chu".to_string(), config.require("pika").unwrap());
config.require("whatever").unwrap_err();
}
#[test]
fn test_add_source_to_config() {
let config = ConfigParameters::build(&[("pika", "chu"), ("chari", "zard")])
.add_source(&TestSource::from([("jiggly", "puff")]))
.unwrap();
assert_eq!(
ConfigParameters {
parameters: HashMap::from([
("pika".to_string(), "chu".to_string()),
("chari".to_string(), "zard".to_string()),
("jiggly".to_string(), "puff".to_string())
])
},
config
);
}
#[test]
fn test_add_source_replace_existing_value() {
let config = ConfigParameters::build(&[("pika", "pika")])
.add_source(&TestSource::from([("pika", "not chu")]))
.unwrap();
assert_eq!(
ConfigParameters {
parameters: HashMap::from([("pika".to_string(), "not chu".to_string()),])
},
config
);
}
}