1use 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#[derive(Debug, Clone, PartialEq, Eq)]
126pub enum ValidateError {
127 Single(String),
131 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#[derive(Debug, Clone)]
163pub enum Rule {
164 Simple(String),
166 WithArgs(String, String),
168 Multiple(Vec<Rule>),
170}
171
172impl Rule {
173 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 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
214pub type RuleCallback = Arc<dyn Fn(&Value, &str, &Value) -> bool + Send + Sync>;
227
228pub 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
265pub 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
329pub 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
343pub struct Validate {
362 rule: IndexMap<String, Rule>,
364 message: IndexMap<String, String>,
366 field: IndexMap<String, String>,
368 regex: IndexMap<String, String>,
370 scene: IndexMap<String, Vec<String>>,
372 scene_callbacks: IndexMap<String, scene::SceneCallback>,
374 current_scene: Option<String>,
376 batch: bool,
378 only: Vec<String>,
380 remove: IndexMap<String, Option<Vec<String>>>,
382 append: IndexMap<String, Vec<String>>,
384 error: ValidateError,
386 type_callbacks: IndexMap<String, RuleCallback>,
388 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 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 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 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 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 pub fn scene(mut self, name: &str) -> Self {
463 self.current_scene = Some(name.to_string());
464 self
465 }
466
467 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 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 pub fn has_scene(&self, name: &str) -> bool {
518 self.scene.contains_key(name) || self.scene_callbacks.contains_key(name)
519 }
520
521 pub fn batch(mut self, batch: bool) -> Self {
525 self.batch = batch;
526 self
527 }
528
529 pub fn only(mut self, fields: Vec<String>) -> Self {
533 self.only = fields;
534 self
535 }
536
537 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 pub fn append(mut self, field: &str, rule: Vec<String>) -> Self {
554 self.append.insert(field.to_string(), rule);
555 self
556 }
557
558 pub fn only_mut(&mut self, fields: Vec<String>) {
563 self.only = fields;
564 }
565
566 pub fn remove_mut(&mut self, field: &str, rule: Option<Vec<String>>) {
570 self.remove.insert(field.to_string(), rule);
571 }
572
573 pub fn append_mut(&mut self, field: &str, rule: Vec<String>) {
577 self.append.insert(field.to_string(), rule);
578 }
579
580 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 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 pub fn set_lang(mut self, lang: Arc<dyn message::Lang>) -> Self {
626 self.lang = Some(lang);
627 self
628 }
629
630 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 if let Some(scene_name) = self.current_scene.clone() {
653 self.only.clear();
655 self.append.clear();
656 self.remove.clear();
657 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 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 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 if !self.only.is_empty() && !self.only.contains(&field_name) {
687 continue;
688 }
689
690 let value = Self::get_data_value(data, &field_name);
692
693 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 let _ = &append_clone; 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 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 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 if matches!(self.remove.get(field), Some(None)) && !self.append.contains_key(field) {
764 return Ok(());
765 }
766
767 let mut rule_list = rules.to_list();
769
770 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 let (cb_type, args, info) = Self::get_validate_type(rule_type, rule_args);
789
790 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 let result = if let Some(cb) = self.type_callbacks.get(&cb_type) {
808 cb(value, &args, data)
810 } else if info == "must"
811 || info.starts_with("require")
812 || (!value.is_null() && !is_empty_string(value))
813 {
814 self.dispatch_builtin(&cb_type, value, &args, data, field, title)
816 } else {
817 true
819 };
820
821 if !result {
822 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 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 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 "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 "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 "length" => rules::length(value, rule, data, field),
874 "max" => rules::max(value, rule, data, field),
875 "min" => rules::min(value, rule, data, field),
876 "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 "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" => 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 "activeUrl" => rules::active_url(value, rule, data, field),
893 _ => {
894 true
896 }
897 }
898 }
899
900 pub fn get_data_value(data: &Value, key: &str) -> Value {
914 if key.parse::<i64>().is_ok() || key.parse::<f64>().is_ok() {
916 return Value::String(key.to_string());
917 }
918 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 data.get(key).cloned().unwrap_or(Value::Null)
931 }
932
933 pub fn get_validate_type(rule_type: &str, rule_args: &str) -> (String, String, String) {
949 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 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 (resolved_type.clone(), rule_args.to_string(), resolved_type)
996 } else if PHP_METHODS.contains(&resolved_type.as_str()) {
997 (resolved_type.clone(), String::new(), resolved_type)
999 } else {
1000 ("is".to_string(), resolved_type.clone(), resolved_type)
1002 }
1003 }
1004
1005 pub fn get_rule_msg(&self, field: &str, title: &str, type_name: &str, rule: &str) -> String {
1028 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 if let Some(msg) = self.message.get(field) {
1035 return self.parse_error_msg_with_lang(msg, rule, title);
1036 }
1037 if let Some(&msg) = TYPE_MSG.get(type_name) {
1039 return self.parse_error_msg_with_lang(msg, rule, title);
1040 }
1041 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 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 pub fn parse_error_msg_with_lang(&self, msg: &str, rule: &str, title: &str) -> String {
1077 let translated = message::translate_msg(msg, self.lang.as_ref());
1079 Self::parse_error_msg(&translated, rule, title)
1080 }
1081
1082 pub fn parse_error_msg(msg: &str, rule: &str, title: &str) -> String {
1097 let mut result = msg.to_string();
1098
1099 if !result.contains(':') {
1101 return result;
1102 }
1103
1104 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 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 if result.contains(":rule") {
1123 result = result.replace(":rule", rule);
1124 }
1125
1126 result
1127 }
1128
1129 pub fn get_error(&self) -> &ValidateError {
1131 &self.error
1132 }
1133
1134 pub fn require(value: &Value, _rule: &str) -> bool {
1149 !is_empty_value(value) || matches!(value, Value::String(s) if s == "0")
1152 }
1153
1154 pub fn must(value: &Value, _rule: &str) -> bool {
1158 !is_empty_value(value) || matches!(value, Value::String(s) if s == "0")
1159 }
1160
1161 pub fn is(value: &Value, rule: &str, _data: &Value) -> bool {
1188 let rule = rule.trim();
1189 match rule {
1190 "require" => {
1191 !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 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 "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 true
1236 }
1237 }
1238 }
1239
1240 pub fn regex_validate(
1244 value: &Value,
1245 rule: &str,
1246 custom_regex: &IndexMap<String, String>,
1247 ) -> bool {
1248 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 if rule.starts_with('/') {
1257 rule.to_string()
1258 } else {
1259 format!("/^{}/$", rule)
1260 }
1261 };
1262
1263 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 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
1281pub(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
1303fn is_empty_string(value: &Value) -> bool {
1307 matches!(value, Value::String(s) if s.is_empty())
1308}
1309
1310fn is_valid_date(value: &Value) -> bool {
1312 let s = match value {
1313 Value::String(s) => s,
1314 _ => return false,
1315 };
1316 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
1337fn is_valid_email(value: &Value) -> bool {
1339 let s = match value {
1340 Value::String(s) => s,
1341 _ => return false,
1342 };
1343 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
1351fn is_valid_url(value: &Value) -> bool {
1353 let s = match value {
1354 Value::String(s) => s,
1355 _ => return false,
1356 };
1357 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
1367fn is_valid_ip(value: &Value) -> bool {
1369 let s = match value {
1370 Value::String(s) => s,
1371 Value::Number(n) => {
1372 let _ = n;
1374 return false;
1375 }
1376 _ => return false,
1377 };
1378 use std::net::IpAddr;
1379 s.parse::<IpAddr>().is_ok()
1380}
1381
1382fn 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
1394fn 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
1413fn php_regex_to_rust(pattern: &str) -> String {
1419 let mut result = pattern.to_string();
1420 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 if result.starts_with('/') && result.len() > 2 {
1433 let flags_start = result.rfind('/').unwrap_or(result.len() - 1);
1434 if flags_start > 0 {
1435 let _inner = &result[1..flags_start];
1437 let flags = &result[flags_start + 1..];
1438 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#[cfg(test)]
1461#[allow(clippy::approx_constant)]
1462mod tests {
1463 use super::*;
1464 use serde_json::json;
1465
1466 #[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 #[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 #[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 #[test]
1649 fn test_static_default_regex_contains_all() {
1650 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 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 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 #[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 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 let data = json!({"123": "value"});
1738 assert_eq!(
1739 Validate::get_data_value(&data, "123"),
1740 json!("123") );
1742 }
1743
1744 #[test]
1745 fn test_get_data_value_float_numeric_key() {
1746 let data = json!({});
1748 assert_eq!(Validate::get_data_value(&data, "3.14"), json!("3.14"));
1749 }
1750
1751 #[test]
1756 fn test_get_validate_type_no_alias() {
1757 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 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 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 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 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 #[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 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 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 #[test]
1857 fn test_get_rule_msg_priority_field_type() {
1858 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 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 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 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 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 #[test]
1907 fn test_require_non_empty_string() {
1908 assert!(Validate::require(&json!("hello"), ""));
1909 assert!(Validate::require(&json!("0"), "")); }
1911
1912 #[test]
1913 fn test_require_empty_values() {
1914 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 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 #[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 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)); assert!(!Validate::is(&json!("1381234567"), "mobile", &Value::Null)); }
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 #[test]
2117 fn test_regex_validate_default_pattern() {
2118 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 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 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 #[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 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 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 }
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 let mut v = Validate::new().rule("age", "integer");
2221 let data = json!({}); assert!(v.check(&data).is_ok());
2223 }
2224
2225 #[test]
2226 fn test_check_field_title_parsing() {
2227 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 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 #[test]
2271 fn test_check_scene_filters_fields() {
2272 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 let data = json!({"name": "Alice"});
2280 assert!(v.check(&data).is_ok());
2281 }
2282
2283 #[test]
2284 fn test_check_scene_resets_state() {
2285 let mut v = Validate::new()
2287 .rule("name", "require")
2288 .register_scene("s1", vec!["name".to_string()])
2289 .only(vec!["other".to_string()]) .scene("s1"); let data = json!({"name": "Alice"});
2292 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 #[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 #[test]
2336 fn test_check_dispatch_eq_rule() {
2337 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()); 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 assert!(v.check_rule(&json!(6), "gt:5").is_ok());
2349 assert!(v.check_rule(&json!(5), "gt:5").is_err());
2350 assert!(v.check_rule(&json!(5), "egt:5").is_ok());
2352 assert!(v.check_rule(&json!(4), "egt:5").is_err());
2353 assert!(v.check_rule(&json!(4), "lt:5").is_ok());
2355 assert!(v.check_rule(&json!(5), "lt:5").is_err());
2356 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()); 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()); 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 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 assert!(v.check_rule(&json!("abc"), "max:5").is_ok());
2390 assert!(v.check_rule(&json!("abcdef"), "max:5").is_err());
2391 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 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()); 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 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 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 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 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 let data = json!({"contact": "", "email": ""});
2485 assert!(v.check(&data).is_ok());
2486 }
2487
2488 #[test]
2489 fn test_check_dispatch_alias_operators() {
2490 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 #[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 #[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 match v.get_error() {
2570 ValidateError::Single(s) => assert!(s.is_empty()),
2571 _ => panic!("expected Single (empty)"),
2572 }
2573 }
2574
2575 #[test]
2580 fn test_php_bug_numeric_key_returns_key_itself() {
2581 let data = json!({"123": "value", "456": "another"});
2584 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 let mut v = Validate::new().rule("age", "integer");
2592 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 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 let mut v = Validate::new()
2609 .rule("name", "require")
2610 .rule("email", "require")
2611 .only(vec!["email".to_string()]) .register_scene("scene1", vec!["name".to_string()])
2613 .scene("scene1");
2614 let data = json!({"name": "Alice"}); assert!(v.check(&data).is_ok());
2617 }
2618
2619 #[test]
2620 fn test_php_behavior_remove_all_rules_for_field() {
2621 let mut v = Validate::new().rule("name", "require").remove("name", None);
2623 let data = json!({}); assert!(v.check(&data).is_ok());
2625 }
2626
2627 #[test]
2628 fn test_php_behavior_remove_specific_rule() {
2629 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 assert!(v.check(&data).is_ok());
2636 }
2637
2638 #[test]
2639 fn test_php_behavior_append_adds_rule() {
2640 let mut v = Validate::new()
2642 .rule("contact", "require")
2643 .append("contact", vec!["email".to_string()]);
2644 assert!(v.check(&json!({"contact": "user@example.com"})).is_ok());
2646 assert!(v.check(&json!({"contact": "invalid"})).is_err());
2648 }
2649
2650 #[test]
2651 fn test_php_behavior_get_rule_msg_lookup_chain() {
2652 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 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 let v = Validate::new();
2668 assert_eq!(
2669 v.get_rule_msg("name", "名称", "email", ""),
2670 "名称 not a valid email address"
2671 );
2672
2673 assert_eq!(
2675 v.get_rule_msg("name", "名称", "requireIf", ""),
2676 "名称 require"
2677 );
2678
2679 assert_eq!(
2682 v.get_rule_msg("name", "名称", "unknownType", ""),
2683 "名称not conform to the rules"
2684 );
2685 }
2686
2687 #[test]
2692 fn test_register_scene_callback_basic() {
2693 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 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 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 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 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 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 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 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 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 let data = json!({"name": "Alice2"});
2784 assert!(v.check(&data).is_ok());
2785 }
2786
2787 #[test]
2788 fn test_scene_callback_resets_state() {
2789 let mut v = Validate::new()
2792 .rule("name", "require")
2793 .rule("email", "require")
2794 .only(vec!["email".to_string()]) .register_scene_callback(
2796 "scene1",
2797 Arc::new(|v| {
2798 v.only_mut(vec!["name".to_string()]);
2799 }),
2800 )
2801 .scene("scene1");
2802 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 let mut v = Validate::new()
2814 .rule("name", "require")
2815 .rule("email", "require")
2816 .only(vec!["email".to_string()]) .scene("nonexistent"); let data = json!({"name": "Alice"}); assert!(v.check(&data).is_err());
2822 }
2823
2824 #[test]
2825 fn test_only_mut_method() {
2826 let mut v = Validate::new();
2828 v.only_mut(vec!["a".to_string(), "b".to_string()]);
2829 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"}); assert!(v.check(&data).is_ok());
2838 }
2839
2840 #[test]
2841 fn test_append_mut_method() {
2842 let mut v = Validate::new().rule("name", "require");
2844 v.append_mut("name", vec!["max:3".to_string()]);
2845 let data = json!({"name": "Alice"});
2847 assert!(v.check(&data).is_err());
2848 }
2849
2850 #[test]
2851 fn test_remove_mut_method() {
2852 let mut v = Validate::new().rule("name", "require|max:3");
2854 v.remove_mut("name", Some(vec!["max".to_string()]));
2855 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 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 let data = json!({
2888 "name": "Alice",
2889 "email": "test@example.com",
2890 "age": 30
2891 });
2892 assert!(v.check(&data).is_ok());
2893 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 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"}); 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"}); assert!(v2.check(&data2).is_ok());
2926 }
2927
2928 #[test]
2929 fn test_php_behavior_scene_resets_all_three_state() {
2930 let mut v = Validate::new()
2933 .rule("name", "require|max:5")
2934 .rule("email", "require")
2935 .only(vec!["email".to_string()]) .append("name", vec!["max:3".to_string()]) .remove("email", None) .register_scene("reset", vec!["name".to_string()])
2939 .scene("reset");
2940 let data = json!({"name": "Alice"}); assert!(v.check(&data).is_ok());
2948 }
2949
2950 #[test]
2955 fn test_set_lang_basic_injection() {
2956 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 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 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 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 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 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 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 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 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 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 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 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 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 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 let lang: Arc<dyn message::Lang> =
3121 Arc::new(message::SimpleLang::new().set("NAME_REQUIRED", "名称必填"));
3122 let mut msgs = IndexMap::new();
3123 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 #[test]
3144 fn test_php_behavior_lang_translation_priority_percent_over_has() {
3145 let lang: Arc<dyn message::Lang> = Arc::new(
3147 message::SimpleLang::new()
3148 .set("{%key}", "整体键") .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 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 assert_eq!(msg, "字段 不符合规则");
3168 }
3169
3170 #[test]
3171 fn test_php_behavior_lang_translation_case_insensitive() {
3172 let lang: Arc<dyn message::Lang> =
3174 Arc::new(message::SimpleLang::new().set("RequireError", ":attribute 必填"));
3175 let mut msgs = IndexMap::new();
3176 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 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 assert_eq!(msg, "nonexistent_key");
3193 }
3194
3195 #[test]
3196 fn test_php_behavior_lang_percent_var_extracts_key_via_substr() {
3197 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 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 assert_eq!(msg, "年龄 必须在 18 - 60 之间(默认 18,60)");
3221 }
3222}