manaconf 0.2.0

a layered configuration library
Documentation
use std::convert::{TryFrom};
use std::error::Error;
use std::fmt::{Display, Formatter, Result as FmtResult};

/// Holds a value read from a configuration source
pub enum Value {
    String(String),
    Int(i32),
    Long(i64),
    Uint(u32),
    Ulong(u64),
    Float(f32),
    Double(f64),
    Boolean(bool),
}

macro_rules! sub_try_from_value_errors {
    ($err:ty => panic($s:expr)) => {
        impl From<$err> for TryFromValueError {
            fn from(_: $err) -> Self {
                panic!($s)
            }
        }
    };
    ($err:ty => $val:ident) => {
        impl From<$err> for TryFromValueError {
            fn from(value: $err) -> Self {
                TryFromValueError::$val(value)
            }
        }
    };
}

#[derive(Debug)]
pub enum TryFromValueError {
    UnsupportedConversion,
    ParseFloatError(std::num::ParseFloatError),
    ParseIntError(std::num::ParseIntError),
    ParseBoolError(std::str::ParseBoolError),
    TryFromIntError(std::num::TryFromIntError),
    Other(Box<dyn Error>),
}

sub_try_from_value_errors!(std::num::ParseFloatError => ParseFloatError);
sub_try_from_value_errors!(std::num::ParseIntError => ParseIntError);
sub_try_from_value_errors!(std::str::ParseBoolError => ParseBoolError);
sub_try_from_value_errors!(std::num::TryFromIntError => TryFromIntError);
sub_try_from_value_errors!(std::convert::Infallible => panic("Attempted conversion of infallible error"));

pub trait TryFromValue: Sized {
    fn try_from_value(value: Value) -> Result<Self, TryFromValueError>;
}

impl Error for TryFromValueError {}
impl Display for TryFromValueError {
    fn fmt(&self, fmt: &mut Formatter<'_>) -> FmtResult {
        match self {
            TryFromValueError::UnsupportedConversion => {
                write!(fmt, "Can't convert to requested type")
            }
            TryFromValueError::ParseFloatError(e) => {
                write!(fmt, "Failed to parse value as float: {}", e)
            }
            TryFromValueError::ParseIntError(e) => {
                write!(fmt, "Failed to parse value as an integer: {}", e)
            }
            TryFromValueError::ParseBoolError(e) => {
                write!(fmt, "Failed to parse value as a boolean: {}", e)
            }
            TryFromValueError::TryFromIntError(e) => {
                write!(fmt, "Failed to convert from integer: {}", e)
            }
            TryFromValueError::Other(e) => write!(fmt, "Failed to convert Value: {}", e),
        }
    }
}

impl TryFromValue for Value {
    fn try_from_value(value: Value) -> Result<Self, TryFromValueError> {
        Ok(value)
    }
}

impl TryFromValue for String {
    fn try_from_value(value: Value) -> Result<Self, TryFromValueError> {
        match value {
            Value::String(v) => Ok(v),
            Value::Int(v) => Ok(v.to_string()),
            Value::Long(v) => Ok(v.to_string()),
            Value::Uint(v) => Ok(v.to_string()),
            Value::Ulong(v) => Ok(v.to_string()),
            Value::Float(v) => Ok(v.to_string()),
            Value::Double(v) => Ok(v.to_string()),
            Value::Boolean(v) => Ok(v.to_string()),
        }
    }
}

impl TryFromValue for bool {
    fn try_from_value(value: Value) -> Result<Self, TryFromValueError> {
        match value {
            Value::String(v) => Ok(v.parse()?),
            Value::Int(v) => Ok(v != 0),
            Value::Long(v) => Ok(v != 0),
            Value::Uint(v) => Ok(v != 0),
            Value::Ulong(v) => Ok(v != 0),
            Value::Float(v) => Ok(v != 0.0),
            Value::Double(v) => Ok(v != 0.0),
            Value::Boolean(v) => Ok(v),
        }
    }
}

macro_rules! try_from_value_int {
    ($int_type:ty) => {
        impl TryFromValue for $int_type {
            fn try_from_value(value: Value) -> Result<Self, TryFromValueError> {
                match value {
                    Value::String(v) => Ok(v.parse()?),
                    Value::Int(v) => Ok(<$int_type>::try_from(v)?),
                    Value::Long(v) => Ok(<$int_type>::try_from(v)?),
                    Value::Uint(v) => Ok(<$int_type>::try_from(v)?),
                    Value::Ulong(v) => Ok(<$int_type>::try_from(v)?),
                    Value::Float(v) => Ok(v as $int_type),
                    Value::Double(v) => Ok(v as $int_type),
                    Value::Boolean(_) => Err(TryFromValueError::UnsupportedConversion),
                }
            }
        }
    };
}

try_from_value_int!(u8);
try_from_value_int!(u16);
try_from_value_int!(u32);
try_from_value_int!(u64);
try_from_value_int!(i8);
try_from_value_int!(i16);
try_from_value_int!(i32);
try_from_value_int!(i64);

macro_rules! try_from_value_float {
    ($float_type:ty) => {
        impl TryFromValue for $float_type {
            fn try_from_value(value: Value) -> Result<Self, TryFromValueError> {
                match value {
                    Value::String(v) => Ok(v.parse()?),
                    Value::Int(v) => Ok(v as $float_type),
                    Value::Long(v) => Ok(v as $float_type),
                    Value::Uint(v) => Ok(v as $float_type),
                    Value::Ulong(v) => Ok(v as $float_type),
                    Value::Float(v) => Ok(v as $float_type),
                    Value::Double(v) => Ok(v as $float_type),
                    Value::Boolean(_) => Err(TryFromValueError::UnsupportedConversion),
                }
            }
        }
    };
}

try_from_value_float!(f32);
try_from_value_float!(f64);