use crate::prelude::*;
use paste::paste;
#[derive(Debug)]
pub(crate) enum Value {
Boolean(bool),
Integer(i64),
Float(f64),
String(String),
StringVec(Vec<String>),
}
pub(crate) trait ConstantDefault: 'static {
fn constant_default() -> &'static Self;
}
macro_rules! impl_value_traits {
($($target:ty => $variant:ident / $default:expr),*) => {
$(
impl<'a> TryFrom<&'a Value> for &'a $target {
type Error = Error;
fn try_from(value: &'a Value) -> std::result::Result<Self, Self::Error> {
if let Value::$variant(v) = value {
Ok(v)
} else {
Err(anyhow!("Cannot cast {value:?} to {}.", stringify!($target)))
}
}
}
paste! {
static [<CONSTANT_DEFAULT_ $target:upper>]: $target = $default;
impl ConstantDefault for $target {
fn constant_default() -> &'static Self
{
&[<CONSTANT_DEFAULT_ $target:upper>]
}
}
}
)*
};
}
type VecString = Vec<String>;
static CONSTANT_DEFAULT_BOOL: bool = false;
impl<'a> TryFrom<&'a Value> for &'a bool {
type Error = Error;
fn try_from(value: &'a Value) -> std::result::Result<Self, Self::Error> {
if let Value::Boolean(v) = value {
Ok(v)
} else {
Err(anyhow!("Cannot cast {value:?} to bool."))
}
}
}
impl ConstantDefault for bool {
fn constant_default() -> &'static Self {
&CONSTANT_DEFAULT_BOOL
}
}
impl_value_traits!(
i64 => Integer / 0,
f64 => Float / 0.0,
String => String / String::new(),
VecString => StringVec / Vec::new()
);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn try_from_bool_success() {
let val = Value::Boolean(true);
let result: Result<&bool> = (&val).try_into();
assert_eq!(result.unwrap(), &true);
}
#[test]
fn try_from_bool_failure() {
let val = Value::Integer(42);
let result: Result<&bool> = (&val).try_into();
assert!(result.is_err());
}
#[test]
fn try_from_i64_success() {
let val = Value::Integer(42);
let result: Result<&i64> = (&val).try_into();
assert_eq!(result.unwrap(), &42);
}
#[test]
fn try_from_i64_failure() {
let val = Value::Float(std::f64::consts::PI);
let result: Result<&i64> = (&val).try_into();
assert!(result.is_err());
}
#[test]
fn try_from_f64_success() {
let val = Value::Float(std::f64::consts::PI);
let result: Result<&f64> = (&val).try_into();
assert_eq!(result.unwrap(), &std::f64::consts::PI);
}
#[test]
fn try_from_f64_failure() {
let val = Value::String("hello".to_string());
let result: Result<&f64> = (&val).try_into();
assert!(result.is_err());
}
#[test]
fn try_from_string_success() {
let val = Value::String("hello".to_string());
let result: Result<&String> = (&val).try_into();
assert_eq!(result.unwrap(), "hello");
}
#[test]
fn try_from_string_failure() {
let val = Value::Boolean(false);
let result: Result<&String> = (&val).try_into();
assert!(result.is_err());
}
#[test]
fn try_from_vec_string_success() {
let val = Value::StringVec(vec!["a".to_string(), "b".to_string()]);
let result: Result<&Vec<String>> = (&val).try_into();
assert!(result.is_ok());
let vec_ref = result.unwrap();
assert_eq!(vec_ref.len(), 2);
assert_eq!(vec_ref[0], "a");
}
#[test]
fn try_from_vec_string_failure() {
let val = Value::Integer(123);
let result: Result<&Vec<String>> = (&val).try_into();
assert!(result.is_err());
}
#[test]
fn constant_default_bool() {
assert!(!*bool::constant_default());
}
#[test]
fn constant_default_i64() {
assert_eq!(*i64::constant_default(), 0);
}
#[test]
fn constant_default_f64() {
assert_eq!(*f64::constant_default(), 0.0);
}
#[test]
fn constant_default_string() {
assert_eq!(*String::constant_default(), "");
}
#[test]
fn constant_default_vec_string() {
assert!(Vec::<String>::constant_default().is_empty());
}
}