use crate::confpath::ConfPath;
use crate::error::ConfigError;
use std::str::FromStr;
use std::rc::Rc;
use std::convert::TryInto;
use std::error::Error;
use std::ops::RangeBounds;
pub trait SourceLocation : std::fmt::Display + std::fmt::Debug {}
pub struct Value<T> {
value: T,
source: Rc<dyn SourceLocation>
}
impl <T> Value<T> {
pub fn new(value: T, source: Rc<dyn SourceLocation>) -> Rc<Self> {
Rc::new(Self {
value,
source
})
}
pub fn source(&self) -> Rc<dyn SourceLocation>{
self.source.clone()
}
}
#[derive(Clone)]
struct Item<T> {
key: ConfPath,
values: Vec<Rc<Value<T>>>
}
#[derive(Clone)]
pub struct StringItem(Item<String>);
impl StringItem {
pub(crate) fn new(key: ConfPath) -> Self {
Self (Item {
key,
values: Vec::with_capacity(1)
})
}
pub(crate) fn from(key: ConfPath, values: &[Rc<Value<String>>]) -> Self {
Self (Item {
key,
values: Vec::from(values)
})
}
pub(crate) fn push(&mut self, new_value: Rc<Value<String>>) {
self.0.values.push(new_value);
}
pub(crate) fn clear(&mut self) -> &mut Self {
self.0.values.clear();
self
}
}
#[derive(Clone)]
pub struct TypedItem<T: FromStr>(Item<T>);
impl <T: FromStr> TypedItem<T> {
pub(crate) fn new(key: ConfPath, values: Vec<Rc<Value<T>>>) -> Self {
Self(Item {
key,
values
})
}
}
impl <T: FromStr> TypedItem<T> {
pub fn filter(self, filter: impl Fn(&T) -> Result<(), Box<dyn Error>>) -> Result<Self, ConfigError> {
for v in self.0.values.iter() {
filter(&v.value).map_err(|e| ConfigError::ValueError(e, v.source.clone()))?;
}
Ok(self)
}
}
pub enum MapAction {
Keep,
Replace(Vec<String>),
Drop,
Fail(Box<dyn Error>)
}
impl StringItem {
pub fn map(self, mapper: impl Fn(&String) -> MapAction) -> Result<Self, ConfigError> {
let mut mapped_item = StringItem::new(self.0.key);
for v in self.0.values.into_iter() {
match mapper(&v.value) {
MapAction::Keep => mapped_item.push(v),
MapAction::Replace(new_values_list) => for value in new_values_list.into_iter().map(|mapped_v| Value::new(mapped_v, v.source.clone())) { mapped_item.push(value); },
MapAction::Drop => (),
MapAction::Fail(error) => return Err(ConfigError::ValueError(error, v.source.clone()))
}
}
Ok(mapped_item)
}
}
impl <T: FromStr> TryInto<TypedItem<T>> for Result<StringItem, ConfigError> where T::Err: Error + 'static {
type Error = ConfigError;
fn try_into(self) -> Result<TypedItem<T>, ConfigError> {
let s = self?;
let typed_values: Result<Vec<Rc<Value<T>>>, ConfigError> = s.0.values.into_iter().map(|v|
v.value.parse::<T>().map(|nv|
Value::new(nv, v.source.clone())
)
.map_err(|e| ConfigError::from_error(e, v.source.clone()))
)
.collect();
Ok(TypedItem::new(s.0.key, typed_values?))
}
}
pub trait ValueExtractor<T: FromStr> {
fn try_value(self) -> Result<Option<T>, ConfigError>;
fn value(self) -> Result<T, ConfigError>;
fn values<R: RangeBounds<usize>>(self, range: R) -> Result<Vec<T>, ConfigError>;
}
#[allow(clippy::unnecessary_unwrap)] fn values_out_of_range<T: FromStr, R: RangeBounds<usize>>(mut item: TypedItem<T>, range: R) -> Result<Vec<T>, ConfigError> {
let num_items = item.0.values.len();
if range.contains(&num_items) {
item.0.values.drain(..).map(|r| Rc::try_unwrap(r).map(|v| v.value).map_err(|_| ConfigError::MultipleReferences)).collect()
} else {
let lower_limit_inc = match range.start_bound() {
std::ops::Bound::Included(min) => Some(*min),
std::ops::Bound::Excluded(min) => Some(*min + 1),
std::ops::Bound::Unbounded => None
};
let upper_limit_excl = match range.end_bound() {
std::ops::Bound::Included(max) => Some(*max + 1),
std::ops::Bound::Excluded(max) => Some(*max),
std::ops::Bound::Unbounded => None
};
if lower_limit_inc.is_some() && (num_items < lower_limit_inc.unwrap()) {
Err(ConfigError::NotEnoughValues(lower_limit_inc.unwrap(), item.0.key))
} else if upper_limit_excl.is_some() && (num_items >= upper_limit_excl.unwrap()) {
let first_surplus_index = upper_limit_excl.unwrap().saturating_sub(1);
let surplus_sources = item.0.values.drain(first_surplus_index..).map(|r| Rc::try_unwrap(r).map(|v| v.source).map_err(|_| ConfigError::MultipleReferences)).collect::<Result<Vec<Rc<dyn SourceLocation>>, ConfigError>>()?;
Err(ConfigError::TooManyValues(first_surplus_index, item.0.key, surplus_sources))
} else {
unreachable!("This is not possible because we checked that num_items is not contained in range.");
}
}
}
impl <T: FromStr> ValueExtractor<T> for Result<TypedItem<T>, ConfigError> {
fn try_value(self) -> Result<Option<T>, ConfigError> {
match self.value() {
Ok(value) => Ok(Some(value)),
Err(ConfigError::ValueNotFound(_)) => Ok(None),
Err(error) => Err(error)
}
}
fn value(self) -> Result<T, ConfigError> {
let mut ci = self?.0;
match ci.values.len() {
0 => Err(ConfigError::ValueNotFound(ci.key)),
1 => Rc::try_unwrap(ci.values.pop().unwrap()).map(|v| v.value).map_err(|_| ConfigError::MultipleReferences),
_ => Err(ConfigError::TooManyValues(1, ci.key, ci.values.iter().map(|v| v.source()).collect()))
}
}
fn values<R: RangeBounds<usize>>(self, range: R) -> Result<Vec<T>, ConfigError> {
match self {
Ok(item) => values_out_of_range(item, range),
Err(ConfigError::ValueNotFound(key)) => values_out_of_range(TypedItem::<T>::new(key, Vec::default()), range), Err(error) => Err(error)
}
}
}
impl <T: FromStr> ValueExtractor<T> for Result<StringItem, ConfigError> where T::Err: Error + 'static {
fn try_value(self) -> Result<Option<T>, ConfigError> {
(self.try_into() as Result<TypedItem<T>, ConfigError>).try_value()
}
fn value(self) -> Result<T, ConfigError> {
(self.try_into() as Result<TypedItem<T>, ConfigError>).value()
}
fn values<R: RangeBounds<usize>>(self, range: R) -> Result<Vec<T>, ConfigError> {
(self.try_into() as Result<TypedItem<T>, ConfigError>).values(range)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Config;
use crate::sources::defaults::{Defaults};
use crate::error::ConfigError;
fn prepare_test_config() -> Config {
let mut c = Config::default();
let mut defaults = Defaults::default();
defaults.empty(c.root().push_all(&["no_value"]));
defaults.set(c.root().push_all(&["one_value"]), "one_value", "1.1");
defaults.put(c.root().push_all(&["two_values"]), "two_values", "2.1");
defaults.put(c.root().push_all(&["two_values"]), "two_values", "2.2");
c.add_source(defaults);
c
}
#[test]
fn value_no_value() {
let c = prepare_test_config();
assert_eq!(format!("{}", (c.get(c.root().push_all(&["no_value"])).value() as Result<String, ConfigError>).unwrap_err()), "Missing value for config key 'no_value'.");
}
#[test]
fn value_one_value() {
let c = prepare_test_config();
assert_eq!((c.get(c.root().push_all(&["one_value"])).value() as Result<String, ConfigError>).unwrap(), "one_value");
}
#[test]
fn value_two_values() {
let c = prepare_test_config();
assert_eq!(format!("{}", (c.get(c.root().push_all(&["two_values"])).value() as Result<String, ConfigError>).unwrap_err()), "More than 1 value found for key two_values@['default from 2.1', 'default from 2.2']");
}
#[test]
fn try_value_no_value() {
let c = prepare_test_config();
assert!((c.get(c.root().push_all(&["no_value"])).try_value() as Result<Option<String>, ConfigError>).unwrap().is_none());
}
#[test]
fn try_value_one_value() {
let c = prepare_test_config();
assert_eq!((c.get(c.root().push_all(&["one_value"])).try_value() as Result<Option<String>, ConfigError>).unwrap().unwrap(), "one_value");
}
#[test]
fn try_value_two_values() {
let c = prepare_test_config();
assert_eq!(format!("{}", (c.get(c.root().push_all(&["two_values"])).try_value() as Result<Option<String>, ConfigError>).unwrap_err()), "More than 1 value found for key two_values@['default from 2.1', 'default from 2.2']");
}
#[test]
fn values_no_value() {
let c = prepare_test_config();
let values: Vec<String> = c.get(c.root().push_all(&["no_value"])).values(..).unwrap();
assert_eq!(values.len(), 0);
}
#[test]
fn values_one_value() {
let c = prepare_test_config();
let mut values: Vec<String> = c.get(c.root().push_all(&["one_value"])).values(..).unwrap();
assert_eq!(values.len(), 1);
assert_eq!(values.pop().unwrap(), "one_value");
}
#[test]
fn values_two_values() {
let c = prepare_test_config();
let mut values: Vec<String> = c.get(c.root().push_all(&["two_values"])).values(..).unwrap();
assert_eq!(values.len(), 2);
assert_eq!(values.pop().unwrap(), "two_values");
assert_eq!(values.pop().unwrap(), "two_values");
}
#[test]
fn range_lower_limit() {
let c = prepare_test_config();
assert_eq!(format!("{}", (c.get(c.root().push_all(&["two_values"])).values(3..) as Result<Vec<String>, ConfigError>).unwrap_err()), "Key \'two_values\' must have at least 3 values.");
assert_eq!(format!("{}", (c.get(c.root().push_all(&["two_values"])).values(4..5) as Result<Vec<String>, ConfigError>).unwrap_err()), "Key \'two_values\' must have at least 4 values.");
assert_eq!(format!("{}", (c.get(c.root().push_all(&["no_value"])).values(1..) as Result<Vec<String>, ConfigError>).unwrap_err()), "Key \'no_value\' must have at least 1 values.");
assert_eq!(format!("{}", (c.get(c.root().push_all(&["unkown_key"])).values(1..) as Result<Vec<String>, ConfigError>).unwrap_err()), "Key \'unkown_key\' must have at least 1 values.");
}
#[test]
fn range_upper_limit() {
let c = prepare_test_config();
assert_eq!(format!("{}", (c.get(c.root().push_all(&["two_values"])).values(..=1) as Result<Vec<String>, ConfigError>).unwrap_err()), "More than 1 value found for key two_values@['default from 2.2']");
assert_eq!(format!("{}", (c.get(c.root().push_all(&["two_values"])).values(1..=1) as Result<Vec<String>, ConfigError>).unwrap_err()), "More than 1 value found for key two_values@['default from 2.2']");
}
#[test]
fn range_ok() {
let c = prepare_test_config();
let mut values: Vec<String> = c.get(c.root().push_all(&["two_values"])).values(1..=2).unwrap();
assert_eq!(values.len(), 2);
assert_eq!(values.pop().unwrap(), "two_values");
assert_eq!(values.pop().unwrap(), "two_values");
let mut values: Vec<String> = c.get(c.root().push_all(&["two_values"])).values(2..3).unwrap();
assert_eq!(values.len(), 2);
assert_eq!(values.pop().unwrap(), "two_values");
assert_eq!(values.pop().unwrap(), "two_values");
let mut values: Vec<String> = c.get(c.root().push_all(&["two_values"])).values(0..10).unwrap();
assert_eq!(values.len(), 2);
assert_eq!(values.pop().unwrap(), "two_values");
assert_eq!(values.pop().unwrap(), "two_values");
let mut values: Vec<String> = c.get(c.root().push_all(&["one_value"])).values(..3).unwrap();
assert_eq!(values.len(), 1);
assert_eq!(values.pop().unwrap(), "one_value");
let mut values: Vec<String> = c.get(c.root().push_all(&["one_value"])).values(1..).unwrap();
assert_eq!(values.len(), 1);
assert_eq!(values.pop().unwrap(), "one_value");
let values: Vec<String> = c.get(c.root().push_all(&["unkown_key"])).values(..).unwrap();
assert_eq!(values.len(), 0);
let values: Vec<String> = c.get(c.root().push_all(&["unkown_key"])).values(..1).unwrap();
assert_eq!(values.len(), 0);
let values: Vec<String> = c.get(c.root().push_all(&["unkown_key"])).values(..=1).unwrap();
assert_eq!(values.len(), 0);
let values: Vec<String> = c.get(c.root().push_all(&["unkown_key"])).values(..=0).unwrap();
assert_eq!(values.len(), 0);
}
}