phonenumber_fixed/
validator.rs1use either::*;
16
17use crate::metadata::{DATABASE, Database, Metadata};
18use crate::country;
19use crate::phone_number::{Type, PhoneNumber};
20use crate::consts;
21use crate::parser::helper::Number as ParseNumber;
22use crate::parser;
23
24#[derive(Copy, Clone, Eq, PartialEq, Debug)]
26pub enum Validation {
27 IsPossible,
29
30 IsPossibleLocalOnly,
35
36 InvalidCountryCode,
38
39 TooShort,
41
42 InvalidLength,
46
47 TooLong,
49}
50
51impl Validation {
52 pub fn is_possible(&self) -> bool {
54 match *self {
55 Validation::IsPossible |
56 Validation::IsPossibleLocalOnly =>
57 true,
58
59 _ =>
60 false,
61 }
62 }
63
64 pub fn is_invalid(&self) -> bool {
66 match *self {
67 Validation::InvalidCountryCode |
68 Validation::TooShort |
69 Validation::InvalidLength |
70 Validation::TooLong =>
71 true,
72
73 _ =>
74 false,
75 }
76 }
77
78 pub fn is_invalid_length(&self) -> bool {
80 match *self {
81 Validation::TooShort |
82 Validation::InvalidLength |
83 Validation::TooLong =>
84 true,
85
86 _ =>
87 false,
88 }
89 }
90}
91
92pub fn is_viable<S: AsRef<str>>(string: S) -> bool {
94 let string = string.as_ref();
95
96 if string.len() < consts::MIN_LENGTH_FOR_NSN {
97 return false;
98 }
99
100 parser::valid::phone_number(string).is_ok()
101}
102
103pub fn is_valid(number: &PhoneNumber) -> bool {
105 is_valid_with(&*DATABASE, number)
106}
107
108pub fn is_valid_with(database: &Database, number: &PhoneNumber) -> bool {
110 let code = number.country().code();
111 let national = number.national.to_string();
112 let source = try_opt!(false; source_for(database, code, &national));
113 let meta = try_opt!(false; match source {
114 Left(region) =>
115 database.by_id(region.as_ref()),
116
117 Right(code) =>
118 database.by_code(&code).and_then(|m| m.into_iter().next()),
119 });
120
121 number_type(meta, &national) != Type::Unknown
122}
123
124pub fn length(meta: &Metadata, number: &ParseNumber, kind: Type) -> Validation {
125 let desc = if let Some(desc) = meta.descriptors().get(kind) { desc } else {
126 return Validation::InvalidLength;
127 };
128
129 let length = number.national.len() as u16;
130 let local = &desc.possible_local_length[..];
131 let possible = if desc.possible_length.is_empty() {
132 &desc.possible_length[..]
133 }
134 else {
135 &meta.descriptors.general.possible_length[..]
136 };
137
138 if possible.is_empty() {
139 return Validation::InvalidLength;
140 }
141
142 let minimum = possible[0];
143
144 if local.contains(&length) {
145 Validation::IsPossibleLocalOnly
146 }
147 else if length == minimum {
148 Validation::IsPossible
149 }
150 else if length < minimum {
151 Validation::TooShort
152 }
153 else if length > *possible.last().unwrap() {
154 Validation::TooLong
155 }
156 else if possible.contains(&length) {
157 Validation::IsPossible
158 }
159 else {
160 Validation::InvalidLength
161 }
162}
163
164pub fn source_for(database: &Database, code: u16, national: &str) -> Option<Either<country::Id, u16>> {
166 let regions = try_opt!(None; database.region(&code));
167
168 if regions.len() == 1 {
169 return if regions[0] == "001" {
170 Some(Right(code))
171 } else {
172 match regions[0].parse() {
173 Ok(value) => Some(Left(value)),
174 Err(_) => None,
175 }
176 }
177 }
178
179 for region in regions {
180 let meta = database.by_id(region).unwrap();
181
182 if let Some(pattern) = meta.leading_digits.as_ref() {
183 if let Some(index) = pattern.find(national) {
184 if index.start() == 0 {
185 return Some(Left(region.parse().unwrap()));
186 }
187 }
188 }
189 else if number_type(meta, national) != Type::Unknown {
190 return Some(Left(region.parse().unwrap()));
191 }
192 }
193
194 None
195}
196
197pub fn number_type(meta: &Metadata, value: &str) -> Type {
198 if !meta.descriptors.general.is_match(value) {
199 return Type::Unknown;
200 }
201
202 if meta.descriptors.premium_rate.as_ref().map(|d| d.is_match(value)).unwrap_or(false) {
203 return Type::PremiumRate;
204 }
205
206 if meta.descriptors.toll_free.as_ref().map(|d| d.is_match(value)).unwrap_or(false) {
207 return Type::TollFree;
208 }
209
210 if meta.descriptors.shared_cost.as_ref().map(|d| d.is_match(value)).unwrap_or(false) {
211 return Type::SharedCost;
212 }
213
214 if meta.descriptors.voip.as_ref().map(|d| d.is_match(value)).unwrap_or(false) {
215 return Type::Voip;
216 }
217
218 if meta.descriptors.personal_number.as_ref().map(|d| d.is_match(value)).unwrap_or(false) {
219 return Type::PersonalNumber;
220 }
221
222 if meta.descriptors.pager.as_ref().map(|d| d.is_match(value)).unwrap_or(false) {
223 return Type::Pager;
224 }
225
226 if meta.descriptors.uan.as_ref().map(|d| d.is_match(value)).unwrap_or(false) {
227 return Type::Uan;
228 }
229
230 if meta.descriptors.voicemail.as_ref().map(|d| d.is_match(value)).unwrap_or(false) {
231 return Type::Voicemail;
232 }
233
234 if meta.descriptors.fixed_line.as_ref().map(|d| d.is_match(value)).unwrap_or(false) {
235 if meta.descriptors.fixed_line.as_ref().map(|d| d.national_number.as_str()) ==
236 meta.descriptors.mobile.as_ref().map(|d| d.national_number.as_str())
237 {
238 return Type::FixedLineOrMobile;
239 }
240
241 if meta.descriptors.mobile.as_ref().map(|d| d.is_match(value)).unwrap_or(false) {
242 return Type::FixedLineOrMobile;
243 }
244
245 return Type::FixedLine;
246 }
247
248 if meta.descriptors.mobile.as_ref().map(|d| d.is_match(value)).unwrap_or(false) {
249 return Type::Mobile;
250 }
251
252 Type::Unknown
253}
254
255#[cfg(test)]
256mod test {
257 use crate::validator;
258 use crate::parser;
259 use crate::country;
260
261 #[test]
262 fn validate() {
263 assert!(validator::is_valid(&parser::parse(
264 Some(country::US), "+1 6502530000").unwrap()));
265
266 assert!(validator::is_valid(&parser::parse(
267 Some(country::IT), "+39 0236618300").unwrap()));
268
269 assert!(validator::is_valid(&parser::parse(
270 Some(country::GB), "+44 7912345678").unwrap()));
271
272 assert!(validator::is_valid(&parser::parse(
273 None, "+800 12345678").unwrap()));
274
275 assert!(validator::is_valid(&parser::parse(
276 None, "+979 123456789").unwrap()));
277
278 assert!(validator::is_valid(&parser::parse(
279 None, "+64 21387835").unwrap()));
280
281 assert!(!validator::is_valid(&parser::parse(
282 None, "+1 2530000").unwrap()));
283
284 assert!(!validator::is_valid(&parser::parse(
285 None, "+39 023661830000").unwrap()));
286
287 assert!(!validator::is_valid(&parser::parse(
288 None, "+44 791234567").unwrap()));
289
290 assert!(!validator::is_valid(&parser::parse(
291 None, "+49 1234").unwrap()));
292
293 assert!(!validator::is_valid(&parser::parse(
294 None, "+64 3316005").unwrap()));
295
296 assert!(!validator::is_valid(&parser::parse(
297 None, "+3923 2366").unwrap()));
298
299 assert!(!validator::is_valid(&parser::parse(
300 None, "+800 123456789").unwrap()));
301 }
302}