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 if contains_invalid_character(&phone_number) {
11 return false;
12 }
13
14 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 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 let country = extract_country_data(&phone_number)?;
34
35 phone_number.replace_range(0..country.prefix.to_string().len(), "");
37
38 leading_zero_remover(phone_number);
40
41 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 leading_zero_remover(phone_number);
51}
52
53
54fn contains_invalid_character(phone_number: &String) -> bool {
55 let mut parentheses_count = 0;
56 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 phone_number.retain(|c| c.is_numeric());
75}
76
77fn leading_zero_remover(phone_number: &mut String) {
78 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 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}