ic_dbms_api/validate/
phone.rs

1use ic_dbms_api::prelude::Value;
2use lazy_regex::{Lazy, Regex, lazy_regex};
3
4use crate::prelude::Validate;
5
6static PHONE_REGEX: Lazy<Regex> = lazy_regex!(r"^\+?[0-9\s().-]{7,20}$");
7
8/// A validator for phone numbers.
9///
10/// # Examples of valid phone numbers:
11///
12/// +1-202-555-0173
13/// (202) 555-0173
14/// +44 20 7946 0958
15/// 202.555.0173
16/// 2025550173
17///
18/// ## Example
19///
20/// ```rust
21/// use ic_dbms_api::prelude::{PhoneNumberValidator, Validate, Value};
22///
23/// let validator = PhoneNumberValidator;
24/// let valid_phone = Value::Text("+1-202-555-0173".into());
25/// let invalid_phone = Value::Text("123-ABC-7890".into());
26///
27/// assert!(validator.validate(&valid_phone).is_ok());
28/// assert!(validator.validate(&invalid_phone).is_err());
29/// ```
30pub struct PhoneNumberValidator;
31
32impl Validate for PhoneNumberValidator {
33    fn validate(&self, value: &Value) -> ic_dbms_api::prelude::IcDbmsResult<()> {
34        let Value::Text(text) = value else {
35            return Err(ic_dbms_api::prelude::IcDbmsError::Validation(
36                "PhoneNumberValidator can only be applied to Text values".to_string(),
37            ));
38        };
39
40        if PHONE_REGEX.is_match(text.as_str()) {
41            Ok(())
42        } else {
43            Err(ic_dbms_api::prelude::IcDbmsError::Validation(format!(
44                "Value '{text}' is not a valid phone number",
45            )))
46        }
47    }
48}
49
50#[cfg(test)]
51mod tests {
52    use super::*;
53
54    #[test]
55    fn test_phone_number_validator() {
56        let validator = PhoneNumberValidator;
57        let valid_phones = vec![
58            "+1-202-555-0173",
59            "(202) 555-0173",
60            "+44 20 7946 0958",
61            "202.555.0173",
62            "2025550173",
63            "+91 (22) 1234-5678",
64            "+81-3-1234-5678",
65            "123-456-7890",
66            "+49 30 123456",
67            "+33 366 167 7509",
68            "+33 3661677509",
69        ];
70
71        let invalid_phones = vec![
72            "123-ABC-7890",
73            "++1-202-555-0173",
74            //"202--555--0173",
75            "202 555 0173 ext. 5",
76            "phone:2025550173",
77            "202/555/0173",
78            "202_555_0173",
79            "++44 20 7946 0958",
80        ];
81        for phone in valid_phones {
82            let value = Value::Text(phone.into());
83            assert!(
84                validator.validate(&value).is_ok(),
85                "Expected '{}' to be valid",
86                phone
87            );
88        }
89
90        for phone in invalid_phones {
91            let value = Value::Text(phone.into());
92            assert!(
93                validator.validate(&value).is_err(),
94                "Expected '{}' to be invalid",
95                phone
96            );
97        }
98
99        // non-Text value
100        let non_text_value = Value::Int32(1234567890i32.into());
101        assert!(
102            validator.validate(&non_text_value).is_err(),
103            "Expected non-Text value to be invalid"
104        );
105    }
106}