valida 1.1.2

Modular validation system for Rust with support for nested structures and localization.
Documentation
use crate::core::contract::IValidatorRule;
use crate::core::errors::ValidationError;
use crate::core::primitive::PrimitiveRule;
use crate::core::rules::slice_ref::SliceRef;

pub struct EachRule<R> {
    pub rule: R,
}

impl<V, R> IValidatorRule<V> for EachRule<R>
where
    V: SliceRef,
    V::Item: PrimitiveRule,
    R: IValidatorRule<V::Item>,
{
    fn validate(&self, value: &V) -> Result<(), ValidationError> {
        if let Some(slice) = value.slice() {
            for (i, item) in slice.iter().enumerate() {
                if let Err(mut err) = self.rule.validate(item) {
                    err.params.insert("index".into(), i.to_string());
                    return Err(err);
                }
            }
        }
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::core::contract::IValidatorRule;
    use crate::core::rules::string::max_length::MaxLength;

    fn validator(max: usize) -> EachRule<MaxLength> {
        EachRule {
            rule: MaxLength { max },
        }
    }

    #[test]
    fn validates_all_items_successfully() {
        let value = vec!["red", "green", "blue"];
        let result = validator(10).validate(&value);
        assert!(result.is_ok());
    }

    #[test]
    fn fails_on_excessive_length_item() {
        let value = vec!["ok", "this-is-too-long"];
        let result = validator(5).validate(&value);
        assert!(result.is_err());

        let err = result.unwrap_err();
        assert_eq!(err.key, "validator.max_length");
        assert_eq!(err.params.get("index"), Some(&"1".into()));
        assert_eq!(err.params.get("max"), Some(&"5".into()));
    }

    #[test]
    fn validates_empty_vec() {
        let value: Vec<&str> = vec![];
        let result = validator(3).validate(&value);
        assert!(result.is_ok());
    }

    #[test]
    fn validates_option_none() {
        let value: Option<Vec<String>> = None;
        let result = validator(12).validate(&value);
        assert!(result.is_ok());
    }

    #[test]
    fn validates_boxed_vec() {
        let value = Box::new(vec!["a", "b"]);
        let result = validator(2).validate(&value);
        assert!(result.is_ok());
    }

    #[test]
    fn validates_rc_vec() {
        use std::rc::Rc;
        let value = Rc::new(vec!["one", "two"]);
        let result = validator(3).validate(&value);
        assert!(result.is_ok());
    }

    #[test]
    fn fails_arc_vec_over_limit() {
        use std::sync::Arc;
        let value = Arc::new(vec!["short", "way-too-long"]);
        let result = validator(6).validate(&value);
        assert!(result.is_err());

        let err = result.unwrap_err();
        assert_eq!(err.params.get("index"), Some(&"1".into()));
    }

    #[test]
    fn validates_ref_vec() {
        let vec = vec!["yes", "no"];
        let value = &vec;
        let result = validator(3).validate(&value);
        assert!(result.is_ok());
    }

    #[test]
    fn validates_option_vec_empty() {
        let value: Option<Vec<String>> = Some(vec![]);
        let result = validator(10).validate(&value);
        assert!(result.is_ok());
    }
}