phonenumber_fixed/parser/
mod.rs

1// Copyright (C) 2017 1aim GmbH
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use crate::metadata::{DATABASE, Database};
16use crate::phone_number::{PhoneNumber, Type};
17use crate::national_number::NationalNumber;
18use crate::country;
19use crate::extension::Extension;
20use crate::carrier::Carrier;
21use crate::consts;
22use crate::validator::{self, Validation};
23use crate::error;
24
25use nom::{IResult, branch::alt};
26
27#[macro_use]
28pub mod helper;
29pub mod valid;
30pub mod rfc3966;
31pub mod natural;
32
33/// Parse a phone number.
34pub fn parse<S: AsRef<str>>(country: Option<country::Id>, string: S) -> Result<PhoneNumber, error::Parse> {
35	parse_with(&*DATABASE, country, string)
36}
37
38/// Parse a phone number using a specific `Database`.
39pub fn parse_with<S: AsRef<str>>(database: &Database, country: Option<country::Id>, string: S) -> Result<PhoneNumber, error::Parse> {
40	fn phone_number(i: &str) -> IResult<&str, helper::Number> {
41		parse! { i => alt((rfc3966::phone_number, natural::phone_number)) }
42	}
43
44	// Try to parse the number as RFC3966 or natural language.
45	let (_, mut number) = phone_number(string.as_ref())
46		.or(Err(error::Parse::NoNumber))?;
47
48	// Normalize the number and extract country code.
49	number = helper::country_code(database, country, number)?;
50
51	// Extract carrier and strip national prefix if present.
52	if let Some(meta) = country.and_then(|c| database.by_id(c.as_ref())) {
53		let mut potential = helper::national_number(meta, number.clone());
54
55		// Strip national prefix if present.
56		if let Some(prefix) = meta.national_prefix.as_ref() {
57			if potential.national.starts_with(prefix) {
58				potential.national = helper::trim(potential.national, prefix.len());
59			}
60		}
61
62		if validator::length(meta, &potential, Type::Unknown) != Validation::TooShort {
63			number = potential;
64		}
65	}
66
67	if number.national.len() < consts::MIN_LENGTH_FOR_NSN {
68		return Err(error::Parse::TooShortNsn.into());
69	}
70
71	if number.national.len() > consts::MAX_LENGTH_FOR_NSN {
72		return Err(error::Parse::TooLong.into());
73	}
74
75	Ok(PhoneNumber {
76		code: country::Code {
77			value:  number.prefix.map(|p| p.parse()).unwrap_or(Ok(0))?,
78			source: number.country,
79		},
80
81		national: NationalNumber {
82			value: number.national.parse()?,
83			zeros: number.national.chars().take_while(|&c| c == '0').count() as u8,
84		},
85
86		extension: number.extension.map(|s| Extension(s.into_owned())),
87		carrier:   number.carrier.map(|s| Carrier(s.into_owned())),
88	})
89}
90
91#[cfg(test)]
92mod test {
93	use crate::parser;
94	use crate::phone_number::PhoneNumber;
95	use crate::national_number::NationalNumber;
96	use crate::country;
97
98	#[test]
99	fn parse() {
100		let mut number = PhoneNumber {
101			code: country::Code {
102				value:  64,
103				source: country::Source::Default,
104			},
105
106			national: NationalNumber {
107				value: 33316005,
108				zeros: 0,
109			},
110
111			extension: None,
112			carrier:   None,
113		};
114
115		number.code.source = country::Source::Default;
116		assert_eq!(number, parser::parse(Some(country::NZ), "033316005").unwrap());
117		assert_eq!(number, parser::parse(Some(country::NZ), "33316005").unwrap());
118		assert_eq!(number, parser::parse(Some(country::NZ), "03-331 6005").unwrap());
119		assert_eq!(number, parser::parse(Some(country::NZ), "03 331 6005").unwrap());
120
121		number.code.source = country::Source::Plus;
122		assert_eq!(number, parser::parse(Some(country::NZ), "tel:03-331-6005;phone-context=+64").unwrap());
123		// FIXME: What the fuck is this.
124		// assert_eq!(number, parser::parse(Some(country::NZ), "tel:331-6005;phone-context=+64-3").unwrap());
125		// assert_eq!(number, parser::parse(Some(country::NZ), "tel:331-6005;phone-context=+64-3").unwrap());
126		assert_eq!(number, parser::parse(Some(country::NZ), "tel:03-331-6005;phone-context=+64;a=%A1").unwrap());
127		assert_eq!(number, parser::parse(Some(country::NZ), "tel:03-331-6005;isub=12345;phone-context=+64").unwrap());
128		assert_eq!(number, parser::parse(Some(country::NZ), "tel:+64-3-331-6005;isub=12345").unwrap());
129		assert_eq!(number, parser::parse(Some(country::NZ), "03-331-6005;phone-context=+64").unwrap());
130
131		number.code.source = country::Source::Idd;
132		assert_eq!(number, parser::parse(Some(country::NZ), "0064 3 331 6005").unwrap());
133		assert_eq!(number, parser::parse(Some(country::US), "01164 3 331 6005").unwrap());
134
135		number.code.source = country::Source::Plus;
136		assert_eq!(number, parser::parse(Some(country::US), "+64 3 331 6005").unwrap());
137
138		assert_eq!(number, parser::parse(Some(country::US), "+01164 3 331 6005").unwrap());
139		assert_eq!(number, parser::parse(Some(country::NZ), "+0064 3 331 6005").unwrap());
140		assert_eq!(number, parser::parse(Some(country::NZ), "+ 00 64 3 331 6005").unwrap());
141
142		let number = PhoneNumber {
143			code: country::Code {
144				value:  64,
145				source: country::Source::Number,
146			},
147
148			national: NationalNumber {
149				value: 64123456,
150				zeros: 0,
151			},
152
153			extension: None,
154			carrier:   None,
155		};
156
157		assert_eq!(number, parser::parse(Some(country::NZ), "64(0)64123456").unwrap());
158
159		assert_eq!(PhoneNumber {
160			code: country::Code {
161				value:  49,
162				source: country::Source::Default,
163			},
164
165			national: NationalNumber {
166				value: 30123456,
167				zeros: 0,
168			},
169
170			extension: None,
171			carrier:   None,
172		}, parser::parse(Some(country::DE), "301/23456").unwrap());
173
174		assert_eq!(PhoneNumber {
175			code: country::Code {
176				value:  81,
177				source: country::Source::Plus,
178			},
179
180			national: NationalNumber {
181				value: 2345,
182				zeros: 0,
183			},
184
185			extension: None,
186			carrier:   None,
187		}, parser::parse(Some(country::JP), "+81 *2345").unwrap());
188
189		assert_eq!(PhoneNumber {
190			code: country::Code {
191				value:  64,
192				source: country::Source::Default,
193			},
194
195			national: NationalNumber {
196				value: 12,
197				zeros: 0,
198			},
199
200			extension: None,
201			carrier:   None,
202		}, parser::parse(Some(country::NZ), "12").unwrap());
203
204		assert_eq!(PhoneNumber {
205			code: country::Code {
206				value:  55,
207				source: country::Source::Default,
208			},
209
210			national: NationalNumber {
211				value: 3121286979,
212				zeros: 0,
213			},
214
215			extension: None,
216			carrier:   Some("12".into()),
217		}, parser::parse(Some(country::BR), "012 3121286979").unwrap());
218	}
219}