#![allow(dead_code)]
#[derive(Debug, Clone, PartialEq, Eq)]
#[allow(dead_code)]
pub struct ValidationError {
pub message: String,
}
impl ValidationError {
#[allow(dead_code)]
pub fn new(msg: &str) -> Self {
ValidationError { message: msg.to_string() }
}
}
#[derive(Debug, Clone, Default)]
#[allow(dead_code)]
pub struct ValidationResult {
pub errors: Vec<ValidationError>,
}
impl ValidationResult {
#[allow(dead_code)]
pub fn is_ok(&self) -> bool {
self.errors.is_empty()
}
}
#[allow(dead_code)]
pub type ValidatorFn = Box<dyn Fn(&str) -> Option<String> + Send + Sync>;
#[allow(clippy::type_complexity)]
#[allow(dead_code)]
pub struct Validator {
pub name: String,
pub check: ValidatorFn,
}
impl std::fmt::Debug for Validator {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Validator").field("name", &self.name).finish()
}
}
#[derive(Debug, Default)]
#[allow(dead_code)]
pub struct ValidationChain {
validators: Vec<Validator>,
}
#[allow(dead_code)]
pub fn new_validation_chain() -> ValidationChain {
ValidationChain::default()
}
#[allow(dead_code)]
pub fn add_validator(
chain: &mut ValidationChain,
name: &str,
check: impl Fn(&str) -> Option<String> + Send + Sync + 'static,
) {
chain.validators.push(Validator { name: name.to_string(), check: Box::new(check) });
}
#[allow(dead_code)]
pub fn validate_value(chain: &ValidationChain, value: &str) -> ValidationResult {
let errors = chain
.validators
.iter()
.filter_map(|v| (v.check)(value).map(|msg| ValidationError { message: msg }))
.collect();
ValidationResult { errors }
}
#[allow(dead_code)]
pub fn validation_passed(chain: &ValidationChain, value: &str) -> bool {
validate_value(chain, value).is_ok()
}
#[allow(dead_code)]
pub fn validation_error_count(result: &ValidationResult) -> usize {
result.errors.len()
}
#[allow(dead_code)]
pub fn first_error(result: &ValidationResult) -> Option<&str> {
result.errors.first().map(|e| e.message.as_str())
}
#[allow(dead_code)]
pub fn clear_validators(chain: &mut ValidationChain) {
chain.validators.clear();
}
#[allow(dead_code)]
pub fn validator_count(chain: &ValidationChain) -> usize {
chain.validators.len()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new_validation_chain() {
let chain = new_validation_chain();
assert_eq!(validator_count(&chain), 0);
}
#[test]
fn test_add_validator() {
let mut chain = new_validation_chain();
add_validator(&mut chain, "non_empty", |v| {
if v.is_empty() { Some("must not be empty".to_string()) } else { None }
});
assert_eq!(validator_count(&chain), 1);
}
#[test]
fn test_validation_passed() {
let mut chain = new_validation_chain();
add_validator(&mut chain, "non_empty", |v| {
if v.is_empty() { Some("empty".to_string()) } else { None }
});
assert!(validation_passed(&chain, "hello"));
assert!(!validation_passed(&chain, ""));
}
#[test]
fn test_validate_value_errors() {
let mut chain = new_validation_chain();
add_validator(&mut chain, "min_len", |v| {
if v.len() < 5 { Some("too short".to_string()) } else { None }
});
add_validator(&mut chain, "no_spaces", |v| {
if v.contains(' ') { Some("no spaces".to_string()) } else { None }
});
let result = validate_value(&chain, "a b");
assert_eq!(validation_error_count(&result), 2);
}
#[test]
fn test_first_error() {
let mut chain = new_validation_chain();
add_validator(&mut chain, "v", |_| Some("err".to_string()));
let result = validate_value(&chain, "x");
assert_eq!(first_error(&result), Some("err"));
}
#[test]
fn test_no_errors() {
let chain = new_validation_chain();
let result = validate_value(&chain, "anything");
assert_eq!(validation_error_count(&result), 0);
assert!(first_error(&result).is_none());
}
#[test]
fn test_clear_validators() {
let mut chain = new_validation_chain();
add_validator(&mut chain, "v", |_| None);
clear_validators(&mut chain);
assert_eq!(validator_count(&chain), 0);
}
#[test]
fn test_validation_error_new() {
let e = ValidationError::new("test error");
assert_eq!(e.message, "test error");
}
#[test]
fn test_validation_result_is_ok() {
let result = ValidationResult { errors: vec![] };
assert!(result.is_ok());
let result2 = ValidationResult { errors: vec![ValidationError::new("e")] };
assert!(!result2.is_ok());
}
}