Skip to main content

sz_rust_infra_facade/
validate.rs

1//! 验证器模块 — 对齐 PHP `think\Validate`
2//!
3//! 本模块实现验证器框架,对齐 PHP `think\Validate` 类的
4//! 规则定义、批量验证、错误消息查找、场景管理等核心机制。
5//!
6//! ## PHP 对齐
7//!
8//! ### 核心结构映射
9//!
10//! | PHP 字段 | Rust 字段 | 说明 |
11//! |---------|-----------|------|
12//! | `$rule` | [`Validate::rule`] | 当前验证规则 |
13//! | `$message` | [`Validate::message`] | 验证提示信息 |
14//! | `$field` | [`Validate::field`] | 字段描述 |
15//! | `$typeMsg` | [`TYPE_MSG`] | 默认规则提示 |
16//! | `$alias` | [`ALIAS`] | 验证类型别名 |
17//! | `$defaultRegex` | [`DEFAULT_REGEX`] | 内置正则 |
18//! | `$regex` | [`Validate::regex`] | 自定义正则 |
19//! | `$scene` | [`Validate::scene`] | 验证场景定义 |
20//! | `$currentScene` | `Validate::current_scene` | 当前验证场景 |
21//! | `$batch` | [`Validate::batch`] | 是否批量验证 |
22//! | `$only` | [`Validate::only`] | 场景需要验证的字段 |
23//! | `$remove` | [`Validate::remove`] | 场景移除的规则 |
24//! | `$append` | [`Validate::append`] | 场景追加的规则 |
25//! | `$error` | `Validate::error` | 验证失败错误信息 |
26//! | `$type` | `Validate::type_callbacks` | 自定义验证类型 |
27//!
28//! ### 核心方法映射
29//!
30//! | PHP 方法 | Rust 方法 | 说明 |
31//! |---------|-----------|------|
32//! | `rule($name, $rule)` | [`Validate::rule`] | 添加字段验证规则 |
33//! | `message(array $msg)` | [`Validate::message`] | 设置提示信息 |
34//! | `scene($name)` | [`Validate::scene`] | 设置验证场景 |
35//! | `hasScene($name)` | [`Validate::has_scene`] | 判断场景是否存在 |
36//! | `batch(bool)` | [`Validate::batch`] | 设置批量验证 |
37//! | `only(array)` | [`Validate::only`] | 指定需要验证的字段 |
38//! | `remove($field, $rule)` | [`Validate::remove`] | 移除字段规则 |
39//! | `append($field, $rule)` | [`Validate::append`] | 追加字段规则 |
40//! | `extend($type, $cb)` | [`Validate::extend`] | 注册验证类型 |
41//! | `check(array $data, array $rules)` | [`Validate::check`] | 数据自动验证 |
42//! | `checkRule($value, $rules)` | [`Validate::check_rule`] | 根据规则验证数据 |
43//! | `getError()` | [`Validate::get_error`] | 获取错误信息 |
44//! | `getRuleMsg(...)` | [`Validate::get_rule_msg`] | 获取规则错误提示 |
45//! | `parseErrorMsg(...)` | [`Validate::parse_error_msg`] | 解析错误提示 |
46//! | `getDataValue(...)` | [`Validate::get_data_value`] | 获取数据值 |
47//! | `getValidateType(...)` | [`Validate::get_validate_type`] | 获取验证类型 |
48//! | `is($value, $rule)` | [`Validate::is`] | 验证字段值是否为有效格式 |
49//! | `require($value)` | [`Validate::require`] | 必须验证 |
50//! | `must($value)` | [`Validate::must`] | 必须验证(与 require 等价) |
51//!
52//! ## PHP 行为对齐(R5 硬约束)
53//!
54//! 本模块严格对齐以下 PHP 行为(包括 bug):
55//!
56//! - **R5-1**:`getRuleMsg` 查找优先级链(对齐 PHP 第 1565-1586 行):
57//!   1. `message[field.type]`
58//!   2. `message[field]`
59//!   3. `type_msg[type]`
60//!   4. 如果 type 以 `require` 开头,使用 `type_msg['require']`
61//!   5. 默认 `$title . lang->get('not conform to the rules')`(无前导空格,对齐 PHP 第 1578 行)
62//!
63//! - **R5-2**:`parseErrorMsg` 占位符替换(对齐 PHP 第 1596-1633 行):
64//!   1. `:attribute` → title
65//!   2. `:1` / `:2` / `:3` → rule 按逗号分割后的前 3 个元素
66//!   3. `:rule` → rule 原值(仅当 msg 包含 `:rule` 时)
67//!
68//! - **R5-3**:`getDataValue` 行为(对齐 PHP 第 1536-1554 行):
69//!   - 数值型 key 返回 key 本身(PHP 怪异行为,复刻)
70//!   - 包含 `.` 的 key 按多维数组访问
71//!   - 其他 key 返回 `data[key]` 或 null
72//!
73//! - **R5-4**:`getValidateType` 类型推导(对齐 PHP 第 678-706 行):
74//!   - 别名映射(`>` → `gt`,`>=` → `egt` 等)
75//!   - `info` 字段用于 `remove`/`append` 匹配
76//!
77//! - **R5-5**:空值跳过验证行为(对齐 PHP 第 634-637 行):
78//!   - 如果 value 是 null 或空字符串,且 info 不是 `must` 或不以 `require` 开头,则跳过验证
79//!
80//! - **R5-6**:场景重置行为(对齐 PHP `getScene` 第 1661 行):
81//!   - 切换场景时,`only`/`append`/`remove` 全部重置
82//!
83//! ## 架构说明
84//!
85//! 框架层内置基础规则(`require`/`must`/`is`)。
86//! 完整内置规则集在 `validate/rules.rs` 中
87//! (email/mobile/url/in/notIn/max/min/length 等)。
88//!
89//! 自定义规则通过 [`Validate::extend`] 注册,回调签名:
90//! `Fn(value: &Value, rule: &str, data: &Value) -> bool + Send + Sync`
91//!
92//! ## PHP 源码参考
93//!
94//! - `e:\vue\test\鲜视达\server\vendor\topthink\framework\src\think\Validate.php`
95//!   - 第 24-215 行:类属性定义
96//!   - 第 286-298 行:`rule()` 方法
97//!   - 第 354-360 行:`scene()` 方法
98//!   - 第 471-540 行:`check()` 方法
99//!   - 第 594-669 行:`checkItem()` 方法
100//!   - 第 678-706 行:`getValidateType()` 方法
101//!   - 第 827-888 行:`is()` 方法
102//!   - 第 1536-1554 行:`getDataValue()` 方法
103//!   - 第 1565-1586 行:`getRuleMsg()` 方法
104//!   - 第 1596-1633 行:`parseErrorMsg()` 方法
105
106use std::sync::Arc;
107
108use indexmap::IndexMap;
109use once_cell::sync::Lazy;
110use regex::Regex;
111use serde_json::Value;
112
113pub mod message;
114pub mod rules;
115pub mod scene;
116
117// ============================================================================
118// 验证错误类型
119// ============================================================================
120
121/// 验证错误
122///
123/// 对齐 PHP `$this->error`:在非批量模式下为单条错误字符串,在批量模式下为
124/// `IndexMap<字段名, 错误信息>`。
125#[derive(Debug, Clone, PartialEq, Eq)]
126pub enum ValidateError {
127    /// 单条错误(非批量模式)
128    ///
129    /// 对齐 PHP `$this->error = $result`(字符串赋值)
130    Single(String),
131    /// 多条错误(批量模式,按字段分组)
132    ///
133    /// 对齐 PHP `$this->error[$key] = $result`
134    Batch(IndexMap<String, String>),
135}
136
137impl std::fmt::Display for ValidateError {
138    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
139        match self {
140            ValidateError::Single(msg) => write!(f, "{}", msg),
141            ValidateError::Batch(errors) => {
142                let entries: Vec<String> = errors
143                    .iter()
144                    .map(|(k, v)| format!("{}: {}", k, v))
145                    .collect();
146                write!(f, "{}", entries.join("; "))
147            }
148        }
149    }
150}
151
152impl std::error::Error for ValidateError {}
153
154// ============================================================================
155// 验证规则类型
156// ============================================================================
157
158/// 验证规则表示
159///
160/// 对齐 PHP `$rule`:可以是字符串(`"require|in:a,b,c"`)、数组或闭包。
161/// 闭包通过 [`Validate::extend`] 单独注册。
162#[derive(Debug, Clone)]
163pub enum Rule {
164    /// 单条规则(如 `"require"`、`"email"`)
165    Simple(String),
166    /// 带参数的规则(如 `"in:1,2,3"`、`"length:1,10"`)
167    WithArgs(String, String),
168    /// 多条规则(对齐 PHP `"require|in:1,2,3"` 或 `['require', 'in' => 'a,b,c']`)
169    Multiple(Vec<Rule>),
170}
171
172impl Rule {
173    /// 从 PHP 风格规则字符串创建
174    ///
175    /// ## 示例
176    ///
177    /// ```ignore
178    /// use sz_rust_infra_facade::validate::Rule;
179    ///
180    /// let _ = Rule::from_string("require");
181    /// let _ = Rule::from_string("in:1,2,3");
182    /// let _ = Rule::from_string("require|in:1,2,3"); // 自动转 Multiple
183    /// ```
184    pub fn from_string(s: &str) -> Self {
185        if s.contains('|') {
186            let parts: Vec<Rule> = s
187                .split('|')
188                .map(|p| {
189                    if let Some((t, a)) = p.split_once(':') {
190                        Rule::WithArgs(t.to_string(), a.to_string())
191                    } else {
192                        Rule::Simple(p.to_string())
193                    }
194                })
195                .collect();
196            Rule::Multiple(parts)
197        } else if let Some((t, a)) = s.split_once(':') {
198            Rule::WithArgs(t.to_string(), a.to_string())
199        } else {
200            Rule::Simple(s.to_string())
201        }
202    }
203
204    /// 转为规则列表 `Vec<(type, args)>`(对齐 PHP `explode('|', $rules)`)
205    pub fn to_list(&self) -> Vec<(String, String)> {
206        match self {
207            Rule::Simple(t) => vec![(t.clone(), String::new())],
208            Rule::WithArgs(t, a) => vec![(t.clone(), a.clone())],
209            Rule::Multiple(list) => list.iter().flat_map(|r| r.to_list()).collect(),
210        }
211    }
212}
213
214// ============================================================================
215// 自定义规则回调类型
216// ============================================================================
217
218/// 自定义规则回调类型
219///
220/// 签名:`Fn(value: &Value, rule: &str, data: &Value) -> bool`
221///
222/// - `value`:字段值
223/// - `rule`:规则参数(如 `"1,2,3"` for `in:1,2,3`)
224/// - `data`:完整数据
225/// - 返回:`true` 通过,`false` 失败
226pub type RuleCallback = Arc<dyn Fn(&Value, &str, &Value) -> bool + Send + Sync>;
227
228// ============================================================================
229// 内置静态映射(对齐 PHP 类属性)
230// ============================================================================
231
232/// PHP `think\Validate::$defaultRegex` 内置正则
233///
234/// 对齐 PHP `Validate.php` 第 125-136 行
235pub static DEFAULT_REGEX: Lazy<IndexMap<&'static str, &'static str>> = Lazy::new(|| {
236    let mut m = IndexMap::new();
237    m.insert("alpha", "/^[A-Za-z]+$/");
238    m.insert("alphaNum", "/^[A-Za-z0-9]+$/");
239    m.insert("alphaDash", "/^[A-Za-z0-9\\-\\_]+$/");
240    m.insert(
241        "chs",
242        "/^[\\x{4e00}-\\x{9fa5}\\x{9fa6}-\\x{9fef}\\x{3400}-\\x{4db5}\\x{20000}-\\x{2ebe0}]+$/u",
243    );
244    m.insert(
245        "chsAlpha",
246        "/^[\\x{4e00}-\\x{9fa5}\\x{9fa6}-\\x{9fef}\\x{3400}-\\x{4db5}\\x{20000}-\\x{2ebe0}a-zA-Z]+$/u",
247    );
248    m.insert(
249        "chsAlphaNum",
250        "/^[\\x{4e00}-\\x{9fa5}\\x{9fa6}-\\x{9fef}\\x{3400}-\\x{4db5}\\x{20000}-\\x{2ebe0}a-zA-Z0-9]+$/u",
251    );
252    m.insert(
253        "chsDash",
254        "/^[\\x{4e00}-\\x{9fa5}\\x{9fa6}-\\x{9fef}\\x{3400}-\\x{4db5}\\x{20000}-\\x{2ebe0}a-zA-Z0-9\\_\\-]+$/u",
255    );
256    m.insert("mobile", "/^1[3-9]\\d{9}$/");
257    m.insert(
258        "idCard",
259        "/(^[1-9]\\d{5}(18|19|([23]\\d))\\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\\d{3}[0-9Xx]$)|(^[1-9]\\d{5}\\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\\d{3}$)/",
260    );
261    m.insert("zip", "/\\d{6}/");
262    m
263});
264
265/// PHP `think\Validate::$typeMsg` 内置类型提示
266///
267/// 对齐 PHP `Validate.php` 第 62-113 行
268pub static TYPE_MSG: Lazy<IndexMap<&'static str, &'static str>> = Lazy::new(|| {
269    let mut m = IndexMap::new();
270    m.insert("require", ":attribute require");
271    m.insert("must", ":attribute must");
272    m.insert("number", ":attribute must be numeric");
273    m.insert("integer", ":attribute must be integer");
274    m.insert("float", ":attribute must be float");
275    m.insert("boolean", ":attribute must be bool");
276    m.insert("email", ":attribute not a valid email address");
277    m.insert("mobile", ":attribute not a valid mobile");
278    m.insert("array", ":attribute must be a array");
279    m.insert("accepted", ":attribute must be yes,on or 1");
280    m.insert("date", ":attribute not a valid datetime");
281    m.insert("file", ":attribute not a valid file");
282    m.insert("image", ":attribute not a valid image");
283    m.insert("alpha", ":attribute must be alpha");
284    m.insert("alphaNum", ":attribute must be alpha-numeric");
285    m.insert(
286        "alphaDash",
287        ":attribute must be alpha-numeric, dash, underscore",
288    );
289    m.insert("activeUrl", ":attribute not a valid domain or ip");
290    m.insert("chs", ":attribute must be chinese");
291    m.insert("chsAlpha", ":attribute must be chinese or alpha");
292    m.insert("chsAlphaNum", ":attribute must be chinese,alpha-numeric");
293    m.insert(
294        "chsDash",
295        ":attribute must be chinese,alpha-numeric,underscore, dash",
296    );
297    m.insert("url", ":attribute not a valid url");
298    m.insert("ip", ":attribute not a valid ip");
299    m.insert("dateFormat", ":attribute must be dateFormat of :rule");
300    m.insert("in", ":attribute must be in :rule");
301    m.insert("notIn", ":attribute be notin :rule");
302    m.insert("between", ":attribute must between :1 - :2");
303    m.insert("notBetween", ":attribute not between :1 - :2");
304    m.insert("length", "size of :attribute must be :rule");
305    m.insert("max", "max size of :attribute must be :rule");
306    m.insert("min", "min size of :attribute must be :rule");
307    m.insert("after", ":attribute cannot be less than :rule");
308    m.insert("before", ":attribute cannot exceed :rule");
309    m.insert("expire", ":attribute not within :rule");
310    m.insert("allowIp", "access IP is not allowed");
311    m.insert("denyIp", "access IP denied");
312    m.insert("confirm", ":attribute out of accord with :2");
313    m.insert("different", ":attribute cannot be same with :2");
314    m.insert("egt", ":attribute must greater than or equal :rule");
315    m.insert("gt", ":attribute must greater than :rule");
316    m.insert("elt", ":attribute must less than or equal :rule");
317    m.insert("lt", ":attribute must less than :rule");
318    m.insert("eq", ":attribute must equal :rule");
319    m.insert("unique", ":attribute has exists");
320    m.insert("regex", ":attribute not conform to the rules");
321    m.insert("method", "invalid Request method");
322    m.insert("token", "invalid token");
323    m.insert("fileSize", "filesize not match");
324    m.insert("fileExt", "extensions to upload is not allowed");
325    m.insert("fileMime", "mimetype to upload is not allowed");
326    m
327});
328
329/// PHP `think\Validate::$alias` 验证类型别名
330///
331/// 对齐 PHP `Validate.php` 第 36-38 行
332pub static ALIAS: Lazy<IndexMap<&'static str, &'static str>> = Lazy::new(|| {
333    let mut m = IndexMap::new();
334    m.insert(">", "gt");
335    m.insert(">=", "egt");
336    m.insert("<", "lt");
337    m.insert("<=", "elt");
338    m.insert("=", "eq");
339    m.insert("same", "eq");
340    m
341});
342
343// ============================================================================
344// Validate 主结构
345// ============================================================================
346
347/// 验证器 — 对齐 PHP `think\Validate`
348///
349/// ## 用法
350///
351/// ```ignore
352/// use sz_rust_infra_facade::validate::Validate;
353/// use serde_json::json;
354///
355/// let mut v = Validate::new()
356///     .rule("name", "require")
357///     .rule("age", "require|integer");
358/// let data = json!({"name": "Alice", "age": 30});
359/// assert!(v.check(&data).is_ok());
360/// ```
361pub struct Validate {
362    /// 当前验证规则(字段名 → 规则)
363    rule: IndexMap<String, Rule>,
364    /// 验证提示信息
365    message: IndexMap<String, String>,
366    /// 字段描述(英文名 → 中文名)
367    field: IndexMap<String, String>,
368    /// 自定义正则
369    regex: IndexMap<String, String>,
370    /// 验证场景定义(数组形式,对齐 PHP `$scene`)
371    scene: IndexMap<String, Vec<String>>,
372    /// 验证场景回调(对齐 PHP `scene{Name}` 方法)
373    scene_callbacks: IndexMap<String, scene::SceneCallback>,
374    /// 当前验证场景
375    current_scene: Option<String>,
376    /// 是否批量验证
377    batch: bool,
378    /// 场景需要验证的字段
379    only: Vec<String>,
380    /// 场景需要移除的验证规则(字段 → Option<规则列表>,None 表示移除所有)
381    remove: IndexMap<String, Option<Vec<String>>>,
382    /// 场景需要追加的验证规则
383    append: IndexMap<String, Vec<String>>,
384    /// 验证失败错误信息
385    error: ValidateError,
386    /// 自定义验证类型回调
387    type_callbacks: IndexMap<String, RuleCallback>,
388    /// 多语言实例 — 对齐 PHP `think\Validate::$lang`
389    ///
390    /// 多语言支持。`None` 时跳过翻译(对齐 PHP 未注入 Lang 时的行为)。
391    /// 通过 [`Validate::set_lang`] 注入实例。
392    lang: Option<Arc<dyn message::Lang>>,
393}
394
395impl Default for Validate {
396    fn default() -> Self {
397        Self::new()
398    }
399}
400
401impl Validate {
402    /// 创建新的验证器
403    pub fn new() -> Self {
404        Self {
405            rule: IndexMap::new(),
406            message: IndexMap::new(),
407            field: IndexMap::new(),
408            regex: IndexMap::new(),
409            scene: IndexMap::new(),
410            scene_callbacks: IndexMap::new(),
411            current_scene: None,
412            batch: false,
413            only: Vec::new(),
414            remove: IndexMap::new(),
415            append: IndexMap::new(),
416            error: ValidateError::Single(String::new()),
417            type_callbacks: IndexMap::new(),
418            lang: None,
419        }
420    }
421
422    // ========================================================================
423    // Builder 方法
424    // ========================================================================
425
426    /// 添加字段验证规则
427    ///
428    /// 对齐 PHP `rule($name, $rule = '')`(第 286-298 行)
429    ///
430    /// ## 参数
431    ///
432    /// - `name`:字段名(支持 `field|title` 格式指定字段描述)
433    /// - `rule`:验证规则(字符串形式,如 `"require|in:1,2,3"`)
434    pub fn rule(mut self, name: &str, rule: &str) -> Self {
435        self.rule.insert(name.to_string(), Rule::from_string(rule));
436        self
437    }
438
439    /// 设置提示信息
440    ///
441    /// 对齐 PHP `message(array $message)`(第 341-346 行)
442    pub fn message(mut self, messages: IndexMap<String, String>) -> Self {
443        for (k, v) in messages {
444            self.message.insert(k, v);
445        }
446        self
447    }
448
449    /// 设置字段描述
450    ///
451    /// 对齐 PHP `rule()` 方法中 `$rule` 为数组时合并到 `$this->field`
452    pub fn field(mut self, fields: IndexMap<String, String>) -> Self {
453        for (k, v) in fields {
454            self.field.insert(k, v);
455        }
456        self
457    }
458
459    /// 设置验证场景
460    ///
461    /// 对齐 PHP `scene(string $name)`(第 354-360 行)
462    pub fn scene(mut self, name: &str) -> Self {
463        self.current_scene = Some(name.to_string());
464        self
465    }
466
467    /// 注册场景字段列表
468    ///
469    /// 对齐 PHP `$this->scene[$name] = $fields` 属性赋值
470    pub fn register_scene(mut self, name: &str, fields: Vec<String>) -> Self {
471        self.scene.insert(name.to_string(), fields);
472        self
473    }
474
475    /// 注册场景回调 — 对齐 PHP `protected function scene{Name}()`
476    ///
477    /// 场景回调支持。回调签名 `Fn(&mut Validate) + Send + Sync`,
478    /// 回调内部可调用 [`Validate::only_mut`]、[`Validate::append_mut`]、
479    /// [`Validate::remove_mut`] 修改场景状态。
480    ///
481    /// ## PHP 对齐
482    ///
483    /// ```php
484    /// protected function sceneLogin()
485    /// {
486    ///     return $this->only(['email']);
487    /// }
488    /// ```
489    ///
490    /// Rust 等价:
491    ///
492    /// ```ignore
493    /// use std::sync::Arc;
494    /// v.register_scene_callback("login", Arc::new(|v| {
495    ///     v.only_mut(vec!["email".to_string()]);
496    /// }));
497    /// ```
498    ///
499    /// ## 优先级
500    ///
501    /// 对齐 PHP `getScene`(第 1663-1668 行):**回调优先于数组形式**。
502    /// 如果同一场景名同时注册了数组和回调,回调会被调用,数组被忽略。
503    pub fn register_scene_callback(mut self, name: &str, callback: scene::SceneCallback) -> Self {
504        self.scene_callbacks.insert(name.to_string(), callback);
505        self
506    }
507
508    /// 判断是否存在某个验证场景
509    ///
510    /// 对齐 PHP `hasScene(string $name)`(第 368-371 行)
511    ///
512    /// ## PHP 行为
513    ///
514    /// `return isset($this->scene[$name]) || method_exists($this, 'scene' . $name);`
515    ///
516    /// Rust 实现:检查 `scene` 数组 **或** `scene_callbacks` 映射
517    pub fn has_scene(&self, name: &str) -> bool {
518        self.scene.contains_key(name) || self.scene_callbacks.contains_key(name)
519    }
520
521    /// 设置批量验证
522    ///
523    /// 对齐 PHP `batch(bool $batch = true)`(第 379-384 行)
524    pub fn batch(mut self, batch: bool) -> Self {
525        self.batch = batch;
526        self
527    }
528
529    /// 指定需要验证的字段列表
530    ///
531    /// 对齐 PHP `only(array $fields)`(第 405-410 行)
532    pub fn only(mut self, fields: Vec<String>) -> Self {
533        self.only = fields;
534        self
535    }
536
537    /// 移除某个字段的验证规则
538    ///
539    /// 对齐 PHP `remove($field, $rule = null)`(第 419-438 行)
540    ///
541    /// ## 参数
542    ///
543    /// - `field`:字段名
544    /// - `rule`:要移除的规则列表(`None` 表示移除所有规则)
545    pub fn remove(mut self, field: &str, rule: Option<Vec<String>>) -> Self {
546        self.remove.insert(field.to_string(), rule);
547        self
548    }
549
550    /// 追加某个字段的验证规则
551    ///
552    /// 对齐 PHP `append($field, $rule = null)`(第 447-462 行)
553    pub fn append(mut self, field: &str, rule: Vec<String>) -> Self {
554        self.append.insert(field.to_string(), rule);
555        self
556    }
557
558    /// 指定需要验证的字段列表(`&mut self` 版本)
559    ///
560    /// 对齐 PHP `only(array $fields)` 的 `&mut self` 语义,供 scene 回调使用。
561    /// 对齐 PHP `sceneXxx` 方法内部调用 `$this->only([...])`。
562    pub fn only_mut(&mut self, fields: Vec<String>) {
563        self.only = fields;
564    }
565
566    /// 移除某个字段的验证规则(`&mut self` 版本)
567    ///
568    /// 对齐 PHP `remove($field, $rule = null)` 的 `&mut self` 语义,供 scene 回调使用。
569    pub fn remove_mut(&mut self, field: &str, rule: Option<Vec<String>>) {
570        self.remove.insert(field.to_string(), rule);
571    }
572
573    /// 追加某个字段的验证规则(`&mut self` 版本)
574    ///
575    /// 对齐 PHP `append($field, $rule = null)` 的 `&mut self` 语义,供 scene 回调使用。
576    pub fn append_mut(&mut self, field: &str, rule: Vec<String>) {
577        self.append.insert(field.to_string(), rule);
578    }
579
580    /// 注册验证类型(自定义规则回调)
581    ///
582    /// 对齐 PHP `extend(string $type, callable $callback, string $message = null)`(第 308-317 行)
583    pub fn extend(&mut self, type_name: &str, callback: RuleCallback) -> &mut Self {
584        self.type_callbacks.insert(type_name.to_string(), callback);
585        self
586    }
587
588    /// 设置自定义正则
589    ///
590    /// 对齐 PHP `$this->regex` 属性
591    pub fn regex(mut self, name: &str, pattern: &str) -> Self {
592        self.regex.insert(name.to_string(), pattern.to_string());
593        self
594    }
595
596    /// 设置多语言实例 — 对齐 PHP `setLang(Lang $lang)`
597    ///
598    /// 对齐 PHP `Validate.php` 第 252-255 行
599    ///
600    /// ## PHP 行为
601    ///
602    /// ```php
603    /// public function setLang(Lang $lang)
604    /// {
605    ///     $this->lang = $lang;
606    /// }
607    /// ```
608    ///
609    /// ## 参数
610    ///
611    /// - `lang`:多语言实例(`Arc<dyn Lang>`)
612    ///
613    /// ## 用法
614    ///
615    /// ```ignore
616    /// use std::sync::Arc;
617    /// use sz_rust_infra_facade::validate::Validate;
618    /// use sz_rust_infra_facade::validate::message::{Lang, SimpleLang};
619    ///
620    /// let lang: Arc<dyn Lang> = Arc::new(
621    ///     SimpleLang::new().set("not conform to the rules", "不符合规则")
622    /// );
623    /// let v = Validate::new().set_lang(lang);
624    /// ```
625    pub fn set_lang(mut self, lang: Arc<dyn message::Lang>) -> Self {
626        self.lang = Some(lang);
627        self
628    }
629
630    // ========================================================================
631    // 数据自动验证
632    // ========================================================================
633
634    /// 数据自动验证 — 对齐 PHP `check(array $data, array $rules = [])`
635    ///
636    /// 对齐 PHP `Validate.php` 第 471-540 行
637    ///
638    /// ## 参数
639    ///
640    /// - `data`:待验证数据(JSON Object)
641    ///
642    /// ## 返回
643    ///
644    /// - `Ok(())`:所有规则通过
645    /// - `Err(ValidateError)`:验证失败
646    pub fn check(&mut self, data: &Value) -> Result<(), ValidateError> {
647        let mut batch_errors: IndexMap<String, String> = IndexMap::new();
648        let mut single_error: Option<String> = None;
649
650        // 处理场景(对齐 PHP $this->getScene,第 475-477 行 + 第 1659-1669 行)
651        // 完整对齐 PHP getScene,支持 sceneXxx 回调(回调优先于数组)
652        if let Some(scene_name) = self.current_scene.clone() {
653            // 重置 only/append/remove(对齐 PHP getScene 第 1661 行,R5-6)
654            self.only.clear();
655            self.append.clear();
656            self.remove.clear();
657            // 对齐 PHP getScene 第 1663-1668 行:
658            // - 如果存在 scene{Name} 回调(method_exists),调用回调
659            // - 否则如果 scene[{name}] 数组存在,设置 only
660            // 注:clone Arc 以避免借用冲突
661            if let Some(callback) = self.scene_callbacks.get(&scene_name).cloned() {
662                callback(self);
663            } else if let Some(fields) = self.scene.get(&scene_name) {
664                self.only = fields.clone();
665            }
666        }
667
668        // 收集规则快照(避免借用问题)
669        let rules: Vec<(String, Rule)> = self
670            .rule
671            .iter()
672            .map(|(k, v)| (k.clone(), v.clone()))
673            .collect();
674        let append_clone = self.append.clone();
675
676        for (key, rule) in &rules {
677            // 解析 field|title 格式(对齐 PHP 第 493-498 行)
678            let (field_name, title) = if let Some(idx) = key.find('|') {
679                (key[..idx].to_string(), key[idx + 1..].to_string())
680            } else {
681                let title = self.field.get(key).cloned().unwrap_or_else(|| key.clone());
682                (key.clone(), title)
683            };
684
685            // 场景过滤(对齐 PHP 第 501-503 行)
686            if !self.only.is_empty() && !self.only.contains(&field_name) {
687                continue;
688            }
689
690            // 获取字段值
691            let value = Self::get_data_value(data, &field_name);
692
693            // 检查规则
694            let result = self.check_item(&field_name, &value, rule, data, &title, &[]);
695
696            if let Err(msg) = result {
697                if self.batch {
698                    batch_errors.insert(field_name, msg);
699                } else {
700                    single_error = Some(msg);
701                    break;
702                }
703            }
704        }
705
706        // 处理 append 中未在 rule 里的字段(对齐 PHP 第 484-489 行)
707        let _ = &append_clone; // 已在 check_item 内处理
708
709        if self.batch {
710            if batch_errors.is_empty() {
711                Ok(())
712            } else {
713                self.error = ValidateError::Batch(batch_errors);
714                Err(self.error.clone())
715            }
716        } else {
717            match single_error {
718                None => Ok(()),
719                Some(msg) => {
720                    self.error = ValidateError::Single(msg);
721                    Err(self.error.clone())
722                }
723            }
724        }
725    }
726
727    /// 根据验证规则验证数据 — 对齐 PHP `checkRule($value, $rules)`
728    ///
729    /// 对齐 PHP `Validate.php` 第 549-581 行
730    ///
731    /// ## 参数
732    ///
733    /// - `value`:字段值
734    /// - `rules`:验证规则(字符串形式,如 `"require|in:1,2,3"`)
735    ///
736    /// ## 返回
737    ///
738    /// - `Ok(())`:所有规则通过
739    /// - `Err(String)`:验证失败,包含错误信息
740    pub fn check_rule(&self, value: &Value, rules: &str) -> Result<(), String> {
741        let rule = Rule::from_string(rules);
742        let empty_data = Value::Null;
743        match self.check_item("", value, &rule, &empty_data, "", &[]) {
744            Ok(()) => Ok(()),
745            Err(msg) => Err(msg),
746        }
747    }
748
749    /// 验证单个字段规则 — 对齐 PHP `checkItem`
750    ///
751    /// 对齐 PHP `Validate.php` 第 594-669 行
752    fn check_item(
753        &self,
754        field: &str,
755        value: &Value,
756        rules: &Rule,
757        data: &Value,
758        title: &str,
759        msg: &[String],
760    ) -> Result<(), String> {
761        // remove[field] === None(移除所有)&& append[field] 不存在 → 跳过
762        // 对齐 PHP 第 596-599 行
763        if matches!(self.remove.get(field), Some(None)) && !self.append.contains_key(field) {
764            return Ok(());
765        }
766
767        // 转为规则列表(对齐 PHP 第 602-604 行 explode('|', $rules))
768        let mut rule_list = rules.to_list();
769
770        // 合并 append[field](对齐 PHP 第 606-610 行)
771        if let Some(append_rules) = self.append.get(field) {
772            for ar in append_rules {
773                let extra = Rule::from_string(ar).to_list();
774                for e in extra {
775                    if !rule_list.contains(&e) {
776                        rule_list.push(e);
777                    }
778                }
779            }
780        }
781
782        if rule_list.is_empty() {
783            return Ok(());
784        }
785
786        for (i, (rule_type, rule_args)) in rule_list.iter().enumerate() {
787            // 获取验证类型(对齐 PHP getValidateType)
788            let (cb_type, args, info) = Self::get_validate_type(rule_type, rule_args);
789
790            // 检查 remove/append(对齐 PHP 第 625-630 行)
791            let in_append = self
792                .append
793                .get(field)
794                .map(|a| a.iter().any(|x| x == &info))
795                .unwrap_or(false);
796            let in_remove = self
797                .remove
798                .get(field)
799                .and_then(|r| r.as_ref())
800                .map(|r| r.iter().any(|x| x == &info))
801                .unwrap_or(false);
802            if !in_append && in_remove {
803                continue;
804            }
805
806            // 执行验证
807            let result = if let Some(cb) = self.type_callbacks.get(&cb_type) {
808                // 注册的自定义规则(对齐 PHP 第 632-634 行)
809                cb(value, &args, data)
810            } else if info == "must"
811                || info.starts_with("require")
812                || (!value.is_null() && !is_empty_string(value))
813            {
814                // 内置规则调用(对齐 PHP 第 634-635 行)
815                self.dispatch_builtin(&cb_type, value, &args, data, field, title)
816            } else {
817                // 空值跳过(对齐 PHP 第 636-637 行,R5-5)
818                true
819            };
820
821            if !result {
822                // 验证失败,生成错误消息(对齐 PHP 第 642-652 行)
823                let message = if i < msg.len() && !msg[i].is_empty() {
824                    msg[i].clone()
825                } else {
826                    self.get_rule_msg(field, title, &info, &args)
827                };
828                return Err(message);
829            }
830        }
831
832        Ok(())
833    }
834
835    /// 内置规则分发器
836    ///
837    /// 对齐 PHP `$this->$type($value, $rule, $data, $field, $title)` 调用
838    fn dispatch_builtin(
839        &self,
840        type_name: &str,
841        value: &Value,
842        rule: &str,
843        data: &Value,
844        field: &str,
845        _title: &str,
846    ) -> bool {
847        match type_name {
848            "require" => Self::require(value, rule),
849            "must" => Self::must(value, rule),
850            "is" => {
851                // 对齐 PHP `is` 方法 default 分支(第 870-884 行):
852                // 先检查 type_callbacks 中是否注册了 rule 对应的回调
853                if let Some(cb) = self.type_callbacks.get(rule) {
854                    return cb(value, "", data);
855                }
856                Self::is(value, rule, data)
857            }
858            "regex" => Self::regex_validate(value, rule, &self.regex),
859            // 比较类规则(对齐 PHP eq/gt/egt/lt/elt/confirm/different)
860            "eq" => rules::eq(value, rule, data, field),
861            "gt" => rules::gt(value, rule, data, field),
862            "egt" => rules::egt(value, rule, data, field),
863            "lt" => rules::lt(value, rule, data, field),
864            "elt" => rules::elt(value, rule, data, field),
865            "confirm" => rules::confirm(value, rule, data, field),
866            "different" => rules::different(value, rule, data, field),
867            // 范围类规则
868            "in" => rules::in_rule(value, rule, data, field),
869            "notIn" => rules::not_in(value, rule, data, field),
870            "between" => rules::between(value, rule, data, field),
871            "notBetween" => rules::not_between(value, rule, data, field),
872            // 长度类规则
873            "length" => rules::length(value, rule, data, field),
874            "max" => rules::max(value, rule, data, field),
875            "min" => rules::min(value, rule, data, field),
876            // 日期类规则
877            "dateFormat" => rules::date_format(value, rule, data, field),
878            "after" => rules::after(value, rule, data, field),
879            "before" => rules::before(value, rule, data, field),
880            "afterWith" => rules::after_with(value, rule, data, field),
881            "beforeWith" => rules::before_with(value, rule, data, field),
882            "expire" => rules::expire(value, rule, data, field),
883            // 条件必须类规则
884            "requireIf" => rules::require_if(value, rule, data, field),
885            "requireWith" => rules::require_with(value, rule, data, field),
886            "requireWithout" => rules::require_without(value, rule, data, field),
887            // IP 类规则
888            "ip" => rules::ip(value, rule, data, field),
889            "allowIp" => rules::allow_ip(value, rule, data, field),
890            "denyIp" => rules::deny_ip(value, rule, data, field),
891            // 域名类规则
892            "activeUrl" => rules::active_url(value, rule, data, field),
893            _ => {
894                // 未知类型默认通过(对齐 PHP method_exists 检查失败时的行为)
895                true
896            }
897        }
898    }
899
900    // ========================================================================
901    // 数据值获取
902    // ========================================================================
903
904    /// 获取数据值 — 对齐 PHP `getDataValue`
905    ///
906    /// 对齐 PHP `Validate.php` 第 1536-1554 行
907    ///
908    /// ## PHP 行为(R5-3)
909    ///
910    /// - 数值型 key:返回 key 本身(PHP 怪异行为,复刻)
911    /// - 包含 `.` 的 key:按多维数组访问
912    /// - 其他 key:返回 `data[key]` 或 null
913    pub fn get_data_value(data: &Value, key: &str) -> Value {
914        // 数值型 key 返回 key 本身(PHP 怪异行为,R5-3)
915        if key.parse::<i64>().is_ok() || key.parse::<f64>().is_ok() {
916            return Value::String(key.to_string());
917        }
918        // 多维数组访问
919        if key.contains('.') {
920            let mut current = data;
921            for part in key.split('.') {
922                match current.get(part) {
923                    Some(v) => current = v,
924                    None => return Value::Null,
925                }
926            }
927            return current.clone();
928        }
929        // 普通 key
930        data.get(key).cloned().unwrap_or(Value::Null)
931    }
932
933    // ========================================================================
934    // 验证类型解析
935    // ========================================================================
936
937    /// 获取当前验证类型及规则 — 对齐 PHP `getValidateType`
938    ///
939    /// 对齐 PHP `Validate.php` 第 678-706 行
940    ///
941    /// ## PHP 行为(R5-4)
942    ///
943    /// - 别名映射(`>` → `gt`,`>=` → `egt` 等)
944    /// - 返回 `(type, args, info)`:
945    ///   - `type`:用于分发的回调名(考虑别名)
946    ///   - `args`:规则参数
947    ///   - `info`:原始规则名(用于 remove/append 匹配)
948    pub fn get_validate_type(rule_type: &str, rule_args: &str) -> (String, String, String) {
949        // 别名解析(对齐 PHP 第 682-685 行)
950        let resolved_type = if let Some(&alias) = ALIAS.get(rule_type) {
951            alias.to_string()
952        } else {
953            rule_type.to_string()
954        };
955
956        // 对齐 PHP getValidateType 第 689-705 行(数字 key 分支)
957        // PHP method_exists 检查:Validate 类中存在的方法列表
958        // 这些方法在 PHP Validate 类中存在,可以直接调用
959        const PHP_METHODS: &[&str] = &[
960            "must",
961            "is",
962            "confirm",
963            "different",
964            "egt",
965            "gt",
966            "elt",
967            "lt",
968            "eq",
969            "activeUrl",
970            "ip",
971            "dateFormat",
972            "requireIf",
973            "requireCallback",
974            "requireWith",
975            "requireWithout",
976            "in",
977            "notIn",
978            "between",
979            "notBetween",
980            "length",
981            "max",
982            "min",
983            "after",
984            "before",
985            "afterWith",
986            "beforeWith",
987            "expire",
988            "allowIp",
989            "denyIp",
990            "regex",
991        ];
992
993        if !rule_args.is_empty() {
994            // 有参数规则(对齐 PHP 第 689-695 行,如 "in:1,2,3" → type="in", args="1,2,3", info="in")
995            (resolved_type.clone(), rule_args.to_string(), resolved_type)
996        } else if PHP_METHODS.contains(&resolved_type.as_str()) {
997            // 规则名匹配类方法(对齐 PHP 第 696-699 行 method_exists 分支)
998            (resolved_type.clone(), String::new(), resolved_type)
999        } else {
1000            // 默认走 is 方法(对齐 PHP 第 700-703 行,如 "require"/"integer"/"email" 等)
1001            ("is".to_string(), resolved_type.clone(), resolved_type)
1002        }
1003    }
1004
1005    // ========================================================================
1006    // 错误消息
1007    // ========================================================================
1008
1009    /// 获取验证规则的错误提示信息 — 对齐 PHP `getRuleMsg`
1010    ///
1011    /// 对齐 PHP `Validate.php` 第 1565-1586 行
1012    ///
1013    /// ## PHP 查找优先级(R5-1)
1014    ///
1015    /// 1. `message[field.type]`
1016    /// 2. `message[field]`
1017    /// 3. `type_msg[type]`
1018    /// 4. 如果 type 以 `require` 开头,使用 `type_msg['require']`
1019    /// 5. 默认 `$title . $this->lang->get('not conform to the rules')`
1020    ///
1021    /// ## Lang 翻译
1022    ///
1023    /// 所有分支的 msg 在占位符替换前先经过 [`Self::parse_error_msg_with_lang`]
1024    /// 进行 Lang 翻译(对齐 PHP `parseErrorMsg` 第 1598-1602 行)。
1025    /// 默认分支直接调用 `lang->get('not conform to the rules')`(对齐 PHP
1026    /// 第 1578 行)。
1027    pub fn get_rule_msg(&self, field: &str, title: &str, type_name: &str, rule: &str) -> String {
1028        // 1. message[field.type]
1029        let key1 = format!("{}.{}", field, type_name);
1030        if let Some(msg) = self.message.get(&key1) {
1031            return self.parse_error_msg_with_lang(msg, rule, title);
1032        }
1033        // 2. message[field]
1034        if let Some(msg) = self.message.get(field) {
1035            return self.parse_error_msg_with_lang(msg, rule, title);
1036        }
1037        // 3. type_msg[type]
1038        if let Some(&msg) = TYPE_MSG.get(type_name) {
1039            return self.parse_error_msg_with_lang(msg, rule, title);
1040        }
1041        // 4. require 前缀回退(对齐 PHP 第 1575-1576 行)
1042        if type_name.starts_with("require") {
1043            if let Some(&msg) = TYPE_MSG.get("require") {
1044                return self.parse_error_msg_with_lang(msg, rule, title);
1045            }
1046        }
1047        // 5. 默认(对齐 PHP 第 1578 行:$title . $this->lang->get('not conform to the rules'))
1048        // PHP Lang::get 找不到时返回 name 本身,所以无 Lang 时为 "not conform to the rules"
1049        let suffix = if let Some(lang) = &self.lang {
1050            lang.get("not conform to the rules")
1051        } else {
1052            "not conform to the rules".to_string()
1053        };
1054        format!("{}{}", title, suffix)
1055    }
1056
1057    /// 解析错误提示(含 Lang 翻译) — 对齐 PHP `parseErrorMsg`
1058    ///
1059    /// 对齐 PHP `Validate.php` 第 1596-1633 行
1060    ///
1061    /// ## PHP 行为
1062    ///
1063    /// 1. **Lang 翻译**(第 1598-1602 行,R5-7):
1064    ///    - `{%var}` 语法:`lang->get(substr($msg, 2, -1))`
1065    ///    - `lang->has($msg)`:`lang->get($msg)`
1066    /// 2. **占位符替换**(第 1613-1630 行,R5-2):
1067    ///    - `:attribute` → title
1068    ///    - `:1` / `:2` / `:3` → rule 按逗号分割后的前 3 个元素
1069    ///    - `:rule` → rule 原值(仅当 msg 包含 `:rule` 时)
1070    ///
1071    /// ## 无 Lang 实例时的行为
1072    ///
1073    /// 当 `Validate::lang` 为 `None` 时跳过翻译,直接执行占位符替换。
1074    /// 对齐 PHP 未注入 Lang 时的行为(PHP 中 `$this->lang` 必须存在,否则
1075    /// `parseErrorMsg` 会致命错误;Rust 使用 `Option` 提供更安全的降级)。
1076    pub fn parse_error_msg_with_lang(&self, msg: &str, rule: &str, title: &str) -> String {
1077        // 先 Lang 翻译,再占位符替换(对齐 PHP 第 1598-1602 行)
1078        let translated = message::translate_msg(msg, self.lang.as_ref());
1079        Self::parse_error_msg(&translated, rule, title)
1080    }
1081
1082    /// 解析错误提示 — 对齐 PHP `parseErrorMsg` 占位符替换部分
1083    ///
1084    /// 对齐 PHP `Validate.php` 第 1613-1630 行(不含 Lang 翻译)
1085    ///
1086    /// ## PHP 占位符替换(R5-2)
1087    ///
1088    /// 1. `:attribute` → title
1089    /// 2. `:1` / `:2` / `:3` → rule 按逗号分割后的前 3 个元素
1090    /// 3. `:rule` → rule 原值(仅当 msg 包含 `:rule` 时)
1091    ///
1092    /// ## 说明
1093    ///
1094    /// 本方法为静态方法,不包含 Lang 翻译。如需 Lang 翻译,请使用
1095    /// [`Self::parse_error_msg_with_lang`] 实例方法。
1096    pub fn parse_error_msg(msg: &str, rule: &str, title: &str) -> String {
1097        let mut result = msg.to_string();
1098
1099        // 仅当 msg 包含 `:` 时执行替换(对齐 PHP 第 1613 行)
1100        if !result.contains(':') {
1101            return result;
1102        }
1103
1104        // 将 rule 按逗号分割为前 3 个元素(对齐 PHP 第 1615-1619 行)
1105        let parts: Vec<&str> = if rule.contains(',') {
1106            let split: Vec<&str> = rule.split(',').collect();
1107            split
1108        } else {
1109            vec!["", "", ""]
1110        };
1111        let p1 = parts.first().copied().unwrap_or("");
1112        let p2 = parts.get(1).copied().unwrap_or("");
1113        let p3 = parts.get(2).copied().unwrap_or("");
1114
1115        // 替换 :attribute, :1, :2, :3(对齐 PHP 第 1621-1625 行)
1116        result = result.replace(":attribute", title);
1117        result = result.replace(":1", p1);
1118        result = result.replace(":2", p2);
1119        result = result.replace(":3", p3);
1120
1121        // 替换 :rule(对齐 PHP 第 1627-1629 行,仅当 msg 包含 :rule 时)
1122        if result.contains(":rule") {
1123            result = result.replace(":rule", rule);
1124        }
1125
1126        result
1127    }
1128
1129    /// 获取错误信息 — 对齐 PHP `getError()`
1130    pub fn get_error(&self) -> &ValidateError {
1131        &self.error
1132    }
1133
1134    // ========================================================================
1135    // 内置规则(基础)
1136    // ========================================================================
1137
1138    /// 必须验证 — 对齐 PHP `require`
1139    ///
1140    /// 对齐 PHP `Validate.php` 第 814-817 行(实际由 `is` 处理 require)
1141    ///
1142    /// ## 行为
1143    ///
1144    /// - `null` → `false`
1145    /// - 空字符串 `""` → `false`
1146    /// - 字符串 `"0"` → `true`(PHP 特殊行为)
1147    /// - 其他非空值 → `true`
1148    pub fn require(value: &Value, _rule: &str) -> bool {
1149        // 对齐 PHP `!empty($value) || '0' == $value`
1150        // 字符串 "0" 在 PHP empty() 中被视为空,但 require 将其视为非空
1151        !is_empty_value(value) || matches!(value, Value::String(s) if s == "0")
1152    }
1153
1154    /// 必须验证(与 require 等价) — 对齐 PHP `must`
1155    ///
1156    /// 对齐 PHP `Validate.php` 第 814-817 行
1157    pub fn must(value: &Value, _rule: &str) -> bool {
1158        !is_empty_value(value) || matches!(value, Value::String(s) if s == "0")
1159    }
1160
1161    /// 验证字段值是否为有效格式 — 对齐 PHP `is`
1162    ///
1163    /// 对齐 PHP `Validate.php` 第 827-888 行
1164    ///
1165    /// ## 支持的类型
1166    ///
1167    /// - `require`:必须
1168    /// - `accepted`:接受(`1`/`on`/`yes`)
1169    /// - `date`:有效日期
1170    /// - `boolean`/`bool`:布尔值
1171    /// - `number`:数字
1172    /// - `integer`:整数
1173    /// - `float`:浮点数
1174    /// - `alpha`:字母
1175    /// - `alphaNum`:字母数字
1176    /// - `alphaDash`:字母数字下划线短横线
1177    /// - `chs`:中文
1178    /// - `chsAlpha`:中文或字母
1179    /// - `chsAlphaNum`:中文或字母数字
1180    /// - `chsDash`:中文字母数字下划线短横线
1181    /// - `mobile`:手机号
1182    /// - `email`:邮箱
1183    /// - `url`:URL
1184    /// - `ip`:IP 地址
1185    /// - `macAddr`:MAC 地址
1186    /// - `array`:数组
1187    pub fn is(value: &Value, rule: &str, _data: &Value) -> bool {
1188        let rule = rule.trim();
1189        match rule {
1190            "require" => {
1191                // 对齐 PHP `is` 方法 require 分支:`!empty($value) || '0' == $value`
1192                !is_empty_value(value) || matches!(value, Value::String(s) if s == "0")
1193            }
1194            "accepted" => {
1195                matches!(
1196                    value,
1197                    Value::Number(n) if n.as_i64() == Some(1)
1198                ) || matches!(value, Value::String(s) if s == "1" || s == "on" || s == "yes")
1199            }
1200            "date" => is_valid_date(value),
1201            "boolean" | "bool" => {
1202                matches!(value, Value::Bool(_) | Value::Null)
1203                    || matches!(value, Value::Number(n) if n.as_i64() == Some(0) || n.as_i64() == Some(1))
1204                    || matches!(value, Value::String(s) if s == "0" || s == "1")
1205            }
1206            "number" => {
1207                value.is_number() || matches!(value, Value::String(s) if s.parse::<f64>().is_ok())
1208            }
1209            "integer" => {
1210                value.is_i64() || matches!(value, Value::String(s) if s.parse::<i64>().is_ok())
1211            }
1212            "float" => {
1213                // 对齐 PHP filter_var($value, FILTER_VALIDATE_FLOAT) — 接受整数和浮点数
1214                value.is_number() || matches!(value, Value::String(s) if s.parse::<f64>().is_ok())
1215            }
1216            "array" => value.is_array(),
1217            "email" => is_valid_email(value),
1218            "url" => is_valid_url(value),
1219            "ip" => is_valid_ip(value),
1220            "macAddr" => is_valid_mac(value),
1221            // ctype / 正则类规则
1222            "alpha" => regex_match_default(value, "alpha"),
1223            "alphaNum" => regex_match_default(value, "alphaNum"),
1224            "alphaDash" => regex_match_default(value, "alphaDash"),
1225            "chs" => regex_match_default(value, "chs"),
1226            "chsAlpha" => regex_match_default(value, "chsAlpha"),
1227            "chsAlphaNum" => regex_match_default(value, "chsAlphaNum"),
1228            "chsDash" => regex_match_default(value, "chsDash"),
1229            "mobile" => regex_match_default(value, "mobile"),
1230            "idCard" => regex_match_default(value, "idCard"),
1231            "zip" => regex_match_default(value, "zip"),
1232            _ => {
1233                // 未知类型默认通过(对齐 PHP default 分支的正则匹配行为)
1234                // rules.rs 中已添加更多类型
1235                true
1236            }
1237        }
1238    }
1239
1240    /// 正则验证 — 对齐 PHP `regex`
1241    ///
1242    /// 对齐 PHP `Validate.php` 第 1504-1518 行
1243    pub fn regex_validate(
1244        value: &Value,
1245        rule: &str,
1246        custom_regex: &IndexMap<String, String>,
1247    ) -> bool {
1248        // 查找自定义正则
1249        let pattern = if let Some(p) = custom_regex.get(rule) {
1250            p.clone()
1251        } else if let Some(&p) = DEFAULT_REGEX.get(rule) {
1252            p.to_string()
1253        } else {
1254            // 不是预定义正则,按 PHP 规则补上 /^...$/
1255            // 对齐 PHP 第 1512-1515 行
1256            if rule.starts_with('/') {
1257                rule.to_string()
1258            } else {
1259                format!("/^{}/$", rule)
1260            }
1261        };
1262
1263        // 仅标量值可正则匹配(对齐 PHP 第 1517 行 is_scalar 检查)
1264        let s = match value {
1265            Value::String(s) => s.clone(),
1266            Value::Number(n) => n.to_string(),
1267            Value::Bool(b) => b.to_string(),
1268            _ => return false,
1269        };
1270
1271        // 处理 PHP 正则的 /u 标志(Unicode)
1272        // Rust regex crate 默认就是 Unicode 模式
1273        let rust_pattern = php_regex_to_rust(&pattern);
1274        match Regex::new(&rust_pattern) {
1275            Ok(re) => re.is_match(&s),
1276            Err(_) => false,
1277        }
1278    }
1279}
1280
1281// ============================================================================
1282// 内部辅助函数
1283// ============================================================================
1284
1285/// 判断值是否为空(对齐 PHP `empty()`)
1286///
1287/// PHP `empty()` 对以下值返回 true:
1288/// - `null`、`false`、`""`、`"0"`、`0`、`0.0`、`[]`
1289///
1290/// **注意**:PHP `is` 方法的 `require` 分支使用 `!empty($value) || '0' == $value`,
1291/// 即字符串 `"0"` 被视为非空(PHP 特殊行为)。
1292pub(crate) fn is_empty_value(value: &Value) -> bool {
1293    match value {
1294        Value::Null => true,
1295        Value::Bool(b) => !b,
1296        Value::String(s) => s.is_empty() || s == "0",
1297        Value::Number(n) => n.as_f64().map(|f| f == 0.0).unwrap_or(true),
1298        Value::Array(a) => a.is_empty(),
1299        Value::Object(o) => o.is_empty(),
1300    }
1301}
1302
1303/// 判断值是否为空字符串(对齐 PHP `'' !== $value`)
1304///
1305/// 仅判断空字符串,不判断其他空值(与 `is_empty_value` 区分)。
1306fn is_empty_string(value: &Value) -> bool {
1307    matches!(value, Value::String(s) if s.is_empty())
1308}
1309
1310/// 验证日期格式(对齐 PHP `strtotime`)
1311fn is_valid_date(value: &Value) -> bool {
1312    let s = match value {
1313        Value::String(s) => s,
1314        _ => return false,
1315    };
1316    // 尝试常见日期格式解析
1317    // 对齐 PHP `strtotime()` 的宽松行为
1318    use chrono::{DateTime, NaiveDate, NaiveDateTime};
1319    if DateTime::parse_from_rfc3339(s).is_ok() {
1320        return true;
1321    }
1322    if NaiveDateTime::parse_from_str(s, "%Y-%m-%d %H:%M:%S").is_ok() {
1323        return true;
1324    }
1325    if NaiveDate::parse_from_str(s, "%Y-%m-%d").is_ok() {
1326        return true;
1327    }
1328    if NaiveDateTime::parse_from_str(s, "%Y/%m/%d %H:%M:%S").is_ok() {
1329        return true;
1330    }
1331    if NaiveDate::parse_from_str(s, "%Y/%m/%d").is_ok() {
1332        return true;
1333    }
1334    false
1335}
1336
1337/// 验证邮箱格式(对齐 PHP `FILTER_VALIDATE_EMAIL`)
1338fn is_valid_email(value: &Value) -> bool {
1339    let s = match value {
1340        Value::String(s) => s,
1341        _ => return false,
1342    };
1343    // 简化版邮箱正则(PHP filter_var 更宽松)
1344    let email_re = Lazy::new(|| {
1345        Regex::new(r"^[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}$")
1346            .expect("内置正则表达式编译失败")
1347    });
1348    email_re.is_match(s)
1349}
1350
1351/// 验证 URL 格式(对齐 PHP `FILTER_VALIDATE_URL`)
1352fn is_valid_url(value: &Value) -> bool {
1353    let s = match value {
1354        Value::String(s) => s,
1355        _ => return false,
1356    };
1357    // 必须包含 scheme
1358    s.starts_with("http://")
1359        || s.starts_with("https://")
1360        || s.starts_with("ftp://")
1361        || s.starts_with("ftps://")
1362        || s.starts_with("mailto:")
1363        || s.starts_with("tel:")
1364        || s.starts_with("file://")
1365}
1366
1367/// 验证 IP 地址(对齐 PHP `FILTER_VALIDATE_IP`,支持 IPv4 和 IPv6)
1368fn is_valid_ip(value: &Value) -> bool {
1369    let s = match value {
1370        Value::String(s) => s,
1371        Value::Number(n) => {
1372            // 数字不是有效 IP
1373            let _ = n;
1374            return false;
1375        }
1376        _ => return false,
1377    };
1378    use std::net::IpAddr;
1379    s.parse::<IpAddr>().is_ok()
1380}
1381
1382/// 验证 MAC 地址(对齐 PHP `FILTER_VALIDATE_MAC`)
1383fn is_valid_mac(value: &Value) -> bool {
1384    let s = match value {
1385        Value::String(s) => s,
1386        _ => return false,
1387    };
1388    let mac_re = Lazy::new(|| {
1389        Regex::new(r"^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$").expect("内置正则表达式编译失败")
1390    });
1391    mac_re.is_match(s)
1392}
1393
1394/// 使用内置正则匹配(对齐 PHP `defaultRegex` 查找)
1395fn regex_match_default(value: &Value, regex_name: &str) -> bool {
1396    let s = match value {
1397        Value::String(s) => s.clone(),
1398        Value::Number(n) => n.to_string(),
1399        Value::Bool(b) => b.to_string(),
1400        _ => return false,
1401    };
1402    if let Some(&pattern) = DEFAULT_REGEX.get(regex_name) {
1403        let rust_pattern = php_regex_to_rust(pattern);
1404        match Regex::new(&rust_pattern) {
1405            Ok(re) => re.is_match(&s),
1406            Err(_) => false,
1407        }
1408    } else {
1409        false
1410    }
1411}
1412
1413/// 将 PHP 正则转换为 Rust regex crate 兼容格式
1414///
1415/// 主要差异:
1416/// - PHP `\x{4e00}` Unicode 转义 → Rust `\u{4e00}`
1417/// - PHP `/...$/u` Unicode 标志 → Rust 默认 Unicode
1418fn php_regex_to_rust(pattern: &str) -> String {
1419    let mut result = pattern.to_string();
1420    // 替换 \x{HEX} 为 \u{HEX}
1421    while let Some(start) = result.find("\\x{") {
1422        if let Some(end) = result[start..].find('}') {
1423            let hex = &result[start + 3..start + end];
1424            let replacement = format!("\\u{{{}}}", hex);
1425            result.replace_range(start..start + end + 1, &replacement);
1426        } else {
1427            break;
1428        }
1429    }
1430    // 移除 PHP 正则分隔符和标志(如 /.../u)
1431    // Rust regex 不使用分隔符
1432    if result.starts_with('/') && result.len() > 2 {
1433        let flags_start = result.rfind('/').unwrap_or(result.len() - 1);
1434        if flags_start > 0 {
1435            // 移除首尾 / 和标志(u, i, m, s, U 等)
1436            let _inner = &result[1..flags_start];
1437            let flags = &result[flags_start + 1..];
1438            // Rust 默认 Unicode,'u' 标志可忽略
1439            // 'i' 标志使用 (?i) 前缀
1440            let mut prefix = String::new();
1441            if flags.contains('i') {
1442                prefix.push_str("(?i)");
1443            }
1444            if flags.contains('m') {
1445                prefix.push_str("(?m)");
1446            }
1447            if flags.contains('s') {
1448                prefix.push_str("(?s)");
1449            }
1450            return format!("{}{}", prefix, _inner);
1451        }
1452    }
1453    result
1454}
1455
1456// ============================================================================
1457// 内联单元测试
1458// ============================================================================
1459
1460#[cfg(test)]
1461#[allow(clippy::approx_constant)]
1462mod tests {
1463    use super::*;
1464    use serde_json::json;
1465
1466    // ========================================================================
1467    // 组 1:ValidateError 类型测试
1468    // ========================================================================
1469
1470    #[test]
1471    fn test_validate_error_single_display() {
1472        let err = ValidateError::Single("name require".to_string());
1473        assert_eq!(format!("{}", err), "name require");
1474    }
1475
1476    #[test]
1477    fn test_validate_error_batch_display() {
1478        let mut errors = IndexMap::new();
1479        errors.insert("name".to_string(), "name require".to_string());
1480        errors.insert("age".to_string(), "age must be integer".to_string());
1481        let err = ValidateError::Batch(errors);
1482        let displayed = format!("{}", err);
1483        assert!(displayed.contains("name: name require"));
1484        assert!(displayed.contains("age: age must be integer"));
1485    }
1486
1487    #[test]
1488    fn test_validate_error_clone_eq() {
1489        let err1 = ValidateError::Single("test".to_string());
1490        let err2 = err1.clone();
1491        assert_eq!(err1, err2);
1492    }
1493
1494    // ========================================================================
1495    // 组 2:Rule 类型测试
1496    // ========================================================================
1497
1498    #[test]
1499    fn test_rule_from_string_simple() {
1500        let r = Rule::from_string("require");
1501        match r {
1502            Rule::Simple(s) => assert_eq!(s, "require"),
1503            _ => panic!("expected Simple"),
1504        }
1505    }
1506
1507    #[test]
1508    fn test_rule_from_string_with_args() {
1509        let r = Rule::from_string("in:1,2,3");
1510        match r {
1511            Rule::WithArgs(t, a) => {
1512                assert_eq!(t, "in");
1513                assert_eq!(a, "1,2,3");
1514            }
1515            _ => panic!("expected WithArgs"),
1516        }
1517    }
1518
1519    #[test]
1520    fn test_rule_from_string_multiple() {
1521        let r = Rule::from_string("require|in:1,2,3");
1522        match r {
1523            Rule::Multiple(list) => {
1524                assert_eq!(list.len(), 2);
1525                assert!(matches!(list[0], Rule::Simple(ref s) if s == "require"));
1526                assert!(
1527                    matches!(list[1], Rule::WithArgs(ref t, ref a) if t == "in" && a == "1,2,3")
1528                );
1529            }
1530            _ => panic!("expected Multiple"),
1531        }
1532    }
1533
1534    #[test]
1535    fn test_rule_to_list_simple() {
1536        let r = Rule::Simple("require".to_string());
1537        let list = r.to_list();
1538        assert_eq!(list, vec![("require".to_string(), String::new())]);
1539    }
1540
1541    #[test]
1542    fn test_rule_to_list_multiple() {
1543        let r = Rule::from_string("require|in:1,2,3|email");
1544        let list = r.to_list();
1545        assert_eq!(list.len(), 3);
1546        assert_eq!(list[0], ("require".to_string(), String::new()));
1547        assert_eq!(list[1], ("in".to_string(), "1,2,3".to_string()));
1548        assert_eq!(list[2], ("email".to_string(), String::new()));
1549    }
1550
1551    // ========================================================================
1552    // 组 3:Builder 方法测试
1553    // ========================================================================
1554
1555    #[test]
1556    fn test_validate_new_empty() {
1557        let v = Validate::new();
1558        assert!(!v.batch);
1559        assert!(v.current_scene.is_none());
1560        assert!(v.rule.is_empty());
1561        assert!(v.message.is_empty());
1562        assert!(v.field.is_empty());
1563        assert!(v.scene.is_empty());
1564        assert!(v.only.is_empty());
1565        assert!(v.remove.is_empty());
1566        assert!(v.append.is_empty());
1567        assert!(v.type_callbacks.is_empty());
1568    }
1569
1570    #[test]
1571    fn test_validate_rule_builder() {
1572        let v = Validate::new()
1573            .rule("name", "require")
1574            .rule("age", "integer");
1575        assert_eq!(v.rule.len(), 2);
1576        assert!(v.rule.contains_key("name"));
1577        assert!(v.rule.contains_key("age"));
1578    }
1579
1580    #[test]
1581    fn test_validate_message_builder() {
1582        let mut msgs = IndexMap::new();
1583        msgs.insert("name.require".to_string(), "名称必须".to_string());
1584        let v = Validate::new().message(msgs);
1585        assert_eq!(v.message.get("name.require"), Some(&"名称必须".to_string()));
1586    }
1587
1588    #[test]
1589    fn test_validate_field_builder() {
1590        let mut fields = IndexMap::new();
1591        fields.insert("name".to_string(), "名称".to_string());
1592        let v = Validate::new().field(fields);
1593        assert_eq!(v.field.get("name"), Some(&"名称".to_string()));
1594    }
1595
1596    #[test]
1597    fn test_validate_scene_builder() {
1598        let v = Validate::new()
1599            .register_scene(
1600                "login",
1601                vec!["username".to_string(), "password".to_string()],
1602            )
1603            .scene("login");
1604        assert_eq!(v.current_scene, Some("login".to_string()));
1605        assert!(v.has_scene("login"));
1606        assert!(!v.has_scene("register"));
1607    }
1608
1609    #[test]
1610    fn test_validate_batch_builder() {
1611        let v = Validate::new().batch(true);
1612        assert!(v.batch);
1613    }
1614
1615    #[test]
1616    fn test_validate_only_builder() {
1617        let v = Validate::new().only(vec!["name".to_string()]);
1618        assert_eq!(v.only, vec!["name".to_string()]);
1619    }
1620
1621    #[test]
1622    fn test_validate_remove_builder() {
1623        let v = Validate::new()
1624            .remove("name", None)
1625            .remove("age", Some(vec!["integer".to_string()]));
1626        assert!(matches!(v.remove.get("name"), Some(None)));
1627        assert!(
1628            matches!(v.remove.get("age"), Some(Some(ref r)) if r == &vec!["integer".to_string()])
1629        );
1630    }
1631
1632    #[test]
1633    fn test_validate_append_builder() {
1634        let v = Validate::new().append("name", vec!["email".to_string()]);
1635        assert_eq!(v.append.get("name"), Some(&vec!["email".to_string()]));
1636    }
1637
1638    #[test]
1639    fn test_validate_regex_builder() {
1640        let v = Validate::new().regex("custom", r"^\d{4}$");
1641        assert_eq!(v.regex.get("custom"), Some(&r"^\d{4}$".to_string()));
1642    }
1643
1644    // ========================================================================
1645    // 组 4:静态映射测试
1646    // ========================================================================
1647
1648    #[test]
1649    fn test_static_default_regex_contains_all() {
1650        // 对齐 PHP $defaultRegex 第 125-136 行
1651        assert!(DEFAULT_REGEX.contains_key("alpha"));
1652        assert!(DEFAULT_REGEX.contains_key("alphaNum"));
1653        assert!(DEFAULT_REGEX.contains_key("alphaDash"));
1654        assert!(DEFAULT_REGEX.contains_key("chs"));
1655        assert!(DEFAULT_REGEX.contains_key("chsAlpha"));
1656        assert!(DEFAULT_REGEX.contains_key("chsAlphaNum"));
1657        assert!(DEFAULT_REGEX.contains_key("chsDash"));
1658        assert!(DEFAULT_REGEX.contains_key("mobile"));
1659        assert!(DEFAULT_REGEX.contains_key("idCard"));
1660        assert!(DEFAULT_REGEX.contains_key("zip"));
1661    }
1662
1663    #[test]
1664    fn test_static_type_msg_contains_all() {
1665        // 对齐 PHP $typeMsg 第 62-113 行
1666        assert!(TYPE_MSG.contains_key("require"));
1667        assert!(TYPE_MSG.contains_key("must"));
1668        assert!(TYPE_MSG.contains_key("number"));
1669        assert!(TYPE_MSG.contains_key("email"));
1670        assert!(TYPE_MSG.contains_key("mobile"));
1671        assert!(TYPE_MSG.contains_key("in"));
1672        assert!(TYPE_MSG.contains_key("notIn"));
1673        assert!(TYPE_MSG.contains_key("between"));
1674        assert!(TYPE_MSG.contains_key("length"));
1675        assert!(TYPE_MSG.contains_key("max"));
1676        assert!(TYPE_MSG.contains_key("min"));
1677        assert!(TYPE_MSG.contains_key("eq"));
1678        assert!(TYPE_MSG.contains_key("gt"));
1679        assert!(TYPE_MSG.contains_key("egt"));
1680        assert!(TYPE_MSG.contains_key("lt"));
1681        assert!(TYPE_MSG.contains_key("elt"));
1682        assert!(TYPE_MSG.contains_key("confirm"));
1683        assert!(TYPE_MSG.contains_key("different"));
1684        assert!(TYPE_MSG.contains_key("regex"));
1685    }
1686
1687    #[test]
1688    fn test_static_alias_contains_all() {
1689        // 对齐 PHP $alias 第 36-38 行
1690        assert_eq!(ALIAS.get(">"), Some(&"gt"));
1691        assert_eq!(ALIAS.get(">="), Some(&"egt"));
1692        assert_eq!(ALIAS.get("<"), Some(&"lt"));
1693        assert_eq!(ALIAS.get("<="), Some(&"elt"));
1694        assert_eq!(ALIAS.get("="), Some(&"eq"));
1695        assert_eq!(ALIAS.get("same"), Some(&"eq"));
1696    }
1697
1698    // ========================================================================
1699    // 组 5:get_data_value 测试(R5-3)
1700    // ========================================================================
1701
1702    #[test]
1703    fn test_get_data_value_simple() {
1704        let data = json!({"name": "Alice", "age": 30});
1705        assert_eq!(Validate::get_data_value(&data, "name"), json!("Alice"));
1706        assert_eq!(Validate::get_data_value(&data, "age"), json!(30));
1707    }
1708
1709    #[test]
1710    fn test_get_data_value_missing_field() {
1711        let data = json!({"name": "Alice"});
1712        assert_eq!(Validate::get_data_value(&data, "missing"), Value::Null);
1713    }
1714
1715    #[test]
1716    fn test_get_data_value_nested_dot_notation() {
1717        // 对齐 PHP 多维数组访问(R5-3)
1718        let data = json!({"user": {"profile": {"age": 25}}});
1719        assert_eq!(
1720            Validate::get_data_value(&data, "user.profile.age"),
1721            json!(25)
1722        );
1723    }
1724
1725    #[test]
1726    fn test_get_data_value_nested_missing_intermediate() {
1727        let data = json!({"user": {"name": "Alice"}});
1728        assert_eq!(
1729            Validate::get_data_value(&data, "user.profile.age"),
1730            Value::Null
1731        );
1732    }
1733
1734    #[test]
1735    fn test_get_data_value_numeric_key_php_bug() {
1736        // R5-3:数值型 key 返回 key 本身(PHP 怪异行为,复刻)
1737        let data = json!({"123": "value"});
1738        assert_eq!(
1739            Validate::get_data_value(&data, "123"),
1740            json!("123") // 返回 key 字符串本身,不是 "value"
1741        );
1742    }
1743
1744    #[test]
1745    fn test_get_data_value_float_numeric_key() {
1746        // R5-3:浮点型 key 也返回 key 本身
1747        let data = json!({});
1748        assert_eq!(Validate::get_data_value(&data, "3.14"), json!("3.14"));
1749    }
1750
1751    // ========================================================================
1752    // 组 6:get_validate_type 测试(R5-4)
1753    // ========================================================================
1754
1755    #[test]
1756    fn test_get_validate_type_no_alias() {
1757        // PHP "require" 没有 method_exists,走 is 分支
1758        // type="is", args="require", info="require"
1759        let (t, a, i) = Validate::get_validate_type("require", "");
1760        assert_eq!(t, "is");
1761        assert_eq!(a, "require");
1762        assert_eq!(i, "require");
1763    }
1764
1765    #[test]
1766    fn test_get_validate_type_with_args() {
1767        let (t, a, i) = Validate::get_validate_type("in", "1,2,3");
1768        assert_eq!(t, "in");
1769        assert_eq!(a, "1,2,3");
1770        assert_eq!(i, "in");
1771    }
1772
1773    #[test]
1774    fn test_get_validate_type_alias_gt() {
1775        // 对齐 PHP 别名映射(R5-4)
1776        // ">" 无参数 → 别名解析为 "gt" → "gt" 在 PHP_METHODS 中(method_exists)
1777        // 对齐 PHP getValidateType 第 696-699 行:result = ['gt', '', 'gt']
1778        let (t, a, i) = Validate::get_validate_type(">", "");
1779        assert_eq!(t, "gt");
1780        assert_eq!(a, "");
1781        assert_eq!(i, "gt");
1782    }
1783
1784    #[test]
1785    fn test_get_validate_type_alias_same_to_eq() {
1786        // "same" 无参数 → 别名解析为 "eq" → "eq" 在 PHP_METHODS 中(method_exists)
1787        // 对齐 PHP getValidateType 第 696-699 行:result = ['eq', '', 'eq']
1788        let (t, a, i) = Validate::get_validate_type("same", "");
1789        assert_eq!(t, "eq");
1790        assert_eq!(a, "");
1791        assert_eq!(i, "eq");
1792    }
1793
1794    #[test]
1795    fn test_get_validate_type_must_method_exists() {
1796        // PHP "must" 有 method_exists,直接调用 must 方法
1797        let (t, a, i) = Validate::get_validate_type("must", "");
1798        assert_eq!(t, "must");
1799        assert_eq!(a, "");
1800        assert_eq!(i, "must");
1801    }
1802
1803    #[test]
1804    fn test_get_validate_type_integer_via_is() {
1805        // PHP "integer" 没有 method_exists,走 is 分支
1806        let (t, a, i) = Validate::get_validate_type("integer", "");
1807        assert_eq!(t, "is");
1808        assert_eq!(a, "integer");
1809        assert_eq!(i, "integer");
1810    }
1811
1812    // ========================================================================
1813    // 组 7:parse_error_msg 测试(R5-2)
1814    // ========================================================================
1815
1816    #[test]
1817    fn test_parse_error_msg_attribute_replacement() {
1818        let result = Validate::parse_error_msg(":attribute require", "", "名称");
1819        assert_eq!(result, "名称 require");
1820    }
1821
1822    #[test]
1823    fn test_parse_error_msg_rule_replacement() {
1824        let result = Validate::parse_error_msg(":attribute must be in :rule", "1,2,3", "状态");
1825        assert_eq!(result, "状态 must be in 1,2,3");
1826    }
1827
1828    #[test]
1829    fn test_parse_error_msg_numbered_replacement() {
1830        // 对齐 PHP :1, :2, :3 替换(R5-2)
1831        let result = Validate::parse_error_msg(":attribute must between :1 - :2", "10,20", "年龄");
1832        assert_eq!(result, "年龄 must between 10 - 20");
1833    }
1834
1835    #[test]
1836    fn test_parse_error_msg_no_colon_returns_as_is() {
1837        // 没有 : 的消息原样返回(对齐 PHP 第 1613 行)
1838        let result = Validate::parse_error_msg("access IP denied", "1.2.3.4", "ip");
1839        assert_eq!(result, "access IP denied");
1840    }
1841
1842    #[test]
1843    fn test_parse_error_msg_multiple_placeholders() {
1844        let result = Validate::parse_error_msg(
1845            ":attribute must equal :rule and between :1 - :2",
1846            "5,1,10",
1847            "值",
1848        );
1849        assert_eq!(result, "值 must equal 5,1,10 and between 5 - 1");
1850    }
1851
1852    // ========================================================================
1853    // 组 8:get_rule_msg 测试(R5-1 优先级链)
1854    // ========================================================================
1855
1856    #[test]
1857    fn test_get_rule_msg_priority_field_type() {
1858        // 优先级 1:message[field.type]
1859        let mut msgs = IndexMap::new();
1860        msgs.insert("name.require".to_string(), "名称必须填写".to_string());
1861        let v = Validate::new().message(msgs);
1862        let msg = v.get_rule_msg("name", "名称", "require", "");
1863        assert_eq!(msg, "名称必须填写");
1864    }
1865
1866    #[test]
1867    fn test_get_rule_msg_priority_field_only() {
1868        // 优先级 2:message[field]
1869        let mut msgs = IndexMap::new();
1870        msgs.insert("name".to_string(), "名称错误".to_string());
1871        let v = Validate::new().message(msgs);
1872        let msg = v.get_rule_msg("name", "名称", "require", "");
1873        assert_eq!(msg, "名称错误");
1874    }
1875
1876    #[test]
1877    fn test_get_rule_msg_priority_type_msg() {
1878        // 优先级 3:type_msg[type]
1879        let v = Validate::new();
1880        let msg = v.get_rule_msg("email_field", "邮箱", "email", "");
1881        assert_eq!(msg, "邮箱 not a valid email address");
1882    }
1883
1884    #[test]
1885    fn test_get_rule_msg_priority_require_prefix() {
1886        // 优先级 4:require 前缀回退到 type_msg['require']
1887        let v = Validate::new();
1888        let msg = v.get_rule_msg("field", "字段", "requireIf", "");
1889        assert_eq!(msg, "字段 require");
1890    }
1891
1892    #[test]
1893    fn test_get_rule_msg_default_fallback() {
1894        // 优先级 5:默认消息
1895        // 对齐 PHP 第 1578 行:$title . $this->lang->get('not conform to the rules')
1896        // PHP Lang::get 找不到时返回 name 本身,无 Lang 时为 "not conform to the rules"(无前导空格)
1897        let v = Validate::new();
1898        let msg = v.get_rule_msg("field", "字段", "unknownType", "");
1899        assert_eq!(msg, "字段not conform to the rules");
1900    }
1901
1902    // ========================================================================
1903    // 组 9:内置规则 require/must 测试
1904    // ========================================================================
1905
1906    #[test]
1907    fn test_require_non_empty_string() {
1908        assert!(Validate::require(&json!("hello"), ""));
1909        assert!(Validate::require(&json!("0"), "")); // PHP 特殊行为:"0" 被视为非空
1910    }
1911
1912    #[test]
1913    fn test_require_empty_values() {
1914        // 对齐 PHP empty() 行为
1915        assert!(!Validate::require(&Value::Null, ""));
1916        assert!(!Validate::require(&json!(""), ""));
1917        assert!(!Validate::require(&json!(0), ""));
1918        assert!(!Validate::require(&json!(false), ""));
1919        assert!(!Validate::require(&json!([]), ""));
1920        assert!(!Validate::require(&json!({}), ""));
1921    }
1922
1923    #[test]
1924    fn test_must_equals_require() {
1925        // must 与 require 行为一致
1926        assert_eq!(
1927            Validate::require(&json!("hello"), ""),
1928            Validate::must(&json!("hello"), "")
1929        );
1930        assert_eq!(
1931            Validate::require(&Value::Null, ""),
1932            Validate::must(&Value::Null, "")
1933        );
1934    }
1935
1936    // ========================================================================
1937    // 组 10:内置规则 is 测试
1938    // ========================================================================
1939
1940    #[test]
1941    fn test_is_require() {
1942        assert!(Validate::is(&json!("hello"), "require", &Value::Null));
1943        assert!(!Validate::is(&Value::Null, "require", &Value::Null));
1944    }
1945
1946    #[test]
1947    fn test_is_accepted() {
1948        assert!(Validate::is(&json!("1"), "accepted", &Value::Null));
1949        assert!(Validate::is(&json!("on"), "accepted", &Value::Null));
1950        assert!(Validate::is(&json!("yes"), "accepted", &Value::Null));
1951        assert!(!Validate::is(&json!("no"), "accepted", &Value::Null));
1952    }
1953
1954    #[test]
1955    fn test_is_boolean() {
1956        assert!(Validate::is(&json!(true), "boolean", &Value::Null));
1957        assert!(Validate::is(&json!(false), "boolean", &Value::Null));
1958        assert!(Validate::is(&json!(0), "boolean", &Value::Null));
1959        assert!(Validate::is(&json!(1), "boolean", &Value::Null));
1960        assert!(Validate::is(&json!("0"), "boolean", &Value::Null));
1961        assert!(Validate::is(&json!("1"), "boolean", &Value::Null));
1962        assert!(!Validate::is(&json!(2), "boolean", &Value::Null));
1963    }
1964
1965    #[test]
1966    fn test_is_number() {
1967        assert!(Validate::is(&json!(123), "number", &Value::Null));
1968        assert!(Validate::is(&json!(3.14), "number", &Value::Null));
1969        assert!(Validate::is(&json!("123"), "number", &Value::Null));
1970        assert!(Validate::is(&json!("3.14"), "number", &Value::Null));
1971        assert!(!Validate::is(&json!("abc"), "number", &Value::Null));
1972    }
1973
1974    #[test]
1975    fn test_is_integer() {
1976        assert!(Validate::is(&json!(123), "integer", &Value::Null));
1977        assert!(Validate::is(&json!("123"), "integer", &Value::Null));
1978        assert!(!Validate::is(&json!(3.14), "integer", &Value::Null));
1979        assert!(!Validate::is(&json!("3.14"), "integer", &Value::Null));
1980    }
1981
1982    #[test]
1983    fn test_is_float() {
1984        assert!(Validate::is(&json!(3.14), "float", &Value::Null));
1985        assert!(Validate::is(&json!("3.14"), "float", &Value::Null));
1986        // 整数也是 float(PHP 行为)
1987        assert!(Validate::is(&json!(123), "float", &Value::Null));
1988    }
1989
1990    #[test]
1991    fn test_is_array() {
1992        assert!(Validate::is(&json!([1, 2, 3]), "array", &Value::Null));
1993        assert!(!Validate::is(&json!("string"), "array", &Value::Null));
1994        assert!(!Validate::is(&json!({}), "array", &Value::Null));
1995    }
1996
1997    #[test]
1998    fn test_is_email() {
1999        assert!(Validate::is(
2000            &json!("user@example.com"),
2001            "email",
2002            &Value::Null
2003        ));
2004        assert!(Validate::is(
2005            &json!("user.name+tag@example.co.uk"),
2006            "email",
2007            &Value::Null
2008        ));
2009        assert!(!Validate::is(&json!("invalid"), "email", &Value::Null));
2010        assert!(!Validate::is(&json!("user@"), "email", &Value::Null));
2011    }
2012
2013    #[test]
2014    fn test_is_url() {
2015        assert!(Validate::is(
2016            &json!("http://example.com"),
2017            "url",
2018            &Value::Null
2019        ));
2020        assert!(Validate::is(
2021            &json!("https://example.com/path?q=1"),
2022            "url",
2023            &Value::Null
2024        ));
2025        assert!(!Validate::is(&json!("example.com"), "url", &Value::Null));
2026    }
2027
2028    #[test]
2029    fn test_is_ip() {
2030        assert!(Validate::is(&json!("127.0.0.1"), "ip", &Value::Null));
2031        assert!(Validate::is(&json!("::1"), "ip", &Value::Null));
2032        assert!(Validate::is(&json!("192.168.1.1"), "ip", &Value::Null));
2033        assert!(!Validate::is(&json!("999.999.999.999"), "ip", &Value::Null));
2034        assert!(!Validate::is(&json!("not.an.ip"), "ip", &Value::Null));
2035    }
2036
2037    #[test]
2038    fn test_is_mac_addr() {
2039        assert!(Validate::is(
2040            &json!("00:11:22:33:44:55"),
2041            "macAddr",
2042            &Value::Null
2043        ));
2044        assert!(Validate::is(
2045            &json!("00-11-22-33-44-55"),
2046            "macAddr",
2047            &Value::Null
2048        ));
2049        assert!(!Validate::is(&json!("invalid"), "macAddr", &Value::Null));
2050    }
2051
2052    #[test]
2053    fn test_is_alpha() {
2054        assert!(Validate::is(&json!("abc"), "alpha", &Value::Null));
2055        assert!(Validate::is(&json!("ABC"), "alpha", &Value::Null));
2056        assert!(!Validate::is(&json!("abc123"), "alpha", &Value::Null));
2057        assert!(!Validate::is(&json!("abc_def"), "alpha", &Value::Null));
2058    }
2059
2060    #[test]
2061    fn test_is_alpha_num() {
2062        assert!(Validate::is(&json!("abc123"), "alphaNum", &Value::Null));
2063        assert!(Validate::is(&json!("ABC"), "alphaNum", &Value::Null));
2064        assert!(!Validate::is(&json!("abc_123"), "alphaNum", &Value::Null));
2065    }
2066
2067    #[test]
2068    fn test_is_alpha_dash() {
2069        assert!(Validate::is(&json!("abc123"), "alphaDash", &Value::Null));
2070        assert!(Validate::is(
2071            &json!("abc_def-123"),
2072            "alphaDash",
2073            &Value::Null
2074        ));
2075        assert!(!Validate::is(&json!("abc def"), "alphaDash", &Value::Null));
2076    }
2077
2078    #[test]
2079    fn test_is_chs() {
2080        assert!(Validate::is(&json!("中文"), "chs", &Value::Null));
2081        assert!(!Validate::is(&json!("abc"), "chs", &Value::Null));
2082        assert!(!Validate::is(&json!("中文abc"), "chs", &Value::Null));
2083    }
2084
2085    #[test]
2086    fn test_is_chs_alpha() {
2087        assert!(Validate::is(&json!("中文abc"), "chsAlpha", &Value::Null));
2088        assert!(Validate::is(&json!("中文"), "chsAlpha", &Value::Null));
2089        assert!(!Validate::is(&json!("中文123"), "chsAlpha", &Value::Null));
2090    }
2091
2092    #[test]
2093    fn test_is_mobile() {
2094        assert!(Validate::is(&json!("13812345678"), "mobile", &Value::Null));
2095        assert!(Validate::is(&json!("19912345678"), "mobile", &Value::Null));
2096        assert!(!Validate::is(&json!("12345678901"), "mobile", &Value::Null)); // 不以 1[3-9] 开头
2097        assert!(!Validate::is(&json!("1381234567"), "mobile", &Value::Null)); // 少一位
2098    }
2099
2100    #[test]
2101    fn test_is_date() {
2102        assert!(Validate::is(&json!("2024-01-01"), "date", &Value::Null));
2103        assert!(Validate::is(
2104            &json!("2024-01-01 12:00:00"),
2105            "date",
2106            &Value::Null
2107        ));
2108        assert!(Validate::is(&json!("2024/01/01"), "date", &Value::Null));
2109        assert!(!Validate::is(&json!("invalid date"), "date", &Value::Null));
2110    }
2111
2112    // ========================================================================
2113    // 组 11:regex_validate 测试
2114    // ========================================================================
2115
2116    #[test]
2117    fn test_regex_validate_default_pattern() {
2118        // 使用内置 defaultRegex
2119        let custom = IndexMap::new();
2120        assert!(Validate::regex_validate(&json!("abc"), "alpha", &custom));
2121        assert!(!Validate::regex_validate(&json!("123"), "alpha", &custom));
2122    }
2123
2124    #[test]
2125    fn test_regex_validate_custom_pattern() {
2126        // 使用自定义正则
2127        let mut custom = IndexMap::new();
2128        custom.insert("custom".to_string(), r"^\d{4}$".to_string());
2129        assert!(Validate::regex_validate(&json!("1234"), "custom", &custom));
2130        assert!(!Validate::regex_validate(
2131            &json!("12345"),
2132            "custom",
2133            &custom
2134        ));
2135    }
2136
2137    #[test]
2138    fn test_regex_validate_inline_pattern() {
2139        // 直接传入正则模式(不是预定义名)
2140        let custom = IndexMap::new();
2141        assert!(Validate::regex_validate(&json!("12345"), r"\d{5}", &custom));
2142        assert!(!Validate::regex_validate(&json!("abc"), r"\d{5}", &custom));
2143    }
2144
2145    // ========================================================================
2146    // 组 12:check 方法测试
2147    // ========================================================================
2148
2149    #[test]
2150    fn test_check_success_single_rule() {
2151        let mut v = Validate::new().rule("name", "require");
2152        let data = json!({"name": "Alice"});
2153        assert!(v.check(&data).is_ok());
2154    }
2155
2156    #[test]
2157    fn test_check_failure_single_rule() {
2158        let mut v = Validate::new().rule("name", "require");
2159        let data = json!({"name": ""});
2160        let result = v.check(&data);
2161        assert!(result.is_err());
2162        match result.unwrap_err() {
2163            ValidateError::Single(msg) => assert!(msg.contains("require")),
2164            _ => panic!("expected Single error"),
2165        }
2166    }
2167
2168    #[test]
2169    fn test_check_success_multiple_rules() {
2170        let mut v = Validate::new()
2171            .rule("name", "require")
2172            .rule("age", "require|integer");
2173        let data = json!({"name": "Alice", "age": 30});
2174        assert!(v.check(&data).is_ok());
2175    }
2176
2177    #[test]
2178    fn test_check_failure_multiple_rules_single_mode() {
2179        // 非批量模式:返回第一个错误
2180        // 注意:IndexMap 保持插入顺序(已从 HashMap 迁移到 IndexMap)
2181        let mut v = Validate::new()
2182            .rule("name", "require")
2183            .rule("age", "require|integer");
2184        let data = json!({"name": "", "age": "not_int"});
2185        let result = v.check(&data);
2186        assert!(result.is_err());
2187        assert!(matches!(result.unwrap_err(), ValidateError::Single(_)));
2188    }
2189
2190    #[test]
2191    fn test_check_batch_mode_collects_all_errors() {
2192        // 批量模式:收集所有错误
2193        let mut v = Validate::new()
2194            .rule("name", "require")
2195            .rule("age", "require|integer")
2196            .batch(true);
2197        let data = json!({"name": "", "age": "not_int"});
2198        let result = v.check(&data);
2199        assert!(result.is_err());
2200        match result.unwrap_err() {
2201            ValidateError::Batch(errors) => {
2202                assert!(errors.contains_key("name"));
2203                // age 字段 "not_int" 是非空字符串,但 integer 验证失败
2204                // 注:由于 age 非空,integer 规则会触发
2205            }
2206            ValidateError::Single(_) => panic!("expected Batch error in batch mode"),
2207        }
2208    }
2209
2210    #[test]
2211    fn test_check_missing_field_with_require() {
2212        let mut v = Validate::new().rule("name", "require");
2213        let data = json!({});
2214        assert!(v.check(&data).is_err());
2215    }
2216
2217    #[test]
2218    fn test_check_missing_field_without_require_skips() {
2219        // R5-5:非 require 规则在字段缺失时跳过验证
2220        let mut v = Validate::new().rule("age", "integer");
2221        let data = json!({}); // age 字段缺失
2222        assert!(v.check(&data).is_ok());
2223    }
2224
2225    #[test]
2226    fn test_check_field_title_parsing() {
2227        // 字段名格式 field|title
2228        let mut v = Validate::new().rule("name|名称", "require");
2229        let data = json!({"name": ""});
2230        let result = v.check(&data);
2231        assert!(result.is_err());
2232        match result.unwrap_err() {
2233            ValidateError::Single(msg) => assert!(msg.contains("名称")),
2234            _ => panic!("expected Single error"),
2235        }
2236    }
2237
2238    #[test]
2239    fn test_check_field_description_from_field_map() {
2240        let mut fields = IndexMap::new();
2241        fields.insert("name".to_string(), "用户名".to_string());
2242        let mut v = Validate::new().field(fields).rule("name", "require");
2243        let data = json!({"name": ""});
2244        let result = v.check(&data);
2245        assert!(result.is_err());
2246        match result.unwrap_err() {
2247            ValidateError::Single(msg) => assert!(msg.contains("用户名")),
2248            _ => panic!("expected Single error"),
2249        }
2250    }
2251
2252    #[test]
2253    fn test_check_custom_message_override() {
2254        // R5-1:message[field.type] 优先级最高
2255        let mut msgs = IndexMap::new();
2256        msgs.insert("name.require".to_string(), "名称必填".to_string());
2257        let mut v = Validate::new().message(msgs).rule("name", "require");
2258        let data = json!({"name": ""});
2259        let result = v.check(&data);
2260        match result.unwrap_err() {
2261            ValidateError::Single(msg) => assert_eq!(msg, "名称必填"),
2262            _ => panic!("expected Single error"),
2263        }
2264    }
2265
2266    // ========================================================================
2267    // 组 13:场景测试(基础)
2268    // ========================================================================
2269
2270    #[test]
2271    fn test_check_scene_filters_fields() {
2272        // 场景过滤:only 列表中的字段才验证
2273        let mut v = Validate::new()
2274            .rule("name", "require")
2275            .rule("age", "require")
2276            .register_scene("login", vec!["name".to_string()])
2277            .scene("login");
2278        // age 缺失但不在 scene 中,应该通过
2279        let data = json!({"name": "Alice"});
2280        assert!(v.check(&data).is_ok());
2281    }
2282
2283    #[test]
2284    fn test_check_scene_resets_state() {
2285        // R5-6:切换场景时重置 only/append/remove
2286        let mut v = Validate::new()
2287            .rule("name", "require")
2288            .register_scene("s1", vec!["name".to_string()])
2289            .only(vec!["other".to_string()]) // 设置一个 only
2290            .scene("s1"); // 切换场景应该重置 only
2291        let data = json!({"name": "Alice"});
2292        // scene s1 的 only = ["name"],所以 name 在 only 中,应该验证
2293        assert!(v.check(&data).is_ok());
2294    }
2295
2296    #[test]
2297    fn test_has_scene() {
2298        let v = Validate::new()
2299            .register_scene("login", vec!["name".to_string()])
2300            .register_scene("register", vec!["name".to_string(), "email".to_string()]);
2301        assert!(v.has_scene("login"));
2302        assert!(v.has_scene("register"));
2303        assert!(!v.has_scene("logout"));
2304    }
2305
2306    // ========================================================================
2307    // 组 14:check_rule 测试
2308    // ========================================================================
2309
2310    #[test]
2311    fn test_check_rule_success() {
2312        let v = Validate::new();
2313        assert!(v.check_rule(&json!("hello"), "require").is_ok());
2314        assert!(v.check_rule(&json!(123), "integer").is_ok());
2315    }
2316
2317    #[test]
2318    fn test_check_rule_failure() {
2319        let v = Validate::new();
2320        assert!(v.check_rule(&Value::Null, "require").is_err());
2321        assert!(v.check_rule(&json!("abc"), "integer").is_err());
2322    }
2323
2324    #[test]
2325    fn test_check_rule_multiple() {
2326        let v = Validate::new();
2327        assert!(v.check_rule(&json!(123), "require|integer").is_ok());
2328        assert!(v.check_rule(&json!(""), "require|integer").is_err());
2329    }
2330
2331    // ========================================================================
2332    // 组 14.1:dispatch_builtin 集成测试(验证 rules.rs 接入)
2333    // ========================================================================
2334
2335    #[test]
2336    fn test_check_dispatch_eq_rule() {
2337        // eq:5 → dispatch_builtin("eq", value, "5", ...) → rules::eq
2338        let v = Validate::new();
2339        assert!(v.check_rule(&json!(5), "eq:5").is_ok());
2340        assert!(v.check_rule(&json!("5"), "eq:5").is_ok()); // 松散比较
2341        assert!(v.check_rule(&json!(6), "eq:5").is_err());
2342    }
2343
2344    #[test]
2345    fn test_check_dispatch_gt_egt_lt_elt_rules() {
2346        let v = Validate::new();
2347        // gt:5
2348        assert!(v.check_rule(&json!(6), "gt:5").is_ok());
2349        assert!(v.check_rule(&json!(5), "gt:5").is_err());
2350        // egt:5
2351        assert!(v.check_rule(&json!(5), "egt:5").is_ok());
2352        assert!(v.check_rule(&json!(4), "egt:5").is_err());
2353        // lt:5
2354        assert!(v.check_rule(&json!(4), "lt:5").is_ok());
2355        assert!(v.check_rule(&json!(5), "lt:5").is_err());
2356        // elt:5
2357        assert!(v.check_rule(&json!(5), "elt:5").is_ok());
2358        assert!(v.check_rule(&json!(6), "elt:5").is_err());
2359    }
2360
2361    #[test]
2362    fn test_check_dispatch_in_not_in_rules() {
2363        let v = Validate::new();
2364        assert!(v.check_rule(&json!(1), "in:1,2,3").is_ok());
2365        assert!(v.check_rule(&json!("1"), "in:1,2,3").is_ok()); // 松散比较
2366        assert!(v.check_rule(&json!(4), "in:1,2,3").is_err());
2367        assert!(v.check_rule(&json!(4), "notIn:1,2,3").is_ok());
2368        assert!(v.check_rule(&json!(1), "notIn:1,2,3").is_err());
2369    }
2370
2371    #[test]
2372    fn test_check_dispatch_between_not_between_rules() {
2373        let v = Validate::new();
2374        assert!(v.check_rule(&json!(5), "between:1,10").is_ok());
2375        assert!(v.check_rule(&json!("5"), "between:1,10").is_ok()); // 松散比较
2376        assert!(v.check_rule(&json!(0), "between:1,10").is_err());
2377        assert!(v.check_rule(&json!(11), "between:1,10").is_err());
2378        assert!(v.check_rule(&json!(0), "notBetween:1,10").is_ok());
2379    }
2380
2381    #[test]
2382    fn test_check_dispatch_length_max_min_rules() {
2383        let v = Validate::new();
2384        // length
2385        assert!(v.check_rule(&json!("abc"), "length:3").is_ok());
2386        assert!(v.check_rule(&json!("abc"), "length:1,5").is_ok());
2387        assert!(v.check_rule(&json!("abcdef"), "length:1,5").is_err());
2388        // max
2389        assert!(v.check_rule(&json!("abc"), "max:5").is_ok());
2390        assert!(v.check_rule(&json!("abcdef"), "max:5").is_err());
2391        // min
2392        assert!(v.check_rule(&json!("abc"), "min:3").is_ok());
2393        assert!(v.check_rule(&json!("ab"), "min:3").is_err());
2394    }
2395
2396    #[test]
2397    fn test_check_dispatch_length_unicode_chinese() {
2398        // 中文按字符计数(对齐 PHP mb_strlen)
2399        let v = Validate::new();
2400        assert!(v.check_rule(&json!("中文测试"), "length:4").is_ok());
2401        assert!(v.check_rule(&json!("中"), "length:1").is_ok());
2402    }
2403
2404    #[test]
2405    fn test_check_dispatch_date_format_rule() {
2406        let v = Validate::new();
2407        assert!(v
2408            .check_rule(&json!("2024-01-15"), "dateFormat:Y-m-d")
2409            .is_ok());
2410        assert!(v
2411            .check_rule(&json!("2024/01/15"), "dateFormat:Y-m-d")
2412            .is_err());
2413    }
2414
2415    #[test]
2416    fn test_check_dispatch_after_before_rules() {
2417        let v = Validate::new();
2418        assert!(v
2419            .check_rule(&json!("2024-01-02"), "after:2024-01-01")
2420            .is_ok());
2421        assert!(v
2422            .check_rule(&json!("2023-12-31"), "after:2024-01-01")
2423            .is_err());
2424        assert!(v
2425            .check_rule(&json!("2023-12-31"), "before:2024-01-01")
2426            .is_ok());
2427        assert!(v
2428            .check_rule(&json!("2024-01-02"), "before:2024-01-01")
2429            .is_err());
2430    }
2431
2432    #[test]
2433    fn test_check_dispatch_ip_rule() {
2434        let v = Validate::new();
2435        assert!(v.check_rule(&json!("127.0.0.1"), "ip:ipv4").is_ok());
2436        assert!(v.check_rule(&json!("127.0.0.1"), "ip").is_ok()); // 默认 ipv4
2437        assert!(v.check_rule(&json!("::1"), "ip:ipv6").is_ok());
2438        assert!(v.check_rule(&json!("::1"), "ip:ipv4").is_err());
2439    }
2440
2441    #[test]
2442    fn test_check_dispatch_confirm_rule_via_check() {
2443        // 通过 check 方法验证 confirm 规则的字段推断
2444        let mut v = Validate::new().rule("password", "require|confirm");
2445        let data = json!({"password": "abc123", "password_confirm": "abc123"});
2446        assert!(v.check(&data).is_ok());
2447
2448        let data = json!({"password": "abc123", "password_confirm": "different"});
2449        assert!(v.check(&data).is_err());
2450    }
2451
2452    #[test]
2453    fn test_check_dispatch_different_rule_via_check() {
2454        let mut v = Validate::new().rule("field1", "different:field2");
2455        let data = json!({"field1": "abc", "field2": "xyz"});
2456        assert!(v.check(&data).is_ok());
2457
2458        let data = json!({"field1": "abc", "field2": "abc"});
2459        assert!(v.check(&data).is_err());
2460    }
2461
2462    #[test]
2463    fn test_check_dispatch_require_if_rule_via_check() {
2464        let mut v = Validate::new().rule("username", "requireIf:type,login");
2465        // type=login 时 username 必须非空
2466        let data = json!({"type": "login", "username": "alice"});
2467        assert!(v.check(&data).is_ok());
2468        let data = json!({"type": "login", "username": ""});
2469        assert!(v.check(&data).is_err());
2470        // type!=login 时 username 不验证
2471        let data = json!({"type": "register", "username": ""});
2472        assert!(v.check(&data).is_ok());
2473    }
2474
2475    #[test]
2476    fn test_check_dispatch_require_with_rule_via_check() {
2477        let mut v = Validate::new().rule("email", "requireWith:contact");
2478        // contact 有值时 email 必须
2479        let data = json!({"contact": "some_value", "email": "user@example.com"});
2480        assert!(v.check(&data).is_ok());
2481        let data = json!({"contact": "some_value", "email": ""});
2482        assert!(v.check(&data).is_err());
2483        // contact 无值时 email 不验证
2484        let data = json!({"contact": "", "email": ""});
2485        assert!(v.check(&data).is_ok());
2486    }
2487
2488    #[test]
2489    fn test_check_dispatch_alias_operators() {
2490        // PHP 别名:> → gt, >= → egt, < → lt, <= → elt, = → eq
2491        let v = Validate::new();
2492        assert!(v.check_rule(&json!(6), ">:5").is_ok());
2493        assert!(v.check_rule(&json!(5), ">=:5").is_ok());
2494        assert!(v.check_rule(&json!(4), "<:5").is_ok());
2495        assert!(v.check_rule(&json!(5), "<=:5").is_ok());
2496        assert!(v.check_rule(&json!(5), "=:5").is_ok());
2497    }
2498
2499    // ========================================================================
2500    // 组 15:extend 自定义规则测试
2501    // ========================================================================
2502
2503    #[test]
2504    fn test_extend_custom_rule_pass() {
2505        let mut v = Validate::new();
2506        v.extend(
2507            "custom_even",
2508            Arc::new(|value: &Value, _rule: &str, _data: &Value| {
2509                value.as_i64().map(|n| n % 2 == 0).unwrap_or(false)
2510            }),
2511        );
2512        let mut v = v.rule("num", "custom_even");
2513        let data = json!({"num": 4});
2514        assert!(v.check(&data).is_ok());
2515    }
2516
2517    #[test]
2518    fn test_extend_custom_rule_fail() {
2519        let mut v = Validate::new();
2520        v.extend(
2521            "custom_even",
2522            Arc::new(|value: &Value, _rule: &str, _data: &Value| {
2523                value.as_i64().map(|n| n % 2 == 0).unwrap_or(false)
2524            }),
2525        );
2526        let mut v = v.rule("num", "custom_even");
2527        let data = json!({"num": 5});
2528        let result = v.check(&data);
2529        assert!(result.is_err());
2530    }
2531
2532    #[test]
2533    fn test_extend_custom_rule_with_args() {
2534        let mut v = Validate::new();
2535        v.extend(
2536            "custom_min",
2537            Arc::new(|value: &Value, rule: &str, _data: &Value| {
2538                let min: i64 = rule.parse().unwrap_or(0);
2539                value.as_i64().map(|n| n >= min).unwrap_or(false)
2540            }),
2541        );
2542        let mut v = v.rule("num", "custom_min:10");
2543        assert!(v.check(&json!({"num": 15})).is_ok());
2544        assert!(v.check(&json!({"num": 5})).is_err());
2545    }
2546
2547    // ========================================================================
2548    // 组 16:get_error 测试
2549    // ========================================================================
2550
2551    #[test]
2552    fn test_get_error_after_check_failure() {
2553        let mut v = Validate::new().rule("name", "require");
2554        let data = json!({"name": ""});
2555        let _ = v.check(&data);
2556        let error = v.get_error();
2557        match error {
2558            ValidateError::Single(msg) => assert!(msg.contains("require")),
2559            _ => panic!("expected Single error"),
2560        }
2561    }
2562
2563    #[test]
2564    fn test_get_error_after_check_success() {
2565        let mut v = Validate::new().rule("name", "require");
2566        let data = json!({"name": "Alice"});
2567        let _ = v.check(&data);
2568        // 成功时 error 保持初始状态
2569        match v.get_error() {
2570            ValidateError::Single(s) => assert!(s.is_empty()),
2571            _ => panic!("expected Single (empty)"),
2572        }
2573    }
2574
2575    // ========================================================================
2576    // 组 17:PHP 行为对齐测试(R5 硬约束)
2577    // ========================================================================
2578
2579    #[test]
2580    fn test_php_bug_numeric_key_returns_key_itself() {
2581        // R5-3:PHP getDataValue 对数值型 key 返回 key 本身
2582        // 这是一个 PHP 怪异行为,sz-rust 1:1 复刻
2583        let data = json!({"123": "value", "456": "another"});
2584        // 数值 key "123" 返回 "123"(key 本身),不是 "value"
2585        assert_eq!(Validate::get_data_value(&data, "123"), json!("123"));
2586    }
2587
2588    #[test]
2589    fn test_php_behavior_empty_value_skips_non_require_rules() {
2590        // R5-5:PHP checkItem 中,空值且非 require/must 规则 → 跳过验证
2591        let mut v = Validate::new().rule("age", "integer");
2592        // age 为空字符串,integer 规则应该跳过
2593        let data = json!({"age": ""});
2594        assert!(v.check(&data).is_ok());
2595    }
2596
2597    #[test]
2598    fn test_php_behavior_require_validates_string_zero() {
2599        // PHP 特殊行为:"0" 在 require 中被视为非空
2600        let mut v = Validate::new().rule("count", "require");
2601        let data = json!({"count": "0"});
2602        assert!(v.check(&data).is_ok());
2603    }
2604
2605    #[test]
2606    fn test_php_behavior_scene_resets_only_append_remove() {
2607        // R5-6:PHP getScene 方法重置 only/append/remove
2608        let mut v = Validate::new()
2609            .rule("name", "require")
2610            .rule("email", "require")
2611            .only(vec!["email".to_string()]) // 手动设置 only
2612            .register_scene("scene1", vec!["name".to_string()])
2613            .scene("scene1");
2614        let data = json!({"name": "Alice"}); // 没有 email
2615                                             // scene1 切换后 only 应该是 ["name"],email 不验证
2616        assert!(v.check(&data).is_ok());
2617    }
2618
2619    #[test]
2620    fn test_php_behavior_remove_all_rules_for_field() {
2621        // remove[field] = None 表示移除所有规则
2622        let mut v = Validate::new().rule("name", "require").remove("name", None);
2623        let data = json!({}); // name 缺失
2624        assert!(v.check(&data).is_ok());
2625    }
2626
2627    #[test]
2628    fn test_php_behavior_remove_specific_rule() {
2629        // remove[field] = ["integer"] 仅移除 integer 规则
2630        let mut v = Validate::new()
2631            .rule("age", "require|integer")
2632            .remove("age", Some(vec!["integer".to_string()]));
2633        let data = json!({"age": "not_int"});
2634        // integer 被移除,require 通过(非空)
2635        assert!(v.check(&data).is_ok());
2636    }
2637
2638    #[test]
2639    fn test_php_behavior_append_adds_rule() {
2640        // append[field] = ["email"] 追加 email 规则
2641        let mut v = Validate::new()
2642            .rule("contact", "require")
2643            .append("contact", vec!["email".to_string()]);
2644        // contact 是有效邮箱
2645        assert!(v.check(&json!({"contact": "user@example.com"})).is_ok());
2646        // contact 不是邮箱
2647        assert!(v.check(&json!({"contact": "invalid"})).is_err());
2648    }
2649
2650    #[test]
2651    fn test_php_behavior_get_rule_msg_lookup_chain() {
2652        // R5-1:完整的查找链测试
2653        // 1. message[field.type] 存在时优先使用
2654        let mut msgs = IndexMap::new();
2655        msgs.insert("name.require".to_string(), "优先级1".to_string());
2656        msgs.insert("name".to_string(), "优先级2".to_string());
2657        let v = Validate::new().message(msgs);
2658        assert_eq!(v.get_rule_msg("name", "名称", "require", ""), "优先级1");
2659
2660        // 2. 仅 message[field] 存在时
2661        let mut msgs = IndexMap::new();
2662        msgs.insert("name".to_string(), "优先级2".to_string());
2663        let v = Validate::new().message(msgs);
2664        assert_eq!(v.get_rule_msg("name", "名称", "require", ""), "优先级2");
2665
2666        // 3. 仅 type_msg[type] 存在时
2667        let v = Validate::new();
2668        assert_eq!(
2669            v.get_rule_msg("name", "名称", "email", ""),
2670            "名称 not a valid email address"
2671        );
2672
2673        // 4. require 前缀回退
2674        assert_eq!(
2675            v.get_rule_msg("name", "名称", "requireIf", ""),
2676            "名称 require"
2677        );
2678
2679        // 5. 默认(对齐 PHP 第 1578 行:$title . lang->get('not conform to the rules'))
2680        // 无 Lang 时 lang->get 返回 name 本身,无前导空格
2681        assert_eq!(
2682            v.get_rule_msg("name", "名称", "unknownType", ""),
2683            "名称not conform to the rules"
2684        );
2685    }
2686
2687    // ========================================================================
2688    // 组 18:场景回调测试
2689    // ========================================================================
2690
2691    #[test]
2692    fn test_register_scene_callback_basic() {
2693        // 注册场景回调后,has_scene 应返回 true
2694        let v = Validate::new().register_scene_callback("login", Arc::new(|_v| {}));
2695        assert!(v.has_scene("login"));
2696        assert!(!v.has_scene("register"));
2697    }
2698
2699    #[test]
2700    fn test_has_scene_checks_both_array_and_callback() {
2701        // has_scene 同时检查 scene 数组和 scene_callbacks
2702        let v = Validate::new()
2703            .register_scene("array_scene", vec!["name".to_string()])
2704            .register_scene_callback("callback_scene", Arc::new(|_v| {}));
2705        assert!(v.has_scene("array_scene"));
2706        assert!(v.has_scene("callback_scene"));
2707        assert!(!v.has_scene("nonexistent"));
2708    }
2709
2710    #[test]
2711    fn test_scene_callback_sets_only() {
2712        // 场景回调通过 only_mut 设置 only 字段
2713        let mut v = Validate::new()
2714            .rule("name", "require")
2715            .rule("email", "require")
2716            .register_scene_callback(
2717                "login",
2718                Arc::new(|v| {
2719                    v.only_mut(vec!["email".to_string()]);
2720                }),
2721            )
2722            .scene("login");
2723        // data 只有 email,没有 name
2724        // 因为 scene 回调设置 only=["email"],name 不验证
2725        let data = json!({"email": "test@example.com"});
2726        assert!(v.check(&data).is_ok());
2727    }
2728
2729    #[test]
2730    fn test_scene_callback_priority_over_array() {
2731        // 对齐 PHP getScene 第 1663-1668 行:
2732        // 如果同时存在 scene{Name} 方法和 $scene[$name] 数组,方法优先
2733        // 这里:回调设置 only=["email"],数组设置 only=["name"]
2734        // 期望:回调用,only=["email"],name 不验证
2735        let mut v = Validate::new()
2736            .rule("name", "require")
2737            .rule("email", "require")
2738            .register_scene("conflict", vec!["name".to_string()])
2739            .register_scene_callback(
2740                "conflict",
2741                Arc::new(|v| {
2742                    v.only_mut(vec!["email".to_string()]);
2743                }),
2744            )
2745            .scene("conflict");
2746        // data 只有 email,没有 name
2747        // 如果回调用,only=["email"],应该通过
2748        // 如果数组用,only=["name"],应该失败
2749        let data = json!({"email": "test@example.com"});
2750        assert!(v.check(&data).is_ok());
2751    }
2752
2753    #[test]
2754    fn test_scene_callback_can_modify_append() {
2755        // 场景回调通过 append_mut 追加规则
2756        let mut v = Validate::new()
2757            .rule("name", "require")
2758            .register_scene_callback(
2759                "strict",
2760                Arc::new(|v| {
2761                    v.append_mut("name", vec!["max:5".to_string()]);
2762                }),
2763            )
2764            .scene("strict");
2765        // name 长度 6,超过 max:5,应该失败
2766        let data = json!({"name": "Alice2"});
2767        assert!(v.check(&data).is_err());
2768    }
2769
2770    #[test]
2771    fn test_scene_callback_can_modify_remove() {
2772        // 场景回调通过 remove_mut 移除规则
2773        let mut v = Validate::new()
2774            .rule("name", "require|max:5")
2775            .register_scene_callback(
2776                "lenient",
2777                Arc::new(|v| {
2778                    v.remove_mut("name", Some(vec!["max".to_string()]));
2779                }),
2780            )
2781            .scene("lenient");
2782        // name 长度 6,但 max 被移除,require 通过(非空),应该成功
2783        let data = json!({"name": "Alice2"});
2784        assert!(v.check(&data).is_ok());
2785    }
2786
2787    #[test]
2788    fn test_scene_callback_resets_state() {
2789        // R5-6:切换场景时重置 only/append/remove(对齐 PHP getScene 第 1661 行)
2790        // 即使之前手动设置了 only,切换场景后回调中 only_mut 覆盖
2791        let mut v = Validate::new()
2792            .rule("name", "require")
2793            .rule("email", "require")
2794            .only(vec!["email".to_string()]) // 手动设置 only=["email"]
2795            .register_scene_callback(
2796                "scene1",
2797                Arc::new(|v| {
2798                    v.only_mut(vec!["name".to_string()]);
2799                }),
2800            )
2801            .scene("scene1");
2802        // data 有 name,没有 email
2803        // 手动 only=["email"] 被重置,回调设置 only=["name"]
2804        // name 验证通过,email 不在 only 中不验证
2805        let data = json!({"name": "Alice"});
2806        assert!(v.check(&data).is_ok());
2807    }
2808
2809    #[test]
2810    fn test_scene_callback_no_callback_no_array() {
2811        // 场景名既无回调也无数组,仅重置 only/append/remove
2812        // 对齐 PHP getScene:如果 scene 不存在,only/append/remove 仍被重置为空
2813        let mut v = Validate::new()
2814            .rule("name", "require")
2815            .rule("email", "require")
2816            .only(vec!["email".to_string()]) // 手动设置 only
2817            .scene("nonexistent"); // 场景不存在
2818        let data = json!({"name": "Alice"}); // 没有 email
2819                                             // scene 不存在,only 被重置为空,所有字段都验证
2820                                             // name 通过,email 缺失失败
2821        assert!(v.check(&data).is_err());
2822    }
2823
2824    #[test]
2825    fn test_only_mut_method() {
2826        // only_mut 直接修改 only 字段
2827        let mut v = Validate::new();
2828        v.only_mut(vec!["a".to_string(), "b".to_string()]);
2829        // 通过场景应用验证 only 是否生效
2830        // 间接验证:only 被设置后,未在 only 中的字段不验证
2831        let mut v = Validate::new()
2832            .rule("a", "require")
2833            .rule("b", "require")
2834            .rule("c", "require");
2835        v.only_mut(vec!["a".to_string(), "b".to_string()]);
2836        let data = json!({"a": "x", "b": "y"}); // c 缺失
2837        assert!(v.check(&data).is_ok());
2838    }
2839
2840    #[test]
2841    fn test_append_mut_method() {
2842        // append_mut 直接修改 append 字段
2843        let mut v = Validate::new().rule("name", "require");
2844        v.append_mut("name", vec!["max:3".to_string()]);
2845        // name 长度 5,超过 max:3,应该失败
2846        let data = json!({"name": "Alice"});
2847        assert!(v.check(&data).is_err());
2848    }
2849
2850    #[test]
2851    fn test_remove_mut_method() {
2852        // remove_mut 直接修改 remove 字段
2853        let mut v = Validate::new().rule("name", "require|max:3");
2854        v.remove_mut("name", Some(vec!["max".to_string()]));
2855        // max 被移除,require 通过,应该成功
2856        let data = json!({"name": "Alice"});
2857        assert!(v.check(&data).is_ok());
2858    }
2859
2860    #[test]
2861    fn test_php_behavior_scene_callback_mimics_scene_method() {
2862        // PHP 行为对齐:sceneXxx 方法典型用法
2863        // PHP:
2864        //   protected function sceneRegister()
2865        //   {
2866        //       return $this->only(['name', 'email', 'age'])->append('age', 'require');
2867        //   }
2868        // Rust 等价:
2869        // 注:age 必须在 only 中,否则场景过滤会跳过 age,append 不生效
2870        let mut v = Validate::new()
2871            .rule("name", "require|max:25")
2872            .rule("email", "require|email")
2873            .rule("age", "integer")
2874            .register_scene_callback(
2875                "register",
2876                Arc::new(|v| {
2877                    v.only_mut(vec![
2878                        "name".to_string(),
2879                        "email".to_string(),
2880                        "age".to_string(),
2881                    ]);
2882                    v.append_mut("age", vec!["require".to_string()]);
2883                }),
2884            )
2885            .scene("register");
2886        // data 有 name、email、age
2887        let data = json!({
2888            "name": "Alice",
2889            "email": "test@example.com",
2890            "age": 30
2891        });
2892        assert!(v.check(&data).is_ok());
2893        // 缺少 age 应该失败(age 在 only 中,integer 通过空值跳过,但 append 了 require)
2894        let data2 = json!({
2895            "name": "Alice",
2896            "email": "test@example.com"
2897        });
2898        assert!(v.check(&data2).is_err());
2899    }
2900
2901    #[test]
2902    fn test_php_behavior_get_scene_method_priority() {
2903        // PHP getScene 第 1663-1668 行严格对齐:
2904        // method_exists 优先于 isset($scene[$name])
2905        // 注册两个场景名,一个用回调,一个用数组,验证都能正常工作
2906        let mut v1 = Validate::new()
2907            .rule("name", "require")
2908            .rule("email", "require")
2909            .register_scene_callback(
2910                "cb_scene",
2911                Arc::new(|v| {
2912                    v.only_mut(vec!["email".to_string()]);
2913                }),
2914            )
2915            .scene("cb_scene");
2916        let data1 = json!({"email": "test@example.com"}); // name 缺失
2917        assert!(v1.check(&data1).is_ok());
2918
2919        let mut v2 = Validate::new()
2920            .rule("name", "require")
2921            .rule("email", "require")
2922            .register_scene("arr_scene", vec!["email".to_string()])
2923            .scene("arr_scene");
2924        let data2 = json!({"email": "test@example.com"}); // name 缺失
2925        assert!(v2.check(&data2).is_ok());
2926    }
2927
2928    #[test]
2929    fn test_php_behavior_scene_resets_all_three_state() {
2930        // R5-6 完整对齐:切换场景时 only/append/remove 全部重置
2931        // 手动设置 only/append/remove,切换场景后全部被重置
2932        let mut v = Validate::new()
2933            .rule("name", "require|max:5")
2934            .rule("email", "require")
2935            .only(vec!["email".to_string()]) // 手动 only
2936            .append("name", vec!["max:3".to_string()]) // 手动 append
2937            .remove("email", None) // 手动 remove
2938            .register_scene("reset", vec!["name".to_string()])
2939            .scene("reset");
2940        // 切换场景后:
2941        // - only 重置为 ["name"]
2942        // - append 重置为空(name 上的 max:3 失效)
2943        // - remove 重置为空(email 上的 remove 失效)
2944        let data = json!({"name": "Alice"}); // name 长度 5,满足 max:5;email 缺失
2945                                             // only=["name"],email 不验证
2946                                             // append 被重置,name 上的 max:3 不生效,max:5 通过
2947        assert!(v.check(&data).is_ok());
2948    }
2949
2950    // ========================================================================
2951    // 组 19:错误消息国际化测试
2952    // ========================================================================
2953
2954    #[test]
2955    fn test_set_lang_basic_injection() {
2956        // 对齐 PHP setLang(Lang $lang) — 注入 Lang 实例
2957        let lang: Arc<dyn message::Lang> =
2958            Arc::new(message::SimpleLang::new().set("require", "必须填写"));
2959        let v = Validate::new().set_lang(lang);
2960        assert!(v.lang.is_some());
2961    }
2962
2963    #[test]
2964    fn test_get_rule_msg_with_lang_default_translation() {
2965        // 对齐 PHP 第 1578 行:$title . $this->lang->get('not conform to the rules')
2966        // Lang 中存在 'not conform to the rules' 翻译时使用翻译值
2967        let lang: Arc<dyn message::Lang> =
2968            Arc::new(message::SimpleLang::new().set("not conform to the rules", "不符合规则"));
2969        let v = Validate::new().set_lang(lang);
2970        let msg = v.get_rule_msg("field", "字段", "unknownType", "");
2971        assert_eq!(msg, "字段不符合规则");
2972    }
2973
2974    #[test]
2975    fn test_get_rule_msg_with_lang_no_translation_returns_name() {
2976        // 对齐 PHP Lang::get 找不到时返回 name 本身
2977        let lang: Arc<dyn message::Lang> = Arc::new(message::SimpleLang::new());
2978        let v = Validate::new().set_lang(lang);
2979        let msg = v.get_rule_msg("field", "字段", "unknownType", "");
2980        assert_eq!(msg, "字段not conform to the rules");
2981    }
2982
2983    #[test]
2984    fn test_get_rule_msg_with_lang_percent_var_syntax() {
2985        // 对齐 PHP parseErrorMsg 第 1598-1599 行:{%var} 语法
2986        // message[field] = "{%custom_msg}" 时应翻译
2987        let lang: Arc<dyn message::Lang> =
2988            Arc::new(message::SimpleLang::new().set("custom_msg", "自定义错误消息"));
2989        let mut msgs = IndexMap::new();
2990        msgs.insert("name".to_string(), "{%custom_msg}".to_string());
2991        let v = Validate::new().message(msgs).set_lang(lang);
2992        let msg = v.get_rule_msg("name", "名称", "require", "");
2993        assert_eq!(msg, "自定义错误消息");
2994    }
2995
2996    #[test]
2997    fn test_get_rule_msg_with_lang_has_check() {
2998        // 对齐 PHP parseErrorMsg 第 1600-1601 行:lang->has($msg) 检查
2999        // message[field] = "require" 且 lang->has("require") 时翻译
3000        let lang: Arc<dyn message::Lang> =
3001            Arc::new(message::SimpleLang::new().set("require", "必须填写"));
3002        let mut msgs = IndexMap::new();
3003        msgs.insert("name".to_string(), "require".to_string());
3004        let v = Validate::new().message(msgs).set_lang(lang);
3005        let msg = v.get_rule_msg("name", "名称", "require", "");
3006        assert_eq!(msg, "必须填写");
3007    }
3008
3009    #[test]
3010    fn test_get_rule_msg_with_lang_percent_var_with_placeholders() {
3011        // {%var} 翻译后再进行占位符替换
3012        // 翻译结果 ":attribute 必填" 中的 :attribute 应被替换为 title
3013        let lang: Arc<dyn message::Lang> =
3014            Arc::new(message::SimpleLang::new().set("require_msg", ":attribute 必填"));
3015        let mut msgs = IndexMap::new();
3016        msgs.insert("name".to_string(), "{%require_msg}".to_string());
3017        let v = Validate::new().message(msgs).set_lang(lang);
3018        let msg = v.get_rule_msg("name", "名称", "require", "");
3019        assert_eq!(msg, "名称 必填");
3020    }
3021
3022    #[test]
3023    fn test_get_rule_msg_with_lang_has_check_with_placeholders() {
3024        // lang->has 翻译后再进行占位符替换
3025        let lang: Arc<dyn message::Lang> = Arc::new(
3026            message::SimpleLang::new().set("range_msg", ":attribute 必须在 :1 到 :2 之间"),
3027        );
3028        let mut msgs = IndexMap::new();
3029        msgs.insert("age".to_string(), "range_msg".to_string());
3030        let v = Validate::new().message(msgs).set_lang(lang);
3031        let msg = v.get_rule_msg("age", "年龄", "between", "18,60");
3032        assert_eq!(msg, "年龄 必须在 18 到 60 之间");
3033    }
3034
3035    #[test]
3036    fn test_parse_error_msg_with_lang_no_lang_skips_translation() {
3037        // 无 Lang 时跳过翻译,直接进行占位符替换
3038        let v = Validate::new();
3039        let result = v.parse_error_msg_with_lang(":attribute require", "", "名称");
3040        assert_eq!(result, "名称 require");
3041    }
3042
3043    #[test]
3044    fn test_parse_error_msg_with_lang_translates_first() {
3045        // 先翻译,后替换占位符
3046        let lang: Arc<dyn message::Lang> =
3047            Arc::new(message::SimpleLang::new().set("custom_msg", ":attribute 自定义错误 :rule"));
3048        let v = Validate::new().set_lang(lang);
3049        let result = v.parse_error_msg_with_lang("custom_msg", "param1", "字段");
3050        assert_eq!(result, "字段 自定义错误 param1");
3051    }
3052
3053    #[test]
3054    fn test_parse_error_msg_with_lang_percent_var_no_placeholders() {
3055        // {%var} 翻译后无占位符时直接返回
3056        let lang: Arc<dyn message::Lang> =
3057            Arc::new(message::SimpleLang::new().set("plain_msg", "纯文本消息"));
3058        let v = Validate::new().set_lang(lang);
3059        let result = v.parse_error_msg_with_lang("{%plain_msg}", "rule", "字段");
3060        assert_eq!(result, "纯文本消息");
3061    }
3062
3063    #[test]
3064    fn test_parse_error_msg_with_lang_no_colon_no_replacement() {
3065        // 对齐 PHP 第 1613 行:msg 不含 : 时不进行替换
3066        let lang: Arc<dyn message::Lang> =
3067            Arc::new(message::SimpleLang::new().set("plain", "纯文本"));
3068        let v = Validate::new().set_lang(lang);
3069        let result = v.parse_error_msg_with_lang("plain", "rule", "字段");
3070        assert_eq!(result, "纯文本");
3071    }
3072
3073    #[test]
3074    fn test_check_with_lang_translates_error_message() {
3075        // 集成测试:check 方法失败时返回的错误消息包含 Lang 翻译
3076        let lang: Arc<dyn message::Lang> = Arc::new(
3077            message::SimpleLang::new()
3078                .set("not conform to the rules", "不符合规则")
3079                .set("require", ":attribute 必须填写"),
3080        );
3081        let mut v = Validate::new().rule("name", "require").set_lang(lang);
3082        let data = json!({});
3083        let result = v.check(&data);
3084        assert!(result.is_err());
3085        if let Err(ValidateError::Single(msg)) = result {
3086            // type_msg["require"] = ":attribute require"
3087            // lang->has(":attribute require") = false
3088            // 占位符替换::attribute → "名称"(title 从 "name" 推导)
3089            // 注意:name 没有描述,title 默认为字段名 "name"
3090            assert_eq!(msg, "name require");
3091        } else {
3092            panic!("Expected ValidateError::Single");
3093        }
3094    }
3095
3096    #[test]
3097    fn test_check_with_lang_percent_var_in_message() {
3098        // 集成测试:自定义 message 使用 {%var} 语法
3099        let lang: Arc<dyn message::Lang> =
3100            Arc::new(message::SimpleLang::new().set("name_required", "名称是必填字段"));
3101        let mut msgs = IndexMap::new();
3102        msgs.insert("name.require".to_string(), "{%name_required}".to_string());
3103        let mut v = Validate::new()
3104            .rule("name", "require")
3105            .message(msgs)
3106            .set_lang(lang);
3107        let data = json!({});
3108        let result = v.check(&data);
3109        assert!(result.is_err());
3110        if let Err(ValidateError::Single(msg)) = result {
3111            assert_eq!(msg, "名称是必填字段");
3112        } else {
3113            panic!("Expected ValidateError::Single");
3114        }
3115    }
3116
3117    #[test]
3118    fn test_check_with_lang_has_check_in_message() {
3119        // 集成测试:自定义 message 命中 lang->has
3120        let lang: Arc<dyn message::Lang> =
3121            Arc::new(message::SimpleLang::new().set("NAME_REQUIRED", "名称必填"));
3122        let mut msgs = IndexMap::new();
3123        // lang->has 不区分大小写,"name_required" 能命中 "NAME_REQUIRED"
3124        msgs.insert("name.require".to_string(), "name_required".to_string());
3125        let mut v = Validate::new()
3126            .rule("name", "require")
3127            .message(msgs)
3128            .set_lang(lang);
3129        let data = json!({});
3130        let result = v.check(&data);
3131        assert!(result.is_err());
3132        if let Err(ValidateError::Single(msg)) = result {
3133            assert_eq!(msg, "名称必填");
3134        } else {
3135            panic!("Expected ValidateError::Single");
3136        }
3137    }
3138
3139    // ========================================================================
3140    // 组 20:PHP 行为对齐测试(R5-7)
3141    // ========================================================================
3142
3143    #[test]
3144    fn test_php_behavior_lang_translation_priority_percent_over_has() {
3145        // 对齐 PHP parseErrorMsg:{%var} 优先于 lang->has
3146        let lang: Arc<dyn message::Lang> = Arc::new(
3147            message::SimpleLang::new()
3148                .set("{%key}", "整体键") // 理论上不会出现,但验证优先级
3149                .set("key", "提取键"),
3150        );
3151        let mut msgs = IndexMap::new();
3152        msgs.insert("field".to_string(), "{%key}".to_string());
3153        let v = Validate::new().message(msgs).set_lang(lang);
3154        let msg = v.get_rule_msg("field", "字段", "require", "");
3155        assert_eq!(msg, "提取键");
3156    }
3157
3158    #[test]
3159    fn test_php_behavior_lang_default_case_uses_lang_get() {
3160        // 对齐 PHP 第 1578 行:默认分支调用 lang->get('not conform to the rules')
3161        let lang: Arc<dyn message::Lang> =
3162            Arc::new(message::SimpleLang::new().set("not conform to the rules", " 不符合规则"));
3163        let v = Validate::new().set_lang(lang);
3164        let msg = v.get_rule_msg("field", "字段", "unknownType", "");
3165        // PHP: $title . $this->lang->get('not conform to the rules')
3166        // 翻译值为 " 不符合规则"(带前导空格),拼接为 "字段 不符合规则"
3167        assert_eq!(msg, "字段 不符合规则");
3168    }
3169
3170    #[test]
3171    fn test_php_behavior_lang_translation_case_insensitive() {
3172        // 对齐 PHP Lang::has/get 的 strtolower 行为
3173        let lang: Arc<dyn message::Lang> =
3174            Arc::new(message::SimpleLang::new().set("RequireError", ":attribute 必填"));
3175        let mut msgs = IndexMap::new();
3176        // msg = "requireerror"(小写)应命中 Lang 中的 "RequireError"
3177        msgs.insert("name".to_string(), "requireerror".to_string());
3178        let v = Validate::new().message(msgs).set_lang(lang);
3179        let msg = v.get_rule_msg("name", "名称", "require", "");
3180        assert_eq!(msg, "名称 必填");
3181    }
3182
3183    #[test]
3184    fn test_php_behavior_lang_translation_not_found_returns_original() {
3185        // 对齐 PHP Lang::get 找不到时返回 name 本身
3186        let lang: Arc<dyn message::Lang> = Arc::new(message::SimpleLang::new());
3187        let mut msgs = IndexMap::new();
3188        msgs.insert("name".to_string(), "nonexistent_key".to_string());
3189        let v = Validate::new().message(msgs).set_lang(lang);
3190        let msg = v.get_rule_msg("name", "名称", "require", "");
3191        // lang->has("nonexistent_key") = false → 返回原值 "nonexistent_key"
3192        assert_eq!(msg, "nonexistent_key");
3193    }
3194
3195    #[test]
3196    fn test_php_behavior_lang_percent_var_extracts_key_via_substr() {
3197        // 对齐 PHP substr($msg, 2, -1) 提取 {%var} 内的 key
3198        let lang: Arc<dyn message::Lang> =
3199            Arc::new(message::SimpleLang::new().set("my.message.key", "我的消息"));
3200        let mut msgs = IndexMap::new();
3201        msgs.insert("name".to_string(), "{%my.message.key}".to_string());
3202        let v = Validate::new().message(msgs).set_lang(lang);
3203        let msg = v.get_rule_msg("name", "名称", "require", "");
3204        assert_eq!(msg, "我的消息");
3205    }
3206
3207    #[test]
3208    fn test_php_behavior_lang_translation_with_full_pipeline() {
3209        // 完整流水线测试:{%var} → 翻译 → 占位符替换
3210        let lang: Arc<dyn message::Lang> = Arc::new(message::SimpleLang::new().set(
3211            "between_msg",
3212            ":attribute 必须在 :1 - :2 之间(默认 :rule)",
3213        ));
3214        let mut msgs = IndexMap::new();
3215        msgs.insert("age.between".to_string(), "{%between_msg}".to_string());
3216        let v = Validate::new().message(msgs).set_lang(lang);
3217        let msg = v.get_rule_msg("age", "年龄", "between", "18,60");
3218        // 1. {%between_msg} → ":attribute 必须在 :1 - :2 之间(默认 :rule)"
3219        // 2. 占位符替换::attribute → "年龄", :1 → "18", :2 → "60", :rule → "18,60"
3220        assert_eq!(msg, "年龄 必须在 18 - 60 之间(默认 18,60)");
3221    }
3222}