use std::error::Error;
mod helpers;
mod key;
mod value;
pub mod sources;
pub use key::{Key, KeyBuf};
pub use value::{TryFromValue, Value};
pub trait Source {
type Error;
fn get_value(&self, key: &Key) -> Result<Option<Value>, Self::Error>;
}
struct BoxedErrorSource<S> {
contained: S,
}
impl<S, E> From<S> for BoxedErrorSource<S>
where
E: Error + 'static,
S: Source<Error = E>,
{
fn from(source: S) -> Self {
BoxedErrorSource { contained: source }
}
}
impl<S, E> Source for BoxedErrorSource<S>
where
E: Error + 'static,
S: Source<Error = E>,
{
type Error = Box<dyn Error>;
fn get_value(&self, key: &Key) -> Result<Option<Value>, Self::Error> {
self.contained
.get_value(key)
.map_err(|e| Box::new(e) as Box<dyn Error>)
}
}
#[derive(Debug)]
pub enum ValueReadError {
SourceReadError(Box<dyn Error>),
ValueConversionError(Box<dyn Error>),
ValueNotPresent,
}
impl Error for ValueReadError {}
impl std::fmt::Display for ValueReadError {
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ValueReadError::SourceReadError(e) => {
write!(
fmt,
"Error occurred while reading from configuration source: "
)?;
e.fmt(fmt)
}
ValueReadError::ValueConversionError(e) => {
write!(fmt, "Value failed to be converted to requested type: ")?;
e.fmt(fmt)
}
ValueReadError::ValueNotPresent => write!(fmt, "Requested value did not exist"),
}
}
}
pub trait ValueRead: Sized {
fn get_value<T, K>(&self, key: K) -> Result<Option<T>, ValueReadError>
where
K: AsRef<Key>,
T: TryFromValue;
fn get_expected_value<T, K>(&self, key: K) -> Result<T, ValueReadError>
where
K: AsRef<Key>,
T: TryFromValue,
{
self.get_value(key)?.ok_or(ValueReadError::ValueNotPresent)
}
fn bind<T: TryFromValueRead>(&self) -> Result<T, ValueReadError> {
<T as TryFromValueRead>::try_from(self)
}
}
pub trait TryFromValueRead: Sized {
fn try_from<R: ValueRead>(value_read: &R) -> Result<Self, ValueReadError>;
}
pub struct Section<'a> {
key: KeyBuf,
config: &'a Config,
}
impl<'a> Section<'a> {
pub fn section<K: AsRef<Key>>(&self, key: K) -> Section {
self.section(key.as_ref())
}
fn _section(&self, key: &Key) -> Section {
Section {
key: self.key.extend_with_suffix(key),
config: self.config
}
}
}
impl<'a> ValueRead for Section<'a> {
fn get_value<T, K>(&self, key: K) -> Result<Option<T>, ValueReadError>
where
K: AsRef<Key>,
T: TryFromValue,
{
let mut new_key = self.key.to_key_buf();
new_key.push(key.as_ref());
self.config.get_value(&new_key)
}
}
pub struct Config {
sources: Vec<Box<dyn Source<Error = Box<dyn Error>>>>,
}
impl Config {
pub fn section<K: AsRef<Key>>(&self, key: K) -> Section {
Section {
key: key.as_ref().to_owned(),
config: self,
}
}
fn _get_value(&self, key: &Key) -> Result<Option<Value>, ValueReadError> {
if self.sources.len() == 0 {
return Ok(None);
}
self.sources
.iter()
.map(|s| s.get_value(key))
.skip_while(|v| match v {
Ok(None) => true,
_ => false,
})
.next()
.unwrap_or(Ok(None))
.map_err(|e| ValueReadError::SourceReadError(e))
}
}
impl ValueRead for Config {
fn get_value<T, K>(&self, key: K) -> Result<Option<T>, ValueReadError>
where
K: AsRef<Key>,
T: TryFromValue,
{
self._get_value(key.as_ref())?
.map(|v| T::try_from_value(v))
.transpose()
.map_err(|e| ValueReadError::ValueConversionError(Box::new(e)))
}
}
type BoxedDynamicSource = Box<dyn Source<Error = Box<dyn Error>>>;
pub struct Builder {
sources: Vec<BoxedDynamicSource>,
}
impl Builder {
pub fn new() -> Self {
Self {
sources: Vec::new(),
}
}
pub fn add_source<S, E>(mut self, source: S) -> Self
where
E: Error + 'static,
S: Source<Error = E> + 'static,
{
self.sources.push(Box::new(BoxedErrorSource::from(source)));
self
}
pub fn add_source_at_prefix<S, E, K>(self, source: S, prefix: K) -> Self
where
E: Error + 'static,
S: Source<Error = E> + 'static,
K: AsRef<Key>
{
self.add_source(WithPrefixSource {
prefix: prefix.as_ref().to_owned(),
source
})
}
pub fn build(self) -> Config {
Config {
sources: self.sources,
}
}
}
pub struct WithPrefixSource<S: Source> {
prefix: KeyBuf,
source: S,
}
impl<S: Source> Source for WithPrefixSource<S> {
type Error = S::Error;
fn get_value(&self, key: &Key) -> Result<Option<Value>, Self::Error> {
if !key.start_with(key) {
return Ok(None);
}
self.source.get_value(&key.strip_prefix(&self.prefix))
}
}