dom_finder 0.6.0

HTML parsing with CSS selectors
Documentation
use super::errors::ValueConversionError;
use super::value::{InnerMap, Value};

macro_rules! impl_value_extractors {
    ($variant:ident, $inner_type:ty, $as_method:ident, $to_method:ident) => {
        impl Value {
            #[inline]
            pub fn $as_method(&self) -> Option<&$inner_type> {
                if let Self::$variant(v) = self {
                    Some(v)
                } else {
                    None
                }
            }

            #[inline]
            pub fn $to_method(&self) -> Option<$inner_type> {
                if let Self::$variant(v) = self {
                    Some(v.clone())
                } else {
                    None
                }
            }
        }
        impl_try_from!($variant, $inner_type);
    };
}

macro_rules! impl_try_from {
    ($variant:ident, $inner_type:ty) => {
        impl TryFrom<Value> for $inner_type {
            type Error = ValueConversionError;
            fn try_from(value: Value) -> Result<Self, Self::Error> {
                if let Value::$variant(v) = value {
                    Ok(v)
                } else {
                    Err(ValueConversionError {
                        expected: stringify!($variant),
                    })
                }
            }
        }
        impl TryFrom<&Value> for $inner_type {
            type Error = ValueConversionError;
            fn try_from(value: &Value) -> Result<Self, Self::Error> {
                if let Value::$variant(v) = value {
                    Ok(v.clone())
                } else {
                    Err(ValueConversionError {
                        expected: stringify!($variant),
                    })
                }
            }
        }
    };
}
macro_rules! impl_try_from_to_vec {
    ($variant:ident, $rust_type:ty) => {
        impl TryFrom<Value> for Vec<$rust_type> {
            type Error = ValueConversionError;
            fn try_from(value: Value) -> Result<Self, Self::Error> {
                match value {
                    Value::Array(val) => val
                        .iter()
                        .map(|v| {
                            <$rust_type>::try_from(v).map_err(|_| ValueConversionError {
                                expected: concat!("Array<", stringify!($variant), ">"),
                            })
                        })
                        .collect(),
                    _ => Err(ValueConversionError {
                        expected: concat!("Array<", stringify!($variant), ">"),
                    }),
                }
            }
        }

        impl TryFrom<&Value> for Vec<$rust_type> {
            type Error = ValueConversionError;
            fn try_from(value: &Value) -> Result<Self, Self::Error> {
                match value {
                    Value::Array(val) => val
                        .iter()
                        .map(|v| {
                            <$rust_type>::try_from(v).map_err(|_| ValueConversionError {
                                expected: concat!("Array<", stringify!($variant), ">"),
                            })
                        })
                        .collect(),
                    _ => Err(ValueConversionError {
                        expected: concat!("Array<", stringify!($variant), ">"),
                    }),
                }
            }
        }
    };
}

impl_value_extractors!(Int, i64, as_i64, to_i64);
impl_value_extractors!(Float, f64, as_f64, to_f64);
impl_value_extractors!(Bool, bool, as_bool, to_bool);
impl_value_extractors!(String, String, as_string, to_string);
impl_value_extractors!(Array, Vec<Value>, as_array, to_array);
impl_value_extractors!(Object, InnerMap, as_object, to_object);

impl_try_from_to_vec!(Int, i64);
impl_try_from_to_vec!(Float, f64);
impl_try_from_to_vec!(Bool, bool);
impl_try_from_to_vec!(String, String);

impl<'a> TryFrom<&'a Value> for &'a str {
    type Error = ValueConversionError;

    fn try_from(value: &'a Value) -> Result<Self, Self::Error> {
        if let Value::String(val) = value {
            Ok(val.as_str())
        } else {
            Err(ValueConversionError { expected: "String" })
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn try_from_value_to_vec_i64() {
        let source = vec![1, 2, 3];
        let val = Value::from_iter(source.clone());
        let v: Vec<i64> = val.try_into().unwrap();
        assert_eq!(source, v);
    }

    #[test]
    fn try_from_value_to_vec_f64() {
        let source = vec![1.0, 2.0, 3.0];
        let val = Value::from_iter(source.clone());
        let v: Vec<f64> = val.try_into().unwrap();
        assert_eq!(source, v);
    }

    #[test]
    fn try_from_value_to_vec_string() {
        let source = vec!["1", "2", "3"];
        let val = Value::from_iter(source.clone());

        let v: Vec<String> = val.try_into().unwrap();
        assert_eq!(source, v);
    }

    #[test]
    fn try_from_value_to_vec_bool() {
        let source = vec![true, false, false];
        let val = Value::from_iter(source.clone());

        let v: Vec<bool> = val.try_into().unwrap();
        assert_eq!(source, v);
    }
    #[test]
    fn try_from_value_to_vec_wrong_type() {
        let source = vec![1.0, 2.0, 3.0];
        let val = Value::from_iter(source.clone());
        let val_ref = &val;
        let res: Result<Vec<i64>, _> = val_ref.try_into();
        let err = res.unwrap_err();
        assert_eq!(err.expected, "Array<Int>");

        let res: Result<i64, _> = val_ref.try_into();
        let err = res.unwrap_err();
        assert_eq!(err.expected, "Int");
    }

    #[test]
    fn try_from_value_wrong_type() {
        let source = 1.0;
        let val = Value::from(source);
        let res: Result<Vec<i64>, _> = val.try_into();
        let err = res.unwrap_err();
        assert_eq!(err.expected, "Array<Int>");
    }
}