use std::{borrow::Borrow, collections::HashMap, hash::Hash};
#[derive(Debug, Default, Clone)]
pub struct ConfigResolver<Key, ConfigValue> {
base: ConfigValue,
overrides: HashMap<Key, ConfigValue>,
}
impl<Key, ConfigValue> ConfigResolver<Key, ConfigValue> {
pub fn new(base: ConfigValue, overrides: HashMap<Key, ConfigValue>) -> Self {
Self { base, overrides }
}
pub fn get<Q, F, Out>(&self, key: &Q, field: F) -> Out
where
Key: Borrow<Q> + Eq + Hash,
Q: Hash + Eq + ?Sized,
F: FnOnce(&ConfigValue) -> Out,
{
match self.overrides.get(key) {
None => field(&self.base),
Some(set) => field(set),
}
}
pub fn get_opt<Q, F, Out>(&self, key: &Q, mut field: F) -> Option<Out>
where
Key: Borrow<Q> + Eq + Hash,
Q: Hash + Eq + ?Sized,
F: FnMut(&ConfigValue) -> Option<Out>,
{
match self.overrides.get(key) {
None => field(&self.base),
Some(set) => field(set).or_else(|| field(&self.base)),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use maplit::hashmap;
use pretty_assertions::assert_eq;
#[derive(Debug)]
struct TestStruct {
int_field: i32,
str_field: &'static str,
}
impl TestStruct {
fn str_opt(&self) -> Option<&'static str> {
if self.str_field.is_empty() {
None
} else {
Some(self.str_field)
}
}
}
fn input() -> ConfigResolver<&'static str, TestStruct> {
ConfigResolver::new(
TestStruct {
int_field: 1,
str_field: "base",
},
hashmap! {
"key1" => TestStruct {
int_field: 0,
str_field: "",
},
"key2" => TestStruct {
int_field: 2,
str_field: "non-empty",
},
},
)
}
#[test]
fn get_returns_base_value_if_no_overrides() {
assert_eq!(1, input().get("not_existing", |x| x.int_field));
}
#[test]
fn get_returns_override_value_if_key_exists() {
assert_eq!(2, input().get("key2", |x| x.int_field));
}
#[test]
fn get_opt_returns_base_value_if_no_overrides() {
assert_eq!(
Some("base"),
input().get_opt("not_existing", TestStruct::str_opt)
);
}
#[test]
fn get_opt_returns_base_value_if_overrides_returns_none() {
assert_eq!(Some("base"), input().get_opt("key1", TestStruct::str_opt));
}
#[test]
fn get_opt_returns_override_value() {
assert_eq!(
Some("non-empty"),
input().get_opt("key2", TestStruct::str_opt)
);
}
}