skp_validator_rules/comparison/
must_match.rs1use skp_validator_core::{Rule, ValidationContext, ValidationErrors, ValidationError, ValidationResult};
4
5#[derive(Debug, Clone)]
19pub struct MustMatchRule {
20 pub other_field: String,
22 pub message: Option<String>,
24}
25
26impl MustMatchRule {
27 pub fn new(other_field: impl Into<String>) -> Self {
29 Self {
30 other_field: other_field.into(),
31 message: None,
32 }
33 }
34
35 pub fn message(mut self, msg: impl Into<String>) -> Self {
37 self.message = Some(msg.into());
38 self
39 }
40
41 fn get_message(&self) -> String {
42 self.message.clone().unwrap_or_else(|| {
43 format!("Must match '{}'", self.other_field)
44 })
45 }
46}
47
48impl Rule<str> for MustMatchRule {
49 fn validate(&self, value: &str, ctx: &ValidationContext) -> ValidationResult<()> {
50 if let Some(other_value) = ctx.get_string(&self.other_field) {
52 if value == other_value {
53 return Ok(());
54 } else {
55 return Err(ValidationErrors::from_iter([
56 ValidationError::root("must_match", self.get_message())
57 .with_param("other_field", self.other_field.clone())
58 ]));
59 }
60 }
61
62 Ok(())
64 }
65
66 fn name(&self) -> &'static str {
67 "must_match"
68 }
69
70 fn default_message(&self) -> String {
71 format!("Must match '{}'", self.other_field)
72 }
73}
74
75impl Rule<String> for MustMatchRule {
76 fn validate(&self, value: &String, ctx: &ValidationContext) -> ValidationResult<()> {
77 <Self as Rule<str>>::validate(self, value.as_str(), ctx)
78 }
79
80 fn name(&self) -> &'static str {
81 "must_match"
82 }
83
84 fn default_message(&self) -> String {
85 format!("Must match '{}'", self.other_field)
86 }
87}
88
89#[cfg(test)]
90mod tests {
91 use super::*;
92
93 #[test]
94 #[cfg(feature = "serde")]
95 fn test_must_match() {
96 use serde_json::json;
97
98 let rule = MustMatchRule::new("password");
99 let ctx = ValidationContext::from_json(&json!({
100 "password": "secret123",
101 "password_confirm": "secret123"
102 }));
103
104 assert!(rule.validate("secret123", &ctx).is_ok());
106
107 assert!(rule.validate("different", &ctx).is_err());
109 }
110
111 #[test]
112 fn test_custom_message() {
113 let rule = MustMatchRule::new("password").message("Passwords must match");
114 assert_eq!(rule.get_message(), "Passwords must match");
115 }
116}