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::rules::str_ref::StrAsRef;
use std::collections::HashMap;

pub struct NoSuspiciousCharacters {
    pub blacklist: &'static [char],
}

impl<T: StrAsRef> IValidatorRule<T> for NoSuspiciousCharacters {
    fn validate(&self, value: &T) -> Result<(), ValidationError> {
        if let Some(s) = value.as_str_ref() {
            if let Some(c) = s.chars().find(|c| self.blacklist.contains(c)) {
                return Err(ValidationError::new_with_params(
                    "validator.no_suspicious",
                    HashMap::from([("char".into(), c.to_string())]),
                ));
            }
        }
        Ok(())
    }
}

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

    const DEFAULT_BLACKLIST: &[char] = &['<', '>', '&', '\'', '"', '\\', ';', '(', ')'];

    fn validator() -> NoSuspiciousCharacters {
        NoSuspiciousCharacters {
            blacklist: DEFAULT_BLACKLIST,
        }
    }

    #[test]
    fn passes_clean_text() {
        let value = "HelloWorld_2024";
        assert!(validator().validate(&value).is_ok());
    }

    #[test]
    fn fails_on_angle_brackets() {
        let value = "<script>";
        let result = validator().validate(&value);
        assert!(result.is_err());
    }

    #[test]
    fn fails_on_sql_like_string() {
        let value = "admin' OR '1'='1";
        let result = validator().validate(&value);
        assert!(result.is_err());
    }

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