phonelib/
lib.rs

1use constants::COUNTRIES;
2use definitions::Country;
3
4mod constants;
5mod definitions;
6mod tests;
7
8pub fn is_valid_phone_number(phone_number: String) -> bool {
9    // check if the phone number contains invalid character
10    if contains_invalid_character(&phone_number) {
11        return false;
12    }
13
14    // normalize the phone number and check if it is valid or not
15    normalize_phone_number(phone_number).is_some()
16}
17
18pub fn extract_country(phone_number: String) -> Option<&'static Country> {
19    let mut phone_number = phone_number;
20    remove_unwanted_character(&mut phone_number);
21    extract_country_data(&phone_number)
22}
23
24pub fn normalize_phone_number(phone_number: String) -> Option<String> {
25    // make a copy of phone number and normalize it in place and return it
26    normalize_phone_number_in_place(&mut phone_number.clone())
27}
28
29pub fn normalize_phone_number_in_place(phone_number: &mut String) -> Option<String> {
30    remove_unwanted_character(phone_number);
31
32    // extract country data
33    let country = extract_country_data(&phone_number)?;
34
35    // Remove country code from phone number
36    phone_number.replace_range(0..country.prefix.to_string().len(), "");
37
38    // Remove all leading zeros if present
39    leading_zero_remover(phone_number);
40
41    // Add country code again to the phone number and return it
42    let normalize_phone_number = format!("+{}{}", country.prefix, phone_number);
43
44    Some(normalize_phone_number)
45}
46
47fn remove_unwanted_character(phone_number: &mut String) {
48    remove_non_digit_character(phone_number);
49    // Remove leading zero before country code
50    leading_zero_remover(phone_number);
51}
52
53
54fn contains_invalid_character(phone_number: &String) -> bool {
55    let mut parentheses_count = 0;
56    // check if the phone number contains invalid character
57
58    for c in phone_number.chars() {
59        match c {
60            '0'..='9' | '-' | ' ' => {}
61            '(' => parentheses_count += 1,
62            ')' if parentheses_count == 0 => return false,
63            ')' => parentheses_count -= 1,
64            _ => return false,
65        }
66    }
67
68    parentheses_count == 0
69}
70
71
72fn remove_non_digit_character(phone_number: &mut String) {
73    // remove all non digit character
74    phone_number.retain(|c| c.is_numeric());
75}
76
77fn leading_zero_remover(phone_number: &mut String) {
78    // remove all leading zeros
79    while phone_number.starts_with('0') {
80        phone_number.remove(0);
81    }
82}
83
84fn extract_country_data(phone_number: &str) -> Option<&'static Country> {
85    // check if the phone number starts with country code or not and return country data if found
86    for country in COUNTRIES.iter() {
87        if phone_number.starts_with(&country.prefix.to_string()) {
88            if country
89                .phone_lengths
90                .contains(&(phone_number.len() as u8 - country.prefix.to_string().len() as u8))
91            {
92                return Some(country);
93            }
94        }
95    }
96    None
97}