Skip to main content

icydb_model/base/normalizer/intl/
phone.rs

1//! Module: base::normalizer::intl::phone
2//!
3//! Responsibility: base normalizer definitions.
4//! Does not own: validation policy, persistence, or schema mutation semantics.
5//! Boundary: mutates schema field values through facade normalizer traits.
6
7use crate::{prelude::*, visitor::Normalizer};
8
9///
10/// E164PhoneNumber
11/// Parses and re-formats input into canonical E.164 string
12///
13
14#[normalizer]
15pub struct E164PhoneNumber;
16
17impl Normalizer<String> for E164PhoneNumber {
18    fn normalize(&self, value: &mut String) -> Result<(), String> {
19        let trimmed = value.trim();
20
21        if trimmed.is_empty() || trimmed.starts_with('+') {
22            *value = trimmed.to_owned();
23            return Ok(());
24        }
25
26        if trimmed.chars().all(|c| c.is_ascii_digit()) {
27            *value = format!("+{trimmed}");
28            return Ok(());
29        }
30
31        *value = trimmed.to_owned();
32
33        Ok(())
34    }
35}
36
37///
38/// TESTS
39///
40
41#[cfg(test)]
42mod tests {
43    use super::*;
44
45    #[test]
46    fn phone_normalizer_preserves_canonical_e164() {
47        let normalizer = E164PhoneNumber;
48        let mut value = "  +15551234567  ".to_string();
49
50        normalizer.normalize(&mut value).unwrap();
51
52        assert_eq!(value, "+15551234567");
53    }
54
55    #[test]
56    fn phone_normalizer_adds_plus_to_plain_digits() {
57        let normalizer = E164PhoneNumber;
58        let mut value = "15551234567".to_string();
59
60        normalizer.normalize(&mut value).unwrap();
61
62        assert_eq!(value, "+15551234567");
63    }
64
65    #[test]
66    fn phone_normalizer_preserves_non_digit_input_for_validator_rejection() {
67        let normalizer = E164PhoneNumber;
68        let mut value = "+1 (555) 123-4567".to_string();
69
70        normalizer.normalize(&mut value).unwrap();
71
72        assert_eq!(value, "+1 (555) 123-4567");
73    }
74}