Skip to main content

sz_orm_core/validation/
mod.rs

1//! # 数据验证框架(`data-validation` feature)
2//!
3//! 提供 `Validate` trait + `ValidationError` + 8 种字段级校验规则,
4//! 支持 `#[derive(Validate)]` 自动生成验证代码。
5
6pub mod rules;
7
8#[cfg(feature = "validate-on-write")]
9pub mod model_integration;
10
11#[cfg(test)]
12mod derive_tests;
13
14/// 验证错误
15#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
16pub enum ValidationError {
17    /// 必填字段为空
18    #[error("field `{field}` is required but empty")]
19    Required { field: String },
20    /// 长度超出范围
21    #[error("field `{field}` length {actual} not in [{min}, {max}]")]
22    Length {
23        field: String,
24        min: usize,
25        max: usize,
26        actual: usize,
27    },
28    /// 数值超出范围
29    #[error("field `{field}` value {actual} not in [{min}, {max}]")]
30    Range {
31        field: String,
32        min: String,
33        max: String,
34        actual: String,
35    },
36    /// 邮箱格式无效
37    #[error("field `{field}` value `{value}` is not a valid email")]
38    Email { field: String, value: String },
39    /// 正则不匹配
40    #[error("field `{field}` value `{value}` does not match pattern `{pattern}`")]
41    Regex {
42        field: String,
43        pattern: String,
44        value: String,
45    },
46    /// 不包含所需子串
47    #[error("field `{field}` does not contain `{substring}`")]
48    Contains { field: String, substring: String },
49    /// 包含禁止子串
50    #[error("field `{field}` contains forbidden `{substring}`")]
51    DoesNotContain { field: String, substring: String },
52    /// 自定义校验失败
53    #[error("field `{field}` custom validation failed: {reason}")]
54    Custom { field: String, reason: String },
55    /// 聚合错误(非短路,收集全部失败)
56    #[error("validation failed with {count} error(s)")]
57    Aggregate {
58        errors: Vec<ValidationError>,
59        count: usize,
60    },
61}
62
63/// 验证 trait
64pub trait Validate {
65    /// 执行验证,返回 Ok 或聚合错误
66    fn validate(&self) -> Result<(), ValidationError>;
67}
68
69/// 聚合多个验证结果(非短路,收集全部错误)
70pub fn aggregate(results: Vec<Result<(), ValidationError>>) -> Result<(), ValidationError> {
71    let errors: Vec<ValidationError> = results.into_iter().filter_map(|r| r.err()).collect();
72    match errors.len() {
73        0 => Ok(()),
74        1 => Err(errors.into_iter().next().unwrap()),
75        n => Err(ValidationError::Aggregate { errors, count: n }),
76    }
77}