trs-data-value 0.2.0

Data Value for common data types
Documentation
use super::{DataValue, Extract};

macro_rules! impl_extract_nums {
    ($($t:ty),*) => {
        $(
        impl Extract for $t {
            #[inline]
            fn extract(v: &DataValue) -> $t {
                use DataValue::*;
                match v {
                    Bool(b) => (if b == &true { 1 } else { 0 })  as $t,
                    U32(i) => *i as $t,
                    I32(i) => *i as $t,
                    U8(i) => *i as $t,
                    U64(i) => *i as $t,
                    I64(i) => *i as $t,
                    F32(i) => *i as $t,
                    F64(i) => *i as $t,
                    U128(i) => *i as $t,
                    I128(i) => *i as $t,
                    EnumNumber(i) => *i as $t,
                    _ => 0 as $t,
                }
            }
        }
    )*
    };
}

impl_extract_nums!(u32, u64, i32, i64, f32, f64, u128, i128);
impl Extract for u8 {
    fn extract(v: &DataValue) -> u8 {
        use DataValue::*;
        match v {
            U8(i) => *i,
            _ => 0,
        }
    }
}

impl Extract for bool {
    fn extract(v: &DataValue) -> bool {
        match v {
            DataValue::Bool(i) => *i,
            DataValue::U8(i) => *i != 0,
            DataValue::U32(i) => *i != 0,
            DataValue::I32(i) => *i != 0,
            DataValue::U64(i) => *i != 0,
            DataValue::I64(i) => *i != 0,
            DataValue::F32(i) => *i != 0.,
            DataValue::F64(i) => *i != 0.,
            _ => false,
        }
    }
}

impl Extract for String {
    fn extract(v: &DataValue) -> String {
        match v {
            DataValue::String(i) => i.to_string(),
            v => format!("{v}"),
        }
    }
}

impl<T> Extract for Vec<T>
where
    T: Extract,
{
    fn extract(v: &DataValue) -> Vec<T> {
        use DataValue::*;
        match v {
            Vec(i) => i.iter().map(|x| T::extract(x)).collect(),
            _ => vec![],
        }
    }
}

impl<V> Extract for std::collections::HashMap<smartstring::alias::String, V>
where
    V: Extract,
{
    fn extract(v: &DataValue) -> std::collections::HashMap<smartstring::alias::String, V> {
        use DataValue::*;
        match v {
            Map(i) => i.iter().map(|(k, v)| (k.clone(), V::extract(v))).collect(),
            _ => Default::default(),
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use rstest::*;

    #[rstest]
    #[case(DataValue::U8(1), 1)]
    #[case(DataValue::U32(1), 1)]
    #[case(DataValue::I32(1), 1)]
    #[case(DataValue::U64(1), 1)]
    #[case(DataValue::I64(1), 1)]
    #[case(DataValue::F32(1.0), 1)]
    #[case(DataValue::F64(1.0), 1)]
    #[case(DataValue::U128(1), 1)]
    #[case(DataValue::I128(1), 1)]
    #[case(DataValue::EnumNumber(1), 1)]
    #[case(DataValue::Bool(true), 1)]
    #[case(DataValue::Bool(false), 0)]
    #[case(DataValue::String("1".into()), 0)]
    fn test_extract_num(#[case] input: DataValue, #[case] expected: u32) {
        assert_eq!(u32::extract(&input), expected);
    }

    #[rstest]
    #[case(DataValue::U8(1), "1")]
    #[case(DataValue::U32(1), "1")]
    #[case(DataValue::I32(1), "1")]
    #[case(DataValue::U64(1), "1")]
    #[case(DataValue::I64(1), "1")]
    #[case(DataValue::F32(1.1), "1.1")]
    #[case(DataValue::F64(1.1), "1.1")]
    #[case(DataValue::U128(1), "1")]
    #[case(DataValue::I128(1), "1")]
    #[case(DataValue::EnumNumber(1), "1")]
    #[case(DataValue::Bool(true), "true")]
    #[case(DataValue::Bool(false), "false")]
    #[case(DataValue::String("1".into()), "1")]
    fn test_extract_string(#[case] input: DataValue, #[case] expected: &str) {
        assert_eq!(String::extract(&input), expected.to_string());
    }
    #[rstest]
    #[case(DataValue::U8(1), 1)]
    #[case(DataValue::U32(1), 1)]
    #[case(DataValue::I32(1), 1)]
    #[case(DataValue::U64(1), 1)]
    #[case(DataValue::I64(1), 1)]
    #[case(DataValue::F32(1.0), 1)]
    #[case(DataValue::F64(1.0), 1)]
    #[case(DataValue::U128(1), 1)]
    #[case(DataValue::I128(1), 1)]
    #[case(DataValue::EnumNumber(1), 1)]
    #[case(DataValue::Bool(true), 1)]
    #[case(DataValue::Bool(false), 0)]
    #[case(DataValue::String("1".into()), 0)]
    fn test_extract_u64(#[case] input: DataValue, #[case] expected: u64) {
        assert_eq!(u64::extract(&input), expected);
    }

    #[rstest]
    #[case(DataValue::U8(1), 1)]
    #[case(DataValue::U32(1), 1)]
    #[case(DataValue::I32(1), 1)]
    #[case(DataValue::U64(1), 1)]
    #[case(DataValue::I64(1), 1)]
    #[case(DataValue::F32(1.0), 1)]
    #[case(DataValue::F64(1.0), 1)]
    #[case(DataValue::U128(1), 1)]
    #[case(DataValue::I128(1), 1)]
    #[case(DataValue::EnumNumber(1), 1)]
    #[case(DataValue::Bool(true), 1)]
    #[case(DataValue::Bool(false), 0)]
    #[case(DataValue::String("1".into()), 0)]
    fn test_extract_i32(#[case] input: DataValue, #[case] expected: i32) {
        assert_eq!(i32::extract(&input), expected);
    }

    #[rstest]
    #[case(DataValue::U8(1), 1)]
    #[case(DataValue::U32(1), 1)]
    #[case(DataValue::I32(1), 1)]
    #[case(DataValue::U64(1), 1)]
    #[case(DataValue::I64(1), 1)]
    #[case(DataValue::F32(1.0), 1)]
    #[case(DataValue::F64(1.0), 1)]
    #[case(DataValue::U128(1), 1)]
    #[case(DataValue::I128(1), 1)]
    #[case(DataValue::EnumNumber(1), 1)]
    #[case(DataValue::Bool(true), 1)]
    #[case(DataValue::Bool(false), 0)]
    #[case(DataValue::String("1".into()), 0)]
    fn test_extract_i64(#[case] input: DataValue, #[case] expected: i64) {
        assert_eq!(i64::extract(&input), expected);
    }

    #[rstest]
    #[case(DataValue::U8(1), 1f64)]
    #[case(DataValue::U32(1), 1f64)]
    #[case(DataValue::I32(1), 1f64)]
    #[case(DataValue::U64(1), 1f64)]
    #[case(DataValue::I64(1), 1f64)]
    #[case(DataValue::F32(1.0), 1f64)]
    #[case(DataValue::F64(1.0), 1f64)]
    #[case(DataValue::U128(1), 1f64)]
    #[case(DataValue::I128(1), 1f64)]
    #[case(DataValue::EnumNumber(1), 1f64)]
    #[case(DataValue::Bool(true), 1f64)]
    #[case(DataValue::Bool(false), 0f64)]
    #[case(DataValue::String("1".into()), 0f64)]
    fn test_extract_f64(#[case] input: DataValue, #[case] expected: f64) {
        assert_eq!(f64::extract(&input), expected);
    }

    #[rstest]
    #[case(DataValue::U8(1), 1f32)]
    #[case(DataValue::U32(1), 1f32)]
    #[case(DataValue::I32(1), 1f32)]
    #[case(DataValue::U64(1), 1f32)]
    #[case(DataValue::I64(1), 1f32)]
    #[case(DataValue::F32(1.0), 1f32)]
    #[case(DataValue::F64(1.0), 1f32)]
    #[case(DataValue::U128(1), 1f32)]
    #[case(DataValue::I128(1), 1f32)]
    #[case(DataValue::EnumNumber(1), 1f32)]
    #[case(DataValue::Bool(true), 1f32)]
    #[case(DataValue::Bool(false), 0f32)]
    #[case(DataValue::String("1".into()), 0f32)]
    #[case(DataValue::Null, 0f32)]
    fn test_extract_f32(#[case] input: DataValue, #[case] expected: f32) {
        assert_eq!(f32::extract(&input), expected);
    }

    #[rstest]
    #[case(DataValue::Vec(vec![1.into()]), vec![1])]
    #[case(DataValue::Vec(vec![1.into(), 2.into()]), vec![1, 2])]
    #[case(DataValue::Vec(vec![1.into(), 2.into(), 3.into()]), vec![1, 2, 3])]
    fn test_extract_vec(#[case] input: DataValue, #[case] expected: Vec<u32>) {
        assert_eq!(Vec::<u32>::extract(&input), expected);
    }

    #[rstest]
    #[case(DataValue::Map(crate::stdhashmap!("a" => 1)), crate::stdhashmap!("a" => 1))]
    #[case(DataValue::Map(crate::stdhashmap!("a" => 1, "b" => 2)), crate::stdhashmap!("a" => 1, "b" => 2))]
    #[case(DataValue::Map(crate::stdhashmap!("a" => 1, "b" => 2, "c" => 3)), crate::stdhashmap!("a" => 1, "b" => 2, "c" => 3))]
    fn test_extract_map(
        #[case] input: DataValue,
        #[case] expected: std::collections::HashMap<smartstring::alias::String, i32>,
    ) {
        assert_eq!(
            std::collections::HashMap::<smartstring::alias::String, i32>::extract(&input),
            expected
        );
    }
}