Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions src/formatter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,45 @@ mod test {
use crate::formatter::Mode;
use crate::parser;

#[test]
fn fr_leading_zero() {
assert_eq!(
"06 31 96 65 43",
parser::parse(Some(country::FR), "+330631966543")
.unwrap()
.format()
.mode(Mode::National)
.to_string()
);

assert_eq!(
"+33 6 31 96 65 43",
parser::parse(Some(country::FR), "+330631966543")
.unwrap()
.format()
.mode(Mode::International)
.to_string()
);

assert_eq!(
"+33631966543",
parser::parse(Some(country::FR), "+330631966543")
.unwrap()
.format()
.mode(Mode::E164)
.to_string()
);

assert_eq!(
"tel:+33-6-31-96-65-43",
parser::parse(Some(country::FR), "+330631966543")
.unwrap()
.format()
.mode(Mode::Rfc3966)
.to_string()
);
}

#[test]
fn us() {
assert_eq!(
Expand Down
13 changes: 12 additions & 1 deletion src/national_number.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,25 @@
// limitations under the License.

use serde_derive::{Deserialize, Serialize};
use std::fmt;
use std::{fmt, str::FromStr};

/// The national number part of a phone number.
#[derive(Copy, Clone, Eq, PartialEq, Serialize, Deserialize, Hash, Debug)]
pub struct NationalNumber {
value: u64,
}

impl FromStr for NationalNumber {
type Err = crate::error::Parse;

fn from_str(s: &str) -> Result<Self, Self::Err> {
let zeros = s.chars().take_while(|&c| c == '0').count();
let value = s[zeros..].parse::<u64>()?;

NationalNumber::new(value, zeros as u8)
}
}

impl NationalNumber {
pub fn new(value: u64, zeros: u8) -> Result<Self, crate::error::Parse> {
// E.164 specifies a maximum of 15 decimals, which corresponds to slightly over 48.9 bits.
Expand Down
184 changes: 85 additions & 99 deletions src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ use crate::country;
use crate::error;
use crate::extension::Extension;
use crate::metadata::{Database, DATABASE};
use crate::national_number::NationalNumber;
use crate::phone_number::{PhoneNumber, Type};
use crate::validator::{self, Validation};
use nom::{branch::alt, IResult};
Expand Down Expand Up @@ -53,8 +52,27 @@ pub fn parse_with<S: AsRef<str>>(
// Normalize the number and extract country code.
number = helper::country_code(database, country, number)?;

// If no country was supplied, try to determine the metadata from the number.
// We need to determine the country to be able to classify the national prefix if present.
let meta = if let Some(country) = &country {
database.by_id(country.as_ref())
} else {
let code = country::Code {
value: number.prefix.clone().map(|p| p.parse()).unwrap_or(Ok(0))?,
source: number.country,
};

use either::{Left, Right};
let without_zeros = number.national.trim_start_matches('0');
match validator::source_for(database, code.value(), without_zeros) {
Some(Left(region)) => database.by_id(region.as_ref()),
Some(Right(code)) => database.by_code(&code).and_then(|m| m.into_iter().next()),
None => None,
}
};

// Extract carrier and strip national prefix if present.
if let Some(meta) = country.and_then(|c| database.by_id(c.as_ref())) {
if let Some(meta) = meta {
let mut potential = helper::national_number(meta, number.clone());

// Strip national prefix if present.
Expand Down Expand Up @@ -83,10 +101,7 @@ pub fn parse_with<S: AsRef<str>>(
source: number.country,
},

national: NationalNumber::new(
number.national.parse()?,
number.national.chars().take_while(|&c| c == '0').count() as u8,
)?,
national: number.national.parse()?,

extension: number.extension.map(|s| Extension(s.into_owned())),
carrier: number.carrier.map(|s| Carrier(s.into_owned())),
Expand All @@ -95,118 +110,80 @@ pub fn parse_with<S: AsRef<str>>(

#[cfg(test)]
mod test {
use crate::country;
use crate::country::{self, Source};
use crate::metadata::DATABASE;
use crate::national_number::NationalNumber;
use crate::parser;
use crate::phone_number::PhoneNumber;

#[test]
fn parse() {
let mut number = PhoneNumber {
code: country::Code {
value: 64,
source: country::Source::Default,
},
use rstest::*;

#[rstest]
#[case(Source::Default, country::NZ, "033316005")]
#[case(Source::Default, country::NZ, "33316005")]
#[case(Source::Default, country::NZ, "03-331 6005")]
#[case(Source::Default, country::NZ, "03 331 6005")]
#[case(Source::Plus, country::NZ, "tel:03-331-6005;phone-context=+64")]
// FIXME: What the fuck is this.
// #[case(Source::Plus, country::NZ, "tel:331-6005;phone-context=+64-3")]
#[case(Source::Plus, country::NZ, "tel:03-331-6005;phone-context=+64;a=%A1")]
#[case(
Source::Plus,
country::NZ,
"tel:03-331-6005;isub=12345;phone-context=+64"
)]
#[case(Source::Plus, country::NZ, "tel:+64-3-331-6005;isub=12345")]
#[case(Source::Plus, country::NZ, "03-331-6005;phone-context=+64")]
// Idd
#[case(Source::Idd, country::NZ, "0064 3 331 6005")]
#[case(Source::Idd, country::US, "01164 3 331 6005")]
// Plus
#[case(Source::Plus, country::US, "+64 3 331 6005")]
#[case(Source::Plus, country::US, "+01164 3 331 6005")]
#[case(Source::Plus, country::NZ, "+0064 3 331 6005")]
#[case(Source::Plus, country::NZ, "+ 00 64 3 331 6005")]
fn parse_1(
#[case] source: Source,
#[case] country: impl Into<Option<country::Id>>,
#[case] number: &'static str,
) {
let reference = PhoneNumber {
code: country::Code { value: 64, source },

national: NationalNumber::new(33316005, 0).unwrap(),

extension: None,
carrier: None,
};

number.code.source = country::Source::Default;
assert_eq!(
number,
parser::parse(Some(country::NZ), "033316005").unwrap()
);
assert_eq!(
number,
parser::parse(Some(country::NZ), "33316005").unwrap()
);
assert_eq!(
number,
parser::parse(Some(country::NZ), "03-331 6005").unwrap()
);
assert_eq!(
number,
parser::parse(Some(country::NZ), "03 331 6005").unwrap()
);
let country = country.into();
println!("parsing {} with country {:?}", number, country);
let parsed = parser::parse(country, number).unwrap();
println!("number type: {:?}", parsed.number_type(&DATABASE));
println!("parsed: {:?}", parsed);

number.code.source = country::Source::Plus;
assert_eq!(
number,
parser::parse(Some(country::NZ), "tel:03-331-6005;phone-context=+64").unwrap()
);
// FIXME: What the fuck is this.
// assert_eq!(number, parser::parse(Some(country::NZ), "tel:331-6005;phone-context=+64-3").unwrap());
// assert_eq!(number, parser::parse(Some(country::NZ), "tel:331-6005;phone-context=+64-3").unwrap());
assert_eq!(
number,
parser::parse(Some(country::NZ), "tel:03-331-6005;phone-context=+64;a=%A1").unwrap()
);
assert_eq!(
number,
parser::parse(
Some(country::NZ),
"tel:03-331-6005;isub=12345;phone-context=+64"
)
.unwrap()
);
assert_eq!(
number,
parser::parse(Some(country::NZ), "tel:+64-3-331-6005;isub=12345").unwrap()
);
assert_eq!(
number,
parser::parse(Some(country::NZ), "03-331-6005;phone-context=+64").unwrap()
);

number.code.source = country::Source::Idd;
assert_eq!(
number,
parser::parse(Some(country::NZ), "0064 3 331 6005").unwrap()
);
assert_eq!(
number,
parser::parse(Some(country::US), "01164 3 331 6005").unwrap()
);
assert_eq!(reference, parsed);
}

number.code.source = country::Source::Plus;
#[test]
fn parse_2() {
assert_eq!(
number,
parser::parse(Some(country::US), "+64 3 331 6005").unwrap()
);
PhoneNumber {
code: country::Code {
value: 64,
source: Source::Number,
},

assert_eq!(
number,
parser::parse(Some(country::US), "+01164 3 331 6005").unwrap()
);
assert_eq!(
number,
parser::parse(Some(country::NZ), "+0064 3 331 6005").unwrap()
);
assert_eq!(
number,
parser::parse(Some(country::NZ), "+ 00 64 3 331 6005").unwrap()
);
national: NationalNumber::new(64123456, 0).unwrap(),

let number = PhoneNumber {
code: country::Code {
value: 64,
source: country::Source::Number,
extension: None,
carrier: None,
},

national: NationalNumber::new(64123456, 0).unwrap(),

extension: None,
carrier: None,
};

assert_eq!(
number,
parser::parse(Some(country::NZ), "64(0)64123456").unwrap()
);
}

#[test]
fn parse_3() {
assert_eq!(
PhoneNumber {
code: country::Code {
Expand All @@ -221,7 +198,10 @@ mod test {
},
parser::parse(Some(country::DE), "301/23456").unwrap()
);
}

#[test]
fn parse_4() {
assert_eq!(
PhoneNumber {
code: country::Code {
Expand All @@ -236,7 +216,10 @@ mod test {
},
parser::parse(Some(country::JP), "+81 *2345").unwrap()
);
}

#[test]
fn parse_5() {
assert_eq!(
PhoneNumber {
code: country::Code {
Expand All @@ -251,7 +234,10 @@ mod test {
},
parser::parse(Some(country::NZ), "12").unwrap()
);
}

#[test]
fn parse_6() {
assert_eq!(
PhoneNumber {
code: country::Code {
Expand Down
Loading
Loading