1use crate::carrier::Carrier;
16use crate::consts;
17use crate::country;
18use crate::error;
19use crate::extension::Extension;
20use crate::metadata::{Database, DATABASE};
21use crate::national_number::NationalNumber;
22use crate::phone_number::{PhoneNumber, Type};
23use crate::validator::{self, Validation};
24use nom::{branch::alt, IResult};
25
26#[macro_use]
27pub mod helper;
28pub mod natural;
29pub mod rfc3966;
30pub mod valid;
31
32pub fn parse<S: AsRef<str>>(
34 country: Option<country::Id>,
35 string: S,
36) -> Result<PhoneNumber, error::Parse> {
37 parse_with(&DATABASE, country, string)
38}
39
40pub fn parse_with<S: AsRef<str>>(
42 database: &Database,
43 country: Option<country::Id>,
44 string: S,
45) -> Result<PhoneNumber, error::Parse> {
46 fn phone_number(i: &str) -> IResult<&str, helper::Number<'_>> {
47 parse! { i => alt((rfc3966::phone_number, natural::phone_number)) }
48 }
49
50 let (_, mut number) = phone_number(string.as_ref()).or(Err(error::Parse::NoNumber))?;
52
53 number = helper::country_code(database, country, number)?;
55
56 if let Some(meta) = country.and_then(|c| database.by_id(c.as_ref())) {
58 let mut potential = helper::national_number(meta, number.clone());
59
60 if let Some(prefix) = meta.national_prefix.as_ref() {
62 if potential.national.starts_with(prefix) {
63 potential.national = helper::trim(potential.national, prefix.len());
64 }
65 }
66
67 if validator::length(meta, &potential, Type::Unknown) != Validation::TooShort {
68 number = potential;
69 }
70 }
71
72 if number.national.len() < consts::MIN_LENGTH_FOR_NSN {
73 return Err(error::Parse::TooShortNsn);
74 }
75
76 if number.national.len() > consts::MAX_LENGTH_FOR_NSN {
77 return Err(error::Parse::TooLong);
78 }
79
80 Ok(PhoneNumber {
81 code: country::Code {
82 value: number.prefix.map(|p| p.parse()).unwrap_or(Ok(0))?,
83 source: number.country,
84 },
85
86 national: NationalNumber::new(
87 number.national.parse()?,
88 number.national.chars().take_while(|&c| c == '0').count() as u8,
89 )?,
90
91 extension: number.extension.map(|s| Extension(s.into_owned())),
92 carrier: number.carrier.map(|s| Carrier(s.into_owned())),
93 })
94}
95
96#[cfg(test)]
97mod test {
98 use crate::country;
99 use crate::national_number::NationalNumber;
100 use crate::parser;
101 use crate::phone_number::PhoneNumber;
102
103 #[test]
104 fn parse() {
105 let mut number = PhoneNumber {
106 code: country::Code {
107 value: 64,
108 source: country::Source::Default,
109 },
110
111 national: NationalNumber::new(33316005, 0).unwrap(),
112
113 extension: None,
114 carrier: None,
115 };
116
117 number.code.source = country::Source::Default;
118 assert_eq!(
119 number,
120 parser::parse(Some(country::NZ), "033316005").unwrap()
121 );
122 assert_eq!(
123 number,
124 parser::parse(Some(country::NZ), "33316005").unwrap()
125 );
126 assert_eq!(
127 number,
128 parser::parse(Some(country::NZ), "03-331 6005").unwrap()
129 );
130 assert_eq!(
131 number,
132 parser::parse(Some(country::NZ), "03 331 6005").unwrap()
133 );
134
135 number.code.source = country::Source::Plus;
136 assert_eq!(
137 number,
138 parser::parse(Some(country::NZ), "tel:03-331-6005;phone-context=+64").unwrap()
139 );
140 assert_eq!(
144 number,
145 parser::parse(Some(country::NZ), "tel:03-331-6005;phone-context=+64;a=%A1").unwrap()
146 );
147 assert_eq!(
148 number,
149 parser::parse(
150 Some(country::NZ),
151 "tel:03-331-6005;isub=12345;phone-context=+64"
152 )
153 .unwrap()
154 );
155 assert_eq!(
156 number,
157 parser::parse(Some(country::NZ), "tel:+64-3-331-6005;isub=12345").unwrap()
158 );
159 assert_eq!(
160 number,
161 parser::parse(Some(country::NZ), "03-331-6005;phone-context=+64").unwrap()
162 );
163
164 number.code.source = country::Source::Idd;
165 assert_eq!(
166 number,
167 parser::parse(Some(country::NZ), "0064 3 331 6005").unwrap()
168 );
169 assert_eq!(
170 number,
171 parser::parse(Some(country::US), "01164 3 331 6005").unwrap()
172 );
173
174 number.code.source = country::Source::Plus;
175 assert_eq!(
176 number,
177 parser::parse(Some(country::US), "+64 3 331 6005").unwrap()
178 );
179
180 assert_eq!(
181 number,
182 parser::parse(Some(country::US), "+01164 3 331 6005").unwrap()
183 );
184 assert_eq!(
185 number,
186 parser::parse(Some(country::NZ), "+0064 3 331 6005").unwrap()
187 );
188 assert_eq!(
189 number,
190 parser::parse(Some(country::NZ), "+ 00 64 3 331 6005").unwrap()
191 );
192
193 let number = PhoneNumber {
194 code: country::Code {
195 value: 64,
196 source: country::Source::Number,
197 },
198
199 national: NationalNumber::new(64123456, 0).unwrap(),
200
201 extension: None,
202 carrier: None,
203 };
204
205 assert_eq!(
206 number,
207 parser::parse(Some(country::NZ), "64(0)64123456").unwrap()
208 );
209
210 assert_eq!(
211 PhoneNumber {
212 code: country::Code {
213 value: 49,
214 source: country::Source::Default,
215 },
216
217 national: NationalNumber::new(30123456, 0).unwrap(),
218
219 extension: None,
220 carrier: None,
221 },
222 parser::parse(Some(country::DE), "301/23456").unwrap()
223 );
224
225 assert_eq!(
226 PhoneNumber {
227 code: country::Code {
228 value: 81,
229 source: country::Source::Plus,
230 },
231
232 national: NationalNumber::new(2345, 0,).unwrap(),
233
234 extension: None,
235 carrier: None,
236 },
237 parser::parse(Some(country::JP), "+81 *2345").unwrap()
238 );
239
240 assert_eq!(
241 PhoneNumber {
242 code: country::Code {
243 value: 64,
244 source: country::Source::Default,
245 },
246
247 national: NationalNumber::new(12, 0,).unwrap(),
248
249 extension: None,
250 carrier: None,
251 },
252 parser::parse(Some(country::NZ), "12").unwrap()
253 );
254
255 assert_eq!(
256 PhoneNumber {
257 code: country::Code {
258 value: 55,
259 source: country::Source::Default,
260 },
261
262 national: NationalNumber::new(3121286979, 0).unwrap(),
263
264 extension: None,
265 carrier: Some("12".into()),
266 },
267 parser::parse(Some(country::BR), "012 3121286979").unwrap()
268 );
269 }
270
271 #[test]
272 fn issue_43() {
273 let res = parser::parse(None, " 2 22#:");
274 assert!(res.is_err());
275 }
276
277 #[test]
278 fn advisory_1() {
279 let res = parser::parse(None, ".;phone-context=");
280 assert!(res.is_err(), "{res:?}");
281 }
282
283 #[test]
284 fn advisory_2() {
285 let res = parser::parse(None, "+dwPAA;phone-context=AA");
286 assert!(res.is_err(), "{res:?}");
287 }
288}