diff --git a/src/carrier.rs b/src/carrier.rs index 8207b27..8e1f2ac 100644 --- a/src/carrier.rs +++ b/src/carrier.rs @@ -14,34 +14,69 @@ use serde_derive::{Deserialize, Serialize}; use std::fmt; -use std::ops::Deref; + +use crate::ParseError; /// A phone number carrier. +/// +/// See: https://en.wikipedia.org/wiki/Mobile_country_code#National_operators #[derive(Clone, Eq, PartialEq, Serialize, Deserialize, Hash, Debug)] -pub struct Carrier(pub(crate) String); - -impl> From for Carrier { - fn from(value: T) -> Carrier { - Carrier(value.into()) - } +pub struct Carrier { + pub mcc: u16, // always 3 digits + pub mnc: u16, // 2 or 3 digits + pub mnc_3: bool, } -impl Deref for Carrier { - type Target = str; +impl TryFrom<&str> for Carrier { + type Error = ParseError; - fn deref(&self) -> &str { - &self.0 + fn try_from(value: &str) -> Result { + Ok(Self { + mcc: value + .get(0..3) + .and_then(|c| c.parse().ok()) + .ok_or(ParseError::InvalidCountryCode)?, + mnc: value + .get(3..) + .and_then(|c| c.parse().ok()) + .ok_or(ParseError::InvalidNetworkCode)?, + mnc_3: value.len() == 6, + }) } } -impl AsRef for Carrier { - fn as_ref(&self) -> &str { - &self.0 +impl fmt::Display for Carrier { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if self.mnc_3 { + write!(f, "{:03}{:03}", self.mcc, self.mnc) + } else { + write!(f, "{:03}{:02}", self.mcc, self.mnc) + } } } -impl fmt::Display for Carrier { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.0) +#[cfg(test)] +mod test { + use super::Carrier; + use std::convert::TryInto; + + #[test] + fn test_mobile_network_codes() { + assert_eq!( + Carrier { + mcc: 336, + mnc: 1, + mnc_3: true + }, + "336001".try_into().unwrap() + ); + assert_eq!( + Carrier { + mcc: 336, + mnc: 35, + mnc_3: false + }, + "33635".try_into().unwrap() + ); } } diff --git a/src/error.rs b/src/error.rs index 2cbcf72..6fe8d62 100644 --- a/src/error.rs +++ b/src/error.rs @@ -58,12 +58,16 @@ pub enum Parse { #[allow(unused)] // This is unused in the build script NoNumber, - /// The country code supplied did not belong to a supported country or - /// non-geographical entity. + /// The country code supplied is not correct. #[error("invalid country code")] #[allow(unused)] // This is unused in the build script InvalidCountryCode, + /// The network code supplied is not correct + #[error("invalid network code")] + #[allow(unused)] // This is unused in the build script + InvalidNetworkCode, + /// This indicates the string started with an international dialing prefix, /// but after this was stripped from the number, had less digits than any /// valid phone number (including country code) could have. diff --git a/src/formatter.rs b/src/formatter.rs index 999aa0e..48f1d91 100644 --- a/src/formatter.rs +++ b/src/formatter.rs @@ -16,6 +16,7 @@ use crate::{ consts, metadata::{Database, Format, Metadata, DATABASE}, phone_number::PhoneNumber, + Carrier, }; use std::{borrow::Cow, fmt}; @@ -234,7 +235,7 @@ fn replace( meta: &Metadata, formatter: &Format, transform: Option<&str>, - carrier: Option<&str>, + carrier: Option<&Carrier>, ) -> String { formatter .pattern() @@ -249,7 +250,11 @@ fn replace( .as_str(); let format = transform.replace(consts::NP, meta.national_prefix().unwrap_or("")); let format = format.replace(consts::FG, &format!("${}", first)); - let format = format.replace(consts::CC, carrier.unwrap_or("")); + let format = if let Some(carrier) = carrier.map(|c| c.to_string()) { + format.replace(consts::CC, &carrier) + } else { + format.replace(consts::CC, "") + }; consts::FIRST_GROUP.replace(formatter.format(), &*format) } else { diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 85d0fd6..9acbeda 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::carrier::Carrier; +use std::borrow::Borrow; + use crate::consts; use crate::country; use crate::error; @@ -89,7 +90,10 @@ pub fn parse_with>( )?, extension: number.extension.map(|s| Extension(s.into_owned())), - carrier: number.carrier.map(|s| Carrier(s.into_owned())), + carrier: number.carrier.and_then(|s| { + let s: &str = s.borrow(); + s.try_into().ok() + }), }) } @@ -262,7 +266,7 @@ mod test { national: NationalNumber::new(3121286979, 0).unwrap(), extension: None, - carrier: Some("12".into()), + carrier: "12".try_into().ok(), }, parser::parse(Some(country::BR), "012 3121286979").unwrap() );