|
|
|
|
@@ -21,11 +21,11 @@ use std::{
|
|
|
|
|
|
|
|
|
|
use super::phone_number_regexps_and_mappings::PhoneNumberRegExpsAndMappings;
|
|
|
|
|
use crate::{
|
|
|
|
|
errors::NotANumberError, region_code::RegionCode, interfaces::MatcherApi, macros::owned_from_cow_or, phonemetadata::PhoneMetadataCollection, phonenumberutil::{
|
|
|
|
|
region_code::RegionCode, interfaces::MatcherApi, macros::owned_from_cow_or, PhoneMetadataCollection, phonenumberutil::{
|
|
|
|
|
errors::{
|
|
|
|
|
ExtractNumberError, GetExampleNumberError, InternalLogicError,
|
|
|
|
|
InvalidMetadataForValidRegionError, InvalidNumberError, ParseError,
|
|
|
|
|
ValidationError,
|
|
|
|
|
ValidationError, NotANumberError
|
|
|
|
|
}, helper_constants::{
|
|
|
|
|
DEFAULT_EXTN_PREFIX, MAX_LENGTH_COUNTRY_CODE, MAX_LENGTH_FOR_NSN, MIN_LENGTH_FOR_NSN,
|
|
|
|
|
NANPA_COUNTRY_CODE, PLUS_SIGN, REGION_CODE_FOR_NON_GEO_ENTITY, RFC3966_EXTN_PREFIX,
|
|
|
|
|
@@ -37,8 +37,8 @@ use crate::{
|
|
|
|
|
test_number_length_with_unknown_type,
|
|
|
|
|
}, helper_types::{PhoneNumberWithCountryCodeSource}, MatchType, PhoneNumberFormat, PhoneNumberType, NumberLengthType
|
|
|
|
|
},
|
|
|
|
|
phonemetadata::{NumberFormat, PhoneMetadata, PhoneNumberDesc},
|
|
|
|
|
phonenumber::{phone_number::CountryCodeSource, PhoneNumber},
|
|
|
|
|
NumberFormat, PhoneMetadata, PhoneNumberDesc,
|
|
|
|
|
CountryCodeSource, PhoneNumber,
|
|
|
|
|
regex_based_matcher::RegexBasedMatcher, regex_util::{RegexConsume, RegexFullMatch}, regexp_cache::ErrorInvalidRegex, string_util::strip_cow_prefix
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
@@ -189,10 +189,11 @@ impl PhoneNumberUtil {
|
|
|
|
|
/// # Arguments
|
|
|
|
|
///
|
|
|
|
|
/// * `region_code` - The region code for which to get the types.
|
|
|
|
|
pub fn get_supported_types_for_region(
|
|
|
|
|
pub fn get_supported_types_for_region<S: AsRef<str>>(
|
|
|
|
|
&self,
|
|
|
|
|
region_code: &str,
|
|
|
|
|
region_code: S,
|
|
|
|
|
) -> Option<HashSet<PhoneNumberType>> {
|
|
|
|
|
let region_code = region_code.as_ref();
|
|
|
|
|
self.region_to_metadata_map
|
|
|
|
|
.get(region_code)
|
|
|
|
|
.and_then(|metadata| Some(get_supported_types_for_metadata(metadata)))
|
|
|
|
|
@@ -227,12 +228,12 @@ impl PhoneNumberUtil {
|
|
|
|
|
return &self.reg_exps.extn_patterns_for_matching;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn starts_with_plus_chars_pattern(&self, phone_number: &str) -> bool {
|
|
|
|
|
self.reg_exps.plus_chars_pattern.matches_start(phone_number)
|
|
|
|
|
pub fn starts_with_plus_chars_pattern<S: AsRef<str>>(&self, phone_number: S) -> bool {
|
|
|
|
|
self.reg_exps.plus_chars_pattern.matches_start(phone_number.as_ref())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn contains_only_valid_digits(&self, s: &str) -> bool {
|
|
|
|
|
self.reg_exps.digits_pattern.full_match(s)
|
|
|
|
|
pub fn contains_only_valid_digits<S: AsRef<str>>(&self, s: S) -> bool {
|
|
|
|
|
self.reg_exps.digits_pattern.full_match(s.as_ref())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub(crate) fn trim_unwanted_end_chars<'a>(&self, phone_number: &'a str) -> &'a str {
|
|
|
|
|
@@ -704,11 +705,12 @@ impl PhoneNumberUtil {
|
|
|
|
|
///
|
|
|
|
|
/// * `phone_number` - The phone number to format.
|
|
|
|
|
/// * `carrier_code` - The carrier code to include in the formatted number.
|
|
|
|
|
pub fn format_national_number_with_carrier_code(
|
|
|
|
|
pub fn format_national_number_with_carrier_code<S: AsRef<str>>(
|
|
|
|
|
&self,
|
|
|
|
|
phone_number: &PhoneNumber,
|
|
|
|
|
carrier_code: &str,
|
|
|
|
|
carrier_code: S,
|
|
|
|
|
) -> RegexResult<String> {
|
|
|
|
|
let carrier_code= carrier_code.as_ref();
|
|
|
|
|
let country_calling_code = phone_number.country_code();
|
|
|
|
|
let national_significant_number = self.get_national_significant_number(phone_number);
|
|
|
|
|
let region_code = self.get_region_code_for_country_code(country_calling_code);
|
|
|
|
|
@@ -752,15 +754,15 @@ impl PhoneNumberUtil {
|
|
|
|
|
///
|
|
|
|
|
/// * `phone_number` - The phone number to format.
|
|
|
|
|
/// * `fallback_carrier_code` - The carrier code to use if a preferred one is not available.
|
|
|
|
|
pub fn format_national_number_with_preferred_carrier_code(
|
|
|
|
|
pub fn format_national_number_with_preferred_carrier_code<S: AsRef<str>>(
|
|
|
|
|
&self,
|
|
|
|
|
phone_number: &PhoneNumber,
|
|
|
|
|
fallback_carrier_code: &str,
|
|
|
|
|
fallback_carrier_code: S,
|
|
|
|
|
) -> RegexResult<String> {
|
|
|
|
|
let carrier_code = if !phone_number.preferred_domestic_carrier_code().is_empty() {
|
|
|
|
|
phone_number.preferred_domestic_carrier_code()
|
|
|
|
|
} else {
|
|
|
|
|
fallback_carrier_code
|
|
|
|
|
fallback_carrier_code.as_ref()
|
|
|
|
|
};
|
|
|
|
|
self.format_national_number_with_carrier_code(phone_number, carrier_code)
|
|
|
|
|
}
|
|
|
|
|
@@ -782,12 +784,13 @@ impl PhoneNumberUtil {
|
|
|
|
|
/// * `phone_number` - The phone number to format.
|
|
|
|
|
/// * `calling_from` - The region where the call is being placed.
|
|
|
|
|
/// * `with_formatting` - Whether to include formatting characters.
|
|
|
|
|
pub fn format_number_for_mobile_dialing<'b>(
|
|
|
|
|
pub fn format_number_for_mobile_dialing<'b, S: AsRef<str>>(
|
|
|
|
|
&self,
|
|
|
|
|
phone_number: &'b PhoneNumber,
|
|
|
|
|
calling_from: &str,
|
|
|
|
|
calling_from: S,
|
|
|
|
|
with_formatting: bool,
|
|
|
|
|
) -> InternalLogicResult<Cow<'b, str>> {
|
|
|
|
|
let calling_from = calling_from.as_ref();
|
|
|
|
|
let country_calling_code = phone_number.country_code();
|
|
|
|
|
if !self.has_valid_country_calling_code(country_calling_code) {
|
|
|
|
|
return if phone_number.has_raw_input() {
|
|
|
|
|
@@ -1120,8 +1123,8 @@ impl PhoneNumberUtil {
|
|
|
|
|
/// # Arguments
|
|
|
|
|
///
|
|
|
|
|
/// * `phone_number` - The phone number string to normalize.
|
|
|
|
|
pub fn normalize_digits_only<'a>(&self, phone_number: &'a str) -> String {
|
|
|
|
|
dec_from_char::normalize_decimals_filtering(phone_number)
|
|
|
|
|
pub fn normalize_digits_only<S: AsRef<str>>(&self, phone_number: S) -> String {
|
|
|
|
|
dec_from_char::normalize_decimals_filtering(phone_number.as_ref())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Formats a phone number for calling from outside the number's region.
|
|
|
|
|
@@ -1130,11 +1133,12 @@ impl PhoneNumberUtil {
|
|
|
|
|
///
|
|
|
|
|
/// * `phone_number` - The phone number to format.
|
|
|
|
|
/// * `calling_from` - The region where the call is being placed from.
|
|
|
|
|
pub fn format_out_of_country_calling_number<'a>(
|
|
|
|
|
pub fn format_out_of_country_calling_number<'a, S: AsRef<str>>(
|
|
|
|
|
&self,
|
|
|
|
|
phone_number: &'a PhoneNumber,
|
|
|
|
|
calling_from: &str,
|
|
|
|
|
calling_from: S,
|
|
|
|
|
) -> InternalLogicResult<Cow<'a, str>> {
|
|
|
|
|
let calling_from = calling_from.as_ref();
|
|
|
|
|
let Some(metadata_calling_from) = self.region_to_metadata_map.get(calling_from) else {
|
|
|
|
|
trace!(
|
|
|
|
|
"Trying to format number from invalid region {calling_from}\
|
|
|
|
|
@@ -1253,11 +1257,12 @@ impl PhoneNumberUtil {
|
|
|
|
|
///
|
|
|
|
|
/// * `phone_number` - The phone number to format.
|
|
|
|
|
/// * `region_calling_from` - The region from which the number was originally parsed.
|
|
|
|
|
pub fn format_in_original_format<'a>(
|
|
|
|
|
pub fn format_in_original_format<'a, S: AsRef<str>>(
|
|
|
|
|
&self,
|
|
|
|
|
phone_number: &'a PhoneNumber,
|
|
|
|
|
region_calling_from: &str,
|
|
|
|
|
region_calling_from: S,
|
|
|
|
|
) -> InternalLogicResult<Cow<'a, str>> {
|
|
|
|
|
let region_calling_from = region_calling_from.as_ref();
|
|
|
|
|
if phone_number.has_raw_input() && !self.has_formatting_pattern_for_number(phone_number)? {
|
|
|
|
|
// We check if we have the formatting pattern because without that, we might
|
|
|
|
|
// format the number as a group without national prefix.
|
|
|
|
|
@@ -1404,8 +1409,10 @@ impl PhoneNumberUtil {
|
|
|
|
|
///
|
|
|
|
|
/// * `number_to_parse` - The number string to parse.
|
|
|
|
|
/// * `default_region` - The region to assume if the number is not in international format.
|
|
|
|
|
pub fn parse(&self, number_to_parse: &str, default_region: &str) -> ParseResult<PhoneNumber> {
|
|
|
|
|
self.parse_helper(number_to_parse, default_region, false, true)
|
|
|
|
|
pub fn parse<S1: AsRef<str>, S2: AsRef<str>>(
|
|
|
|
|
&self, number_to_parse: S1, default_region: S2
|
|
|
|
|
) -> ParseResult<PhoneNumber> {
|
|
|
|
|
self.parse_helper(number_to_parse.as_ref(), default_region.as_ref(), false, true)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Parses a string into a phone number object, keeping the raw input.
|
|
|
|
|
@@ -1414,12 +1421,12 @@ impl PhoneNumberUtil {
|
|
|
|
|
///
|
|
|
|
|
/// * `number_to_parse` - The number string to parse.
|
|
|
|
|
/// * `default_region` - The region to assume if the number is not in international format.
|
|
|
|
|
pub fn parse_and_keep_raw_input(
|
|
|
|
|
pub fn parse_and_keep_raw_input<S1: AsRef<str>, S2: AsRef<str>>(
|
|
|
|
|
&self,
|
|
|
|
|
number_to_parse: &str,
|
|
|
|
|
default_region: &str,
|
|
|
|
|
number_to_parse: S1,
|
|
|
|
|
default_region: S2,
|
|
|
|
|
) -> ParseResult<PhoneNumber> {
|
|
|
|
|
self.parse_helper(number_to_parse, default_region, true, true)
|
|
|
|
|
self.parse_helper(number_to_parse.as_ref(), default_region.as_ref(), true, true)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Checks if a phone number is valid.
|
|
|
|
|
@@ -1438,11 +1445,12 @@ impl PhoneNumberUtil {
|
|
|
|
|
///
|
|
|
|
|
/// * `phone_number` - The phone number to validate.
|
|
|
|
|
/// * `region_code` - The region to validate against.
|
|
|
|
|
pub fn is_valid_number_for_region(
|
|
|
|
|
pub fn is_valid_number_for_region<S: AsRef<str>>(
|
|
|
|
|
&self,
|
|
|
|
|
phone_number: &PhoneNumber,
|
|
|
|
|
region_code: &str,
|
|
|
|
|
region_code: S,
|
|
|
|
|
) -> bool {
|
|
|
|
|
let region_code = region_code.as_ref();
|
|
|
|
|
let country_code = phone_number.country_code();
|
|
|
|
|
let metadata = self.get_metadata_for_region_or_calling_code(country_code, region_code);
|
|
|
|
|
if let Some(metadata) = metadata.filter(|metadata| {
|
|
|
|
|
@@ -1465,11 +1473,12 @@ impl PhoneNumberUtil {
|
|
|
|
|
///
|
|
|
|
|
/// * `phone_number` - The phone number to format.
|
|
|
|
|
/// * `calling_from` - The region where the call is being placed from.
|
|
|
|
|
pub fn format_out_of_country_keeping_alpha_chars<'a>(
|
|
|
|
|
pub fn format_out_of_country_keeping_alpha_chars<'a, S: AsRef<str>>(
|
|
|
|
|
&self,
|
|
|
|
|
phone_number: &'a PhoneNumber,
|
|
|
|
|
calling_from: &str,
|
|
|
|
|
calling_from: S,
|
|
|
|
|
) -> InternalLogicResult<Cow<'a, str>> {
|
|
|
|
|
let calling_from = calling_from.as_ref();
|
|
|
|
|
// If there is no raw input, then we can't keep alpha characters because there
|
|
|
|
|
// aren't any. In this case, we return FormatOutOfCountryCallingNumber.
|
|
|
|
|
if phone_number.raw_input().is_empty() {
|
|
|
|
|
@@ -1797,11 +1806,13 @@ impl PhoneNumberUtil {
|
|
|
|
|
///
|
|
|
|
|
/// * `number` - The number string to check.
|
|
|
|
|
/// * `region_code` - The region to check against.
|
|
|
|
|
pub fn is_possible_number_for_string(
|
|
|
|
|
pub fn is_possible_number_for_string<S1: AsRef<str>, S2: AsRef<str>>(
|
|
|
|
|
&self,
|
|
|
|
|
phone_number: &str,
|
|
|
|
|
region_dialing_from: &str,
|
|
|
|
|
phone_number: S1,
|
|
|
|
|
region_dialing_from: S2,
|
|
|
|
|
) -> bool {
|
|
|
|
|
let phone_number = phone_number.as_ref();
|
|
|
|
|
let region_dialing_from = region_dialing_from.as_ref();
|
|
|
|
|
match self.parse(phone_number, region_dialing_from) {
|
|
|
|
|
Ok(number_proto) => self.is_possible_number(&number_proto),
|
|
|
|
|
|
|
|
|
|
@@ -2037,7 +2048,8 @@ impl PhoneNumberUtil {
|
|
|
|
|
/// method does not require the number to be normalized in advance - but does
|
|
|
|
|
/// assume that leading non-number symbols have been removed, such as by the
|
|
|
|
|
/// method `ExtractPossibleNumber`.
|
|
|
|
|
pub fn is_viable_phone_number(&self, phone_number: &str) -> bool {
|
|
|
|
|
pub fn is_viable_phone_number<S: AsRef<str>>(&self, phone_number: S) -> bool {
|
|
|
|
|
let phone_number = phone_number.as_ref();
|
|
|
|
|
if phone_number.len() < MIN_LENGTH_FOR_NSN {
|
|
|
|
|
false
|
|
|
|
|
} else {
|
|
|
|
|
@@ -2216,8 +2228,8 @@ impl PhoneNumberUtil {
|
|
|
|
|
/// # Arguments
|
|
|
|
|
///
|
|
|
|
|
/// * `region_code` - The region for which to get an example number.
|
|
|
|
|
pub fn get_example_number(&self, region_code: &str) -> ExampleNumberResult {
|
|
|
|
|
self.get_example_number_for_type_and_region_code(region_code, PhoneNumberType::FixedLine)
|
|
|
|
|
pub fn get_example_number<S: AsRef<str>>(&self, region_code: S) -> ExampleNumberResult {
|
|
|
|
|
self.get_example_number_for_type_and_region_code(region_code.as_ref(), PhoneNumberType::FixedLine)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Gets an example of an invalid phone number for a given region.
|
|
|
|
|
@@ -2225,7 +2237,8 @@ impl PhoneNumberUtil {
|
|
|
|
|
/// # Arguments
|
|
|
|
|
///
|
|
|
|
|
/// * `region_code` - The region for which to get an invalid example number.
|
|
|
|
|
pub fn get_invalid_example_number(&self, region_code: &str) -> ExampleNumberResult {
|
|
|
|
|
pub fn get_invalid_example_number<S: AsRef<str>>(&self, region_code: S) -> ExampleNumberResult {
|
|
|
|
|
let region_code = region_code.as_ref();
|
|
|
|
|
let Some(region_metadata) = self.region_to_metadata_map.get(region_code) else {
|
|
|
|
|
warn!("Invalid or unknown region code ({}) provided.", region_code);
|
|
|
|
|
return Err(GetExampleNumberError::InvalidMetadata);
|
|
|
|
|
@@ -2281,11 +2294,12 @@ impl PhoneNumberUtil {
|
|
|
|
|
///
|
|
|
|
|
/// * `region_code` - The region for which to get an example number.
|
|
|
|
|
/// * `number_type` - The type of number to get an example for.
|
|
|
|
|
pub fn get_example_number_for_type_and_region_code(
|
|
|
|
|
pub fn get_example_number_for_type_and_region_code<S: AsRef<str>>(
|
|
|
|
|
&self,
|
|
|
|
|
region_code: &str,
|
|
|
|
|
region_code: S,
|
|
|
|
|
phone_number_type: PhoneNumberType,
|
|
|
|
|
) -> ExampleNumberResult {
|
|
|
|
|
let region_code = region_code.as_ref();
|
|
|
|
|
let Some(region_metadata) = self.region_to_metadata_map.get(region_code) else {
|
|
|
|
|
warn!("Invalid or unknown region code ({}) provided.", region_code);
|
|
|
|
|
return Err(GetExampleNumberError::InvalidMetadata);
|
|
|
|
|
@@ -2471,7 +2485,8 @@ impl PhoneNumberUtil {
|
|
|
|
|
/// # Arguments
|
|
|
|
|
///
|
|
|
|
|
/// * `phone_number` - Number to normalize.
|
|
|
|
|
pub fn normalize(&self, phone_number: &str) -> String {
|
|
|
|
|
pub fn normalize<S: AsRef<str>>(&self, phone_number: S) -> String {
|
|
|
|
|
let phone_number = phone_number.as_ref();
|
|
|
|
|
if self
|
|
|
|
|
.reg_exps
|
|
|
|
|
.valid_alpha_phone_pattern
|
|
|
|
|
@@ -2788,8 +2803,10 @@ impl PhoneNumberUtil {
|
|
|
|
|
/// # Arguments
|
|
|
|
|
///
|
|
|
|
|
/// * `phone_number` - The phone number string with alpha characters.
|
|
|
|
|
pub fn convert_alpha_characters_in_number(&self, phone_number: &str) -> String {
|
|
|
|
|
normalize_helper(&self.reg_exps.alpha_phone_mappings, false, phone_number)
|
|
|
|
|
pub fn convert_alpha_characters_in_number<S: AsRef<str>>(&self, phone_number: S) -> String {
|
|
|
|
|
normalize_helper(
|
|
|
|
|
&self.reg_exps.alpha_phone_mappings, false, phone_number.as_ref()
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Checks whether two phone numbers match.
|
|
|
|
|
@@ -2856,11 +2873,13 @@ impl PhoneNumberUtil {
|
|
|
|
|
///
|
|
|
|
|
/// * `number1` - The first phone number to compare.
|
|
|
|
|
/// * `number2` - The second phone number to compare.
|
|
|
|
|
pub fn is_number_match_with_two_strings(
|
|
|
|
|
pub fn is_number_match_with_two_strings<S1: AsRef<str>, S2: AsRef<str>>(
|
|
|
|
|
&self,
|
|
|
|
|
first_number: &str,
|
|
|
|
|
second_number: &str,
|
|
|
|
|
first_number: S1,
|
|
|
|
|
second_number: S2,
|
|
|
|
|
) -> MatchResult {
|
|
|
|
|
let first_number = first_number.as_ref();
|
|
|
|
|
let second_number = second_number.as_ref();
|
|
|
|
|
match self.parse(first_number, RegionCode::get_unknown()) {
|
|
|
|
|
Ok(first_number_as_proto) => {
|
|
|
|
|
return self.is_number_match_with_one_string(&first_number_as_proto, second_number);
|
|
|
|
|
@@ -2899,11 +2918,13 @@ impl PhoneNumberUtil {
|
|
|
|
|
///
|
|
|
|
|
/// * `number1` - The first phone number to compare.
|
|
|
|
|
/// * `number2` - The second phone number to compare.
|
|
|
|
|
pub fn is_number_match_with_one_string(
|
|
|
|
|
pub fn is_number_match_with_one_string<S: AsRef<str>>(
|
|
|
|
|
&self,
|
|
|
|
|
first_number: &PhoneNumber,
|
|
|
|
|
second_number: &str,
|
|
|
|
|
second_number: S,
|
|
|
|
|
) -> MatchResult {
|
|
|
|
|
let second_number = second_number.as_ref();
|
|
|
|
|
|
|
|
|
|
// First see if the second number has an implicit country calling code, by
|
|
|
|
|
// attempting to parse it.
|
|
|
|
|
match self.parse(second_number, RegionCode::get_unknown()) {
|
|
|
|
|
@@ -2945,13 +2966,13 @@ impl PhoneNumberUtil {
|
|
|
|
|
/// # Arguments
|
|
|
|
|
///
|
|
|
|
|
/// * `phone_number` - The string to check.
|
|
|
|
|
pub fn is_alpha_number(&self, phone_number: &str) -> bool {
|
|
|
|
|
if !self.is_viable_phone_number(phone_number) {
|
|
|
|
|
pub fn is_alpha_number<S: AsRef<str>>(&self, phone_number: S) -> bool {
|
|
|
|
|
if !self.is_viable_phone_number(phone_number.as_ref()) {
|
|
|
|
|
// Number is too short, or doesn't match the basic phone number pattern.
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
// Copy the number, since we are going to try and strip the extension from it.
|
|
|
|
|
let (number, _extension) = self.maybe_strip_extension(&phone_number);
|
|
|
|
|
let (number, _extension) = self.maybe_strip_extension(phone_number.as_ref());
|
|
|
|
|
return self.reg_exps.valid_alpha_phone_pattern.full_match(number);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|