rust_persian_tools/phone_number/
mod.rs

1pub mod operators;
2
3use thiserror::Error;
4
5pub static PREFIXES: [&str; 4] = ["+98", "98", "0098", "0"];
6
7#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
8#[derive(Error, Clone, Debug, Hash, PartialEq, Eq)]
9pub enum PhoneNumberError {
10    #[error("This prefix is not a valid phone number (prefix : `{0}`)")]
11    InvalidPrefix(String),
12    #[error("The phone number format is invalid")]
13    InvalidFormat,
14    #[error("Unexpected error happened !")]
15    Unknown,
16}
17
18/// This is a simple function that checks if a phone number valid or not
19///
20/// # Examples
21///
22/// ```
23/// use rust_persian_tools::phone_number::is_phone_valid;
24///
25/// assert!(is_phone_valid("+989122221811").is_ok());
26/// assert!(is_phone_valid("12903908").is_err());
27/// ```
28pub fn is_phone_valid(phone_number: impl AsRef<str>) -> Result<(), PhoneNumberError> {
29    let phone_number = phone_number.as_ref();
30
31    let prefix = get_phone_prefix(phone_number).unwrap_or("");
32
33    let phone_number_without_prefix = &phone_number[prefix.len()..];
34
35    if phone_number_without_prefix.len() == 10 && phone_number_without_prefix.starts_with('9') {
36        return Ok(());
37    }
38
39    Err(PhoneNumberError::InvalidFormat)
40}
41
42/// returns phone prefix for example +98 98 based on given phone number
43/// This function returns an `Option<&str>`, where `Some(prefix)` contains the extracted prefix,
44/// and `None` is returned if no valid prefix is found.
45///
46/// # Warning
47/// This function is designed to only works for Iran phone number prefixes ("+98", "98", "0098", "0")
48///
49///
50/// # Examples
51///
52/// ```
53/// use rust_persian_tools::phone_number::get_phone_prefix;
54///
55/// assert_eq!(get_phone_prefix("00989122221811").unwrap(),"0098");
56/// assert_eq!(get_phone_prefix("09122221811").unwrap(),"0");
57/// assert!(get_phone_prefix("29122221811").is_err());
58/// ```
59pub fn get_phone_prefix(phone_number: impl AsRef<str>) -> Result<&'static str, PhoneNumberError> {
60    let phone_number = phone_number.as_ref();
61
62    let prefix = PREFIXES
63        .into_iter()
64        .find(|&prefix| phone_number.starts_with(prefix));
65
66    match prefix {
67        Some(pre) => Ok(pre),
68        None => Err(PhoneNumberError::InvalidFormat),
69    }
70}
71
72/// replaces current phone number prefix with your desired prefix
73///
74/// # Warning
75/// if phone number is not valid it would return None
76///
77/// # Examples
78///
79/// ```
80/// use rust_persian_tools::phone_number::phone_number_normalizer;
81///
82/// assert_eq!(phone_number_normalizer("+989373708555", "0").unwrap(),"09373708555".to_string());
83/// assert!(phone_number_normalizer("09132222", "+98").is_err());
84/// ```
85pub fn phone_number_normalizer(
86    phone_number: impl AsRef<str>,
87    new_prefix: impl AsRef<str>,
88) -> Result<String, PhoneNumberError> {
89    let phone_number = phone_number.as_ref();
90    let new_prefix = new_prefix.as_ref();
91
92    is_phone_valid(phone_number)?;
93
94    if let Ok(prefix) = get_phone_prefix(phone_number) {
95        let (_, splitted) = phone_number.split_at(prefix.len());
96        return Ok(format!("{new_prefix}{splitted}"));
97    }
98
99    Ok(format!("{new_prefix}{phone_number}"))
100}
101
102/// returns operator prefix of phone number (919,912,...)
103///
104/// # Warning
105/// if phone number is not valid it would return None
106///
107///
108/// # Examples
109///
110/// ```
111/// use rust_persian_tools::phone_number::get_operator_prefix;
112///
113/// assert_eq!(get_operator_prefix("00989013708555").unwrap() , "901");
114/// assert!(get_operator_prefix("00988013708555").is_err());
115/// ```
116pub fn get_operator_prefix(phone_number: &str) -> Result<&str, PhoneNumberError> {
117    is_phone_valid(phone_number)?;
118
119    for prefix in PREFIXES {
120        if phone_number.starts_with(prefix) {
121            return Ok(&phone_number[prefix.len()..prefix.len() + 3]);
122        }
123    }
124
125    Err(PhoneNumberError::InvalidFormat)
126}
127
128#[cfg(test)]
129mod tests {
130    use super::*;
131
132    #[test]
133    fn check_phone_number_valid() {
134        assert!(is_phone_valid("9122221811").is_ok());
135        assert!(is_phone_valid("09122221811").is_ok());
136        assert!(is_phone_valid("+989122221811").is_ok());
137        assert!(is_phone_valid("12903908").is_err());
138        assert!(is_phone_valid("901239812390812908").is_err());
139    }
140
141    #[test]
142    fn test_phone_number_normalizer() {
143        // normalize to 0
144
145        assert_eq!(
146            phone_number_normalizer("+989373708555", "0").unwrap(),
147            "09373708555".to_string()
148        );
149
150        assert_eq!(
151            phone_number_normalizer("989373708555", "0").unwrap(),
152            "09373708555".to_string()
153        );
154
155        assert_eq!(
156            phone_number_normalizer("00989022002580", "0").unwrap(),
157            "09022002580".to_string()
158        );
159        assert_eq!(
160            phone_number_normalizer("09122002580", "0").unwrap(),
161            "09122002580".to_string()
162        );
163        assert_eq!(
164            phone_number_normalizer("9322002580", "0").unwrap(),
165            "09322002580".to_string()
166        );
167
168        // normalize to +98
169        assert_eq!(
170            phone_number_normalizer("09373708555", "+98").unwrap(),
171            "+989373708555".to_string()
172        );
173        assert_eq!(
174            phone_number_normalizer("09022002580", "+98").unwrap(),
175            "+989022002580".to_string()
176        );
177        assert_eq!(
178            phone_number_normalizer("09122002580", "+98").unwrap(),
179            "+989122002580".to_string()
180        );
181        assert_eq!(
182            phone_number_normalizer("9322002580", "+98").unwrap(),
183            "+989322002580".to_string()
184        );
185        assert_eq!(
186            phone_number_normalizer("00989022002580", "+98").unwrap(),
187            "+989022002580".to_string()
188        );
189    }
190
191    #[test]
192    fn test_phone_number_normalizer_invalid_phone() {
193        assert!(phone_number_normalizer("09132222", "+98").is_err());
194        assert!(phone_number_normalizer("9191282819921", "0").is_err());
195    }
196
197    #[test]
198    fn test_operator_prefix() {
199        assert_eq!(get_operator_prefix("+989373708555").unwrap(), "937");
200        assert_eq!(get_operator_prefix("00989013708555").unwrap(), "901");
201        assert!(get_operator_prefix("00988013708555").is_err());
202    }
203}