use std::convert::{TryFrom};
use std::error::Error;
use std::fmt::{Display, Formatter, Result as FmtResult};
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);