1use std::{borrow::Cow, fmt};
16use crate::{
17 metadata::{DATABASE, Database, Metadata, Format},
18 phone_number::PhoneNumber,
19 consts
20};
21
22#[derive(Copy, Clone, Eq, PartialEq, Debug)]
24pub enum Mode {
25 E164,
27
28 International,
31
32 National,
34
35 Rfc3966,
37}
38
39#[derive(Copy, Clone, Debug)]
41pub struct Formatter<'n, 'd, 'f> {
42 number: &'n PhoneNumber,
43 database: Option<&'d Database>,
44 mode: Mode,
45 format: Option<&'f Format>,
46}
47
48impl<'n, 'd, 'f> Formatter<'n, 'd, 'f> {
49 pub fn database<'a>(self, database: &'a Database) -> Formatter<'n, 'a, 'f> {
51 Formatter {
52 number: self.number,
53 database: Some(database),
54 mode: self.mode,
55 format: self.format,
56 }
57 }
58
59 pub fn mode(mut self, mode: Mode) -> Formatter<'n, 'd, 'f> {
61 self.mode = mode;
62 self
63 }
64
65 pub fn with<'a>(self, format: &'a Format) -> Formatter<'n, 'd, 'a> {
67 Formatter {
68 number: self.number,
69 database: self.database,
70 mode: self.mode,
71 format: Some(format),
72 }
73 }
74}
75
76pub fn format<'n>(number: &'n PhoneNumber) -> Formatter<'n, 'static, 'static> {
78 Formatter {
79 number: number,
80 database: None,
81 mode: Mode::E164,
82 format: None,
83 }
84}
85
86pub fn format_with<'d, 'n>(database: &'d Database, number: &'n PhoneNumber) -> Formatter<'n, 'd, 'static> {
89 Formatter {
90 number: number,
91 database: Some(database),
92 mode: Mode::E164,
93 format: None,
94 }
95}
96
97impl<'n, 'd, 'f> fmt::Display for Formatter<'n, 'd, 'f> {
98 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
99 let db = self.database.unwrap_or(&*DATABASE);
100
101 let meta = try_opt!(Err(fmt::Error);
103 db.by_code(&self.number.country().code()).map(|m|
104 m.into_iter().next().unwrap()));
105
106 let national = self.number.national().to_string();
107 let formatter = self.format.or_else(|| formatter(&national,
108 if meta.international_formats().is_empty() || self.mode == Mode::National {
109 meta.formats()
110 }
111 else {
112 meta.international_formats()
113 }));
114
115 match self.mode {
116 Mode::E164 => {
118 write!(f, "+{}{}", self.number.country().code(), national)?;
119 }
120
121 Mode::International => {
123 write!(f, "+{} ", self.number.country().code())?;
124
125 if let Some(formatter) = formatter {
126 write!(f, "{}", replace(&national, meta, formatter, None, None))?;
127 }
128 else {
129 write!(f, "{}", national)?;
130 }
131
132 if let Some(ext) = self.number.extension() {
133 write!(f, "{}{}", meta.preferred_extension_prefix()
134 .unwrap_or(" ext. "), ext)?;
135 }
136 }
137
138 Mode::National => {
139 if let Some(formatter) = formatter {
140 let carrier = self.number.carrier().and_then(|c|
141 formatter.domestic_carrier().map(|f| (c, f)));
142
143 if let Some((carrier, format)) = carrier {
144 write!(f, "{}", replace(&national, meta, formatter, Some(format), Some(carrier)))?;
145 }
146 else if let Some(prefix) = formatter.national_prefix() {
147 write!(f, "{}", replace(&national, meta, formatter, Some(prefix), None))?;
148 }
149 else {
150 write!(f, "{}", replace(&national, meta, formatter, None, None))?;
151 }
152 }
153 else {
154 write!(f, "{}", national)?;
155 }
156
157 if let Some(ext) = self.number.extension() {
158 write!(f, "{}{}", meta.preferred_extension_prefix().unwrap_or(" ext. "), ext)?;
159 }
160 }
161
162 Mode::Rfc3966 => {
163 write!(f, "tel:+{}-", self.number.country().code())?;
164
165 if let Some(formatter) = formatter {
166 write!(f, "{}", consts::SEPARATOR_PATTERN.replace_all(
167 &replace(&national, meta, formatter, None, None), "-"))?;
168 }
169 else {
170 write!(f, "{}", national)?;
171 }
172
173 if let Some(ext) = self.number.extension() {
174 write!(f, ";ext={}", ext)?;
175 }
176 }
177 }
178
179 Ok(())
180 }
181}
182
183fn formatter<'a>(number: &str, formats: &'a [Format]) -> Option<&'a Format> {
184 for format in formats {
185 let leading = format.leading_digits();
186
187 if leading.is_empty() || leading.last().unwrap().find(&number).map(|m| m.start() == 0).unwrap_or(false) {
188 if format.pattern().find(&number).map(|m| m.start() == 0 && m.end() == number.len()).unwrap_or(false) {
189 return Some(format);
190 }
191 }
192 }
193
194 None
195}
196
197fn replace(national: &str, meta: &Metadata, formatter: &Format, transform: Option<&str>, carrier: Option<&str>) -> String {
198 formatter.pattern().replace(national, &*if let Some(transform) = transform {
199 let first = consts::FIRST_GROUP.captures(&formatter.format()).unwrap().get(1).unwrap().as_str();
200 let format = transform.replace(*consts::NP, meta.national_prefix().unwrap_or(""));
201 let format = format.replace(*consts::FG, &*format!("${}", first));
202 let format = format.replace(*consts::CC, carrier.unwrap_or(""));
203
204 consts::FIRST_GROUP.replace(formatter.format(), &*format)
205 }
206 else {
207 Cow::Borrowed(formatter.format())
208 }).into()
209}
210
211#[cfg(test)]
212mod test {
213 use crate::parser;
214 use crate::formatter::Mode;
215 use crate::country;
216
217 #[test]
218 fn us() {
219 assert_eq!("(650) 253-0000",
220 parser::parse(Some(country::US), "+1 6502530000").unwrap()
221 .format().mode(Mode::National).to_string());
222
223 assert_eq!("+1 650-253-0000",
224 parser::parse(Some(country::US), "+1 6502530000").unwrap()
225 .format().mode(Mode::International).to_string());
226
227 assert_eq!("(800) 253-0000",
228 parser::parse(Some(country::US), "+1 8002530000").unwrap()
229 .format().mode(Mode::National).to_string());
230
231 assert_eq!("+1 800-253-0000",
232 parser::parse(Some(country::US), "+1 8002530000").unwrap()
233 .format().mode(Mode::International).to_string());
234
235 assert_eq!("(900) 253-0000",
236 parser::parse(Some(country::US), "+1 9002530000").unwrap()
237 .format().mode(Mode::National).to_string());
238
239 assert_eq!("+1 900-253-0000",
240 parser::parse(Some(country::US), "+1 9002530000").unwrap()
241 .format().mode(Mode::International).to_string());
242
243 assert_eq!("tel:+1-900-253-0000",
244 parser::parse(Some(country::US), "+1 9002530000").unwrap()
245 .format().mode(Mode::Rfc3966).to_string());
246 }
247
248 #[test]
249 fn gb() {
250 assert_eq!("020 7031 3000",
251 parser::parse(Some(country::GB), "+44 2070313000").unwrap()
252 .format().mode(Mode::National).to_string());
253
254 assert_eq!("+44 20 7031 3000",
255 parser::parse(Some(country::GB), "+44 2070313000").unwrap()
256 .format().mode(Mode::International).to_string());
257
258 assert_eq!("020 7031 3000",
259 parser::parse(Some(country::GB), "+44 2070313000").unwrap()
260 .format().mode(Mode::National).to_string());
261
262 assert_eq!("07912 345678",
263 parser::parse(Some(country::GB), "+44 7912345678").unwrap()
264 .format().mode(Mode::National).to_string());
265
266 assert_eq!("+44 7912 345678",
267 parser::parse(Some(country::GB), "+44 7912345678").unwrap()
268 .format().mode(Mode::International).to_string());
269 }
270}