Compare commits

..

4 Commits

Author SHA1 Message Date
Vlasislav Kashin
e72187d2d7 Better None handling: Remove unwrap 2025-07-10 12:50:12 +03:00
Vlasislav Kashin
457bb65b9a Better error handling 2025-07-10 12:41:21 +03:00
Vlasislav Kashin
3f07806990 Ugrade methods to return iterators 2025-07-10 11:29:18 +03:00
Vlasislav Kashin
4f5571f7a7 Bug fixes and upgrades 2025-07-10 10:44:11 +03:00
7 changed files with 242 additions and 229 deletions

View File

@@ -64,32 +64,6 @@ pub enum MatchType {
// Separated enum ValidationResult into ValidationResult err and // Separated enum ValidationResult into ValidationResult err and
// ValidationResultOk for using Result<Ok, Err> // ValidationResultOk for using Result<Ok, Err>
/// Possible outcomes when testing if a PhoneNumber is possible.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Error)]
pub enum ValidationResultErr {
/// The number has an invalid country calling code.
#[error("The number has an invalid country calling code")]
InvalidCountryCode,
/// The number is shorter than all valid numbers for this region.
#[error("The number is shorter than all valid numbers for this region")]
TooShort,
/// The number is longer than the shortest valid numbers for this region,
/// shorter than the longest valid numbers for this region, and does not
/// itself have a number length that matches valid numbers for this region.
/// This can also be returned in the case where
/// IsPossibleNumberForTypeWithReason was called, and there are no numbers of
/// this type at all for this region.
#[error("\
The number is longer than the shortest valid numbers for this region,\
shorter than the longest valid numbers for this region, and does not\
itself have a number length that matches valid numbers for this region\
")]
InvalidLength,
/// The number is longer than all valid numbers for this region.
#[error("The number is longer than all valid numbers for this region")]
TooLong,
}
/// Possible outcomes when testing if a PhoneNumber is possible. /// Possible outcomes when testing if a PhoneNumber is possible.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ValidNumberLenType { pub enum ValidNumberLenType {

View File

@@ -6,13 +6,12 @@ use thiserror::Error;
use crate::regexp_cache::ErrorInvalidRegex; use crate::regexp_cache::ErrorInvalidRegex;
#[derive(Debug, PartialEq, Error)] #[derive(Debug, PartialEq, Error)]
pub enum PhoneNumberUtilError { pub enum InternalLogicError {
#[error("{0}")] #[error("{0}")]
InvalidRegexError(#[from] ErrorInvalidRegex), InvalidRegexError(#[from] ErrorInvalidRegex),
#[error("Parse error: {0}")]
ParseError(#[from] ParseError), #[error("{0}")]
#[error("Extract number error: {0}")] InvalidMetadataForValidRegionError(#[from] InvalidMetadataForValidRegionError)
ExtractNumberError(#[from] ExtractNumberError)
} }
#[derive(Debug, PartialEq, Error)] #[derive(Debug, PartialEq, Error)]
@@ -50,7 +49,7 @@ pub enum GetExampleNumberError {
#[error("Parse error: {0}")] #[error("Parse error: {0}")]
ParseError(#[from] ParseError), ParseError(#[from] ParseError),
#[error("{0}")] #[error("{0}")]
InvalidRegexError(#[from] ErrorInvalidRegex), InternalLogicError(#[from] InternalLogicError),
#[error("No example number")] #[error("No example number")]
NoExampleNumberError, NoExampleNumberError,
#[error("Could not get number")] #[error("Could not get number")]
@@ -61,7 +60,35 @@ pub enum GetExampleNumberError {
#[derive(Error, Debug, PartialEq)] #[derive(Error, Debug, PartialEq)]
pub enum MatchError { #[error("Invalid number given")]
#[error("Invalid number given")] pub struct InvalidNumberError(#[from] pub ParseError); // NOT_A_NUMBER in the java version
InvalidNumber(#[from] ParseError), // NOT_A_NUMBER in the java version.
#[derive(Debug, Error, PartialEq)]
#[error("Metadata for valid region MUST not be null")]
pub struct InvalidMetadataForValidRegionError;
/// Possible outcomes when testing if a PhoneNumber is possible.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Error)]
pub enum ValidationResultErr {
/// The number has an invalid country calling code.
#[error("The number has an invalid country calling code")]
InvalidCountryCode,
/// The number is shorter than all valid numbers for this region.
#[error("The number is shorter than all valid numbers for this region")]
TooShort,
/// The number is longer than the shortest valid numbers for this region,
/// shorter than the longest valid numbers for this region, and does not
/// itself have a number length that matches valid numbers for this region.
/// This can also be returned in the case where
/// IsPossibleNumberForTypeWithReason was called, and there are no numbers of
/// this type at all for this region.
#[error("\
The number is longer than the shortest valid numbers for this region,\
shorter than the longest valid numbers for this region, and does not\
itself have a number length that matches valid numbers for this region\
")]
InvalidLength,
/// The number is longer than all valid numbers for this region.
#[error("The number is longer than all valid numbers for this region")]
TooLong,
} }

View File

@@ -15,7 +15,7 @@ pub const PLUS_CHARS: &'static str = "+\u{FF0B}";
pub const VALID_PUNCTUATION: &'static str = "-x\ pub const VALID_PUNCTUATION: &'static str = "-x\
\u{2010}-\u{2015}\u{2212}\u{30FC}\u{FF0D}-\u{FF0F} \u{00A0}\ \u{2010}-\u{2015}\u{2212}\u{30FC}\u{FF0D}-\u{FF0F} \u{00A0}\
\u{00AD}\u{200B}\u{2060}\u{3000}()\u{FF08}\u{FF09}\u{FF3B}\ \u{00AD}\u{200B}\u{2060}\u{3000}()\u{FF08}\u{FF09}\u{FF3B}\
\u{FF3D}.[]/~\u{2053}\u{223C}"; \u{FF3D}.\\[\\]/~\u{2053}\u{223C}";
// Regular expression of characters typically used to start a second phone // Regular expression of characters typically used to start a second phone
// number for the purposes of parsing. This allows us to strip off parts of // number for the purposes of parsing. This allows us to strip off parts of

View File

@@ -12,7 +12,8 @@ use crate::{
}; };
use super::{ use super::{
PhoneNumberFormat, PhoneNumberType, ValidNumberLenType, ValidationResultErr, PhoneNumberFormat, PhoneNumberType, ValidNumberLenType,
errors::ValidationResultErr,
helper_constants::{ helper_constants::{
METADATA, OPTIONAL_EXT_SUFFIX, PLUS_SIGN, POSSIBLE_CHARS_AFTER_EXT_LABEL, METADATA, OPTIONAL_EXT_SUFFIX, PLUS_SIGN, POSSIBLE_CHARS_AFTER_EXT_LABEL,
POSSIBLE_SEPARATORS_BETWEEN_NUMBER_AND_EXT_LABEL, RFC3966_EXTN_PREFIX, RFC3966_PREFIX, POSSIBLE_SEPARATORS_BETWEEN_NUMBER_AND_EXT_LABEL, RFC3966_EXTN_PREFIX, RFC3966_PREFIX,
@@ -20,14 +21,14 @@ use super::{
}; };
/// Loads metadata from helper constants METADATA array /// Loads metadata from helper constants METADATA array
pub(super) fn load_compiled_metadata() -> Result<PhoneMetadataCollection, protobuf::Error> { pub fn load_compiled_metadata() -> Result<PhoneMetadataCollection, protobuf::Error> {
let result = PhoneMetadataCollection::parse_from_bytes(&METADATA)?; let result = PhoneMetadataCollection::parse_from_bytes(&METADATA)?;
Ok(result) Ok(result)
} }
/// Returns a pointer to the description inside the metadata of the appropriate /// Returns a pointer to the description inside the metadata of the appropriate
/// type. /// type.
pub(super) fn get_number_desc_by_type( pub fn get_number_desc_by_type(
metadata: &PhoneMetadata, metadata: &PhoneMetadata,
phone_number_type: PhoneNumberType, phone_number_type: PhoneNumberType,
) -> &PhoneNumberDesc { ) -> &PhoneNumberDesc {
@@ -48,7 +49,7 @@ pub(super) fn get_number_desc_by_type(
} }
/// A helper function that is used by Format and FormatByPattern. /// A helper function that is used by Format and FormatByPattern.
pub(super) fn prefix_number_with_country_calling_code( pub fn prefix_number_with_country_calling_code(
country_calling_code: i32, country_calling_code: i32,
number_format: PhoneNumberFormat, number_format: PhoneNumberFormat,
formatted_number: &mut String, formatted_number: &mut String,
@@ -91,7 +92,7 @@ pub(super) fn prefix_number_with_country_calling_code(
// Returns true when one national number is the suffix of the other or both are // Returns true when one national number is the suffix of the other or both are
// the same. // the same.
pub(super) fn is_national_number_suffix_of_the_other( pub fn is_national_number_suffix_of_the_other(
first_number: &PhoneNumber, first_number: &PhoneNumber,
second_number: &PhoneNumber, second_number: &PhoneNumber,
) -> bool { ) -> bool {
@@ -106,7 +107,7 @@ pub(super) fn is_national_number_suffix_of_the_other(
/// Helper method for constructing regular expressions for parsing. Creates an /// Helper method for constructing regular expressions for parsing. Creates an
/// expression that captures up to max_length digits. /// expression that captures up to max_length digits.
pub(super) fn extn_digits(max_length: u32) -> String { pub fn extn_digits(max_length: u32) -> String {
let mut buf = itoa::Buffer::new(); let mut buf = itoa::Buffer::new();
let max_length_str = buf.format(max_length); let max_length_str = buf.format(max_length);
const HELPER_STR_LEN: usize = 2 + 4 + 2; const HELPER_STR_LEN: usize = 2 + 4 + 2;
@@ -131,7 +132,7 @@ pub(super) fn extn_digits(max_length: u32) -> String {
// number is changed, MaybeStripExtension needs to be updated. // number is changed, MaybeStripExtension needs to be updated.
// - The only capturing groups should be around the digits that you want to // - The only capturing groups should be around the digits that you want to
// capture as part of the extension, or else parsing will fail! // capture as part of the extension, or else parsing will fail!
pub(super) fn create_extn_pattern(for_parsing: bool) -> String { pub fn create_extn_pattern(for_parsing: bool) -> String {
// We cap the maximum length of an extension based on the ambiguity of the // We cap the maximum length of an extension based on the ambiguity of the
// way the extension is prefixed. As per ITU, the officially allowed // way the extension is prefixed. As per ITU, the officially allowed
// length for extensions is actually 40, but we don't support this since we // length for extensions is actually 40, but we don't support this since we
@@ -255,7 +256,7 @@ pub(super) fn create_extn_pattern(for_parsing: bool) -> String {
/// left unchanged in the number. /// left unchanged in the number.
/// ///
/// Returns: normalized_string /// Returns: normalized_string
pub(super) fn normalize_helper( pub fn normalize_helper(
normalization_replacements: &HashMap<char, char>, normalization_replacements: &HashMap<char, char>,
remove_non_matches: bool, remove_non_matches: bool,
phone_number: &str phone_number: &str
@@ -276,7 +277,7 @@ pub(super) fn normalize_helper(
/// Returns `true` if there is any possible number data set for a particular /// Returns `true` if there is any possible number data set for a particular
/// PhoneNumberDesc. /// PhoneNumberDesc.
pub(super) fn desc_has_possible_number_data(desc: &PhoneNumberDesc) -> bool { pub fn desc_has_possible_number_data(desc: &PhoneNumberDesc) -> bool {
// If this is empty, it means numbers of this type inherit from the "general // If this is empty, it means numbers of this type inherit from the "general
// desc" -> the value "-1" means that no numbers exist for this type. // desc" -> the value "-1" means that no numbers exist for this type.
return desc.possible_length.len() != 1 return desc.possible_length.len() != 1
@@ -296,7 +297,7 @@ pub(super) fn desc_has_possible_number_data(desc: &PhoneNumberDesc) -> bool {
/// mention why during a review without needing to change MetadataFilter. /// mention why during a review without needing to change MetadataFilter.
/// ///
/// Returns `true` if there is any data set for a particular PhoneNumberDesc. /// Returns `true` if there is any data set for a particular PhoneNumberDesc.
pub(super) fn desc_has_data(desc: &PhoneNumberDesc) -> bool { pub fn desc_has_data(desc: &PhoneNumberDesc) -> bool {
// Checking most properties since we don't know what's present, since a custom // Checking most properties since we don't know what's present, since a custom
// build may have stripped just one of them (e.g. USE_METADATA_LITE strips // build may have stripped just one of them (e.g. USE_METADATA_LITE strips
// exampleNumber). We don't bother checking the PossibleLengthsLocalOnly, // exampleNumber). We don't bother checking the PossibleLengthsLocalOnly,
@@ -309,7 +310,7 @@ pub(super) fn desc_has_data(desc: &PhoneNumberDesc) -> bool {
/// Returns the types we have metadata for based on the PhoneMetadata object /// Returns the types we have metadata for based on the PhoneMetadata object
/// passed in. /// passed in.
pub(super) fn populate_supported_types_for_metadata( pub fn populate_supported_types_for_metadata(
metadata: &PhoneMetadata, metadata: &PhoneMetadata,
types: &mut HashSet<PhoneNumberType>, types: &mut HashSet<PhoneNumberType>,
) { ) {
@@ -329,7 +330,7 @@ pub(super) fn populate_supported_types_for_metadata(
}); });
} }
pub(super) fn get_supported_types_for_metadata(metadata: &PhoneMetadata) -> HashSet<PhoneNumberType> { pub fn get_supported_types_for_metadata(metadata: &PhoneMetadata) -> HashSet<PhoneNumberType> {
const EFFECTIVE_NUMBER_TYPES: usize = 11 /* count */ - 2 /* filter type or unknown */; const EFFECTIVE_NUMBER_TYPES: usize = 11 /* count */ - 2 /* filter type or unknown */;
let mut types = HashSet::with_capacity(EFFECTIVE_NUMBER_TYPES); let mut types = HashSet::with_capacity(EFFECTIVE_NUMBER_TYPES);
populate_supported_types_for_metadata(metadata, &mut types); populate_supported_types_for_metadata(metadata, &mut types);
@@ -338,7 +339,7 @@ pub(super) fn get_supported_types_for_metadata(metadata: &PhoneMetadata) -> Hash
/// Helper method to check a number against possible lengths for this number /// Helper method to check a number against possible lengths for this number
/// type, and determine whether it matches, or is too short or too long. /// type, and determine whether it matches, or is too short or too long.
pub(super) fn test_number_length( pub fn test_number_length(
phone_number: &str, phone_number: &str,
phone_metadata: &PhoneMetadata, phone_metadata: &PhoneMetadata,
phone_number_type: PhoneNumberType, phone_number_type: PhoneNumberType,
@@ -425,7 +426,7 @@ pub(super) fn test_number_length(
/// Helper method to check a number against possible lengths for this region, /// Helper method to check a number against possible lengths for this region,
/// based on the metadata being passed in, and determine whether it matches, or /// based on the metadata being passed in, and determine whether it matches, or
/// is too short or too long. /// is too short or too long.
pub(super) fn test_number_length_with_unknown_type( pub fn test_number_length_with_unknown_type(
phone_number: &str, phone_number: &str,
phone_metadata: &PhoneMetadata, phone_metadata: &PhoneMetadata,
) -> Result<ValidNumberLenType, ValidationResultErr> { ) -> Result<ValidNumberLenType, ValidationResultErr> {
@@ -454,7 +455,7 @@ pub(crate) fn copy_core_fields_only(from_number: &PhoneNumber) -> PhoneNumber {
/// Determines whether the given number is a national number match for the given /// Determines whether the given number is a national number match for the given
/// PhoneNumberDesc. Does not check against possible lengths! /// PhoneNumberDesc. Does not check against possible lengths!
pub(super) fn is_match( pub fn is_match(
matcher_api: &Box<dyn MatcherApi>, matcher_api: &Box<dyn MatcherApi>,
number: &str, number: &str,
number_desc: &PhoneNumberDesc, number_desc: &PhoneNumberDesc,

View File

@@ -9,7 +9,7 @@ pub(self) mod comparisons;
use std::sync::LazyLock; use std::sync::LazyLock;
pub use enums::{MatchType, PhoneNumberFormat, PhoneNumberType, ValidationResultErr, ValidNumberLenType}; pub use enums::{MatchType, PhoneNumberFormat, PhoneNumberType, ValidNumberLenType};
use thiserror::Error; use thiserror::Error;
// use crate::phonenumberutil::phonenumberutil::PhoneNumberUtil; // use crate::phonenumberutil::phonenumberutil::PhoneNumberUtil;

View File

@@ -164,7 +164,13 @@ pub(super) struct PhoneNumberRegExpsAndMappings {
/// followed by a single digit, separated by valid phone number punctuation. /// followed by a single digit, separated by valid phone number punctuation.
/// This prevents invalid punctuation (such as the star sign in Israeli star /// This prevents invalid punctuation (such as the star sign in Israeli star
/// numbers) getting into the output of the AYTF. /// numbers) getting into the output of the AYTF.
pub is_format_eligible_as_you_type_formatting_regex: Regex pub is_format_eligible_as_you_type_formatting_regex: Regex,
/// Added for function `formatting_rule_has_first_group_only`
/// A pattern that is used to determine if the national prefix formatting rule
/// has the first group only, i.e., does not start with the national prefix.
/// Note that the pattern explicitly allows for unbalanced parentheses.
pub formatting_rule_has_first_group_only_regex: Regex
} }
impl PhoneNumberRegExpsAndMappings { impl PhoneNumberRegExpsAndMappings {
@@ -323,8 +329,17 @@ impl PhoneNumberRegExpsAndMappings {
is_format_eligible_as_you_type_formatting_regex: Regex::new( is_format_eligible_as_you_type_formatting_regex: Regex::new(
&format!("[{}]*\\$1[{}]*(\\$\\d[{}]*)*",VALID_PUNCTUATION, VALID_PUNCTUATION, VALID_PUNCTUATION) &format!("[{}]*\\$1[{}]*(\\$\\d[{}]*)*",VALID_PUNCTUATION, VALID_PUNCTUATION, VALID_PUNCTUATION)
).unwrap(), ).unwrap(),
formatting_rule_has_first_group_only_regex: Regex::new("\\(?\\$1\\)?").unwrap()
}; };
instance.initialize_regexp_mappings(); instance.initialize_regexp_mappings();
instance instance
} }
}
#[cfg(test)]
mod tests {
#[test]
fn check_regexps_are_compiling() {
super::PhoneNumberRegExpsAndMappings::new();
}
} }

View File

@@ -1,15 +1,15 @@
use std::{ use std::{
borrow::Cow, cmp::max, collections::{HashMap, HashSet, VecDeque}, sync::Arc borrow::Cow, cmp::max, collections::{hash_map, HashMap, HashSet, VecDeque}, sync::Arc
}; };
use super::phone_number_regexps_and_mappings::PhoneNumberRegExpsAndMappings; use super::phone_number_regexps_and_mappings::PhoneNumberRegExpsAndMappings;
use crate::{ use crate::{
i18n, interfaces::MatcherApi, macros::owned_from_cow_or, phonenumberutil::{ i18n, interfaces::MatcherApi, macros::owned_from_cow_or, phonenumberutil::{
errors::{ExtractNumberError, GetExampleNumberError, ParseError, PhoneNumberUtilError, MatchError}, helper_constants::{ errors::{ExtractNumberError, GetExampleNumberError, InternalLogicError, InvalidMetadataForValidRegionError, InvalidNumberError, ParseError, ValidationResultErr}, 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, RFC3966_ISDN_SUBADDRESS, RFC3966_PHONE_CONTEXT, RFC3966_PREFIX 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, RFC3966_ISDN_SUBADDRESS, RFC3966_PHONE_CONTEXT, RFC3966_PREFIX
}, helper_functions::{ }, helper_functions::{
self, copy_core_fields_only, get_number_desc_by_type, get_supported_types_for_metadata, is_national_number_suffix_of_the_other, load_compiled_metadata, normalize_helper, prefix_number_with_country_calling_code, test_number_length, test_number_length_with_unknown_type self, copy_core_fields_only, get_number_desc_by_type, get_supported_types_for_metadata, is_national_number_suffix_of_the_other, load_compiled_metadata, normalize_helper, prefix_number_with_country_calling_code, test_number_length, test_number_length_with_unknown_type
}, helper_types::{PhoneNumberAndCarrierCode, PhoneNumberWithCountryCodeSource}, MatchType, PhoneNumberFormat, PhoneNumberType, ValidNumberLenType, ValidationResultErr }, helper_types::{PhoneNumberAndCarrierCode, PhoneNumberWithCountryCodeSource}, MatchType, PhoneNumberFormat, PhoneNumberType, ValidNumberLenType
}, proto_gen::{ }, proto_gen::{
phonemetadata::{NumberFormat, PhoneMetadata, PhoneNumberDesc}, phonemetadata::{NumberFormat, PhoneMetadata, PhoneNumberDesc},
phonenumber::{phone_number::CountryCodeSource, PhoneNumber}, phonenumber::{phone_number::CountryCodeSource, PhoneNumber},
@@ -21,7 +21,6 @@ use log::{error, trace, warn};
use regex::Regex; use regex::Regex;
// Helper type for Result // Helper type for Result
pub type Result<T> = std::result::Result<T, PhoneNumberUtilError>;
pub type RegexResult<T> = std::result::Result<T, ErrorInvalidRegex>; pub type RegexResult<T> = std::result::Result<T, ErrorInvalidRegex>;
@@ -29,6 +28,9 @@ pub type ParseResult<T> = std::result::Result<T, ParseError>;
pub type ExampleNumberResult = std::result::Result<PhoneNumber, GetExampleNumberError>; pub type ExampleNumberResult = std::result::Result<PhoneNumber, GetExampleNumberError>;
pub type ValidationResult = std::result::Result<ValidNumberLenType, ValidationResultErr>; pub type ValidationResult = std::result::Result<ValidNumberLenType, ValidationResultErr>;
pub type MatchResult = std::result::Result<MatchType, InvalidNumberError>;
pub type ExtractNumberResult<T> = std::result::Result<T, ExtractNumberError>;
pub type InternalLogicResult<T> = std::result::Result<T, InternalLogicError>;
pub struct PhoneNumberUtil { pub struct PhoneNumberUtil {
/// An API for validation checking. /// An API for validation checking.
@@ -126,32 +128,21 @@ impl PhoneNumberUtil {
instance instance
} }
pub fn get_supported_regions(&self) -> Vec<&str> { fn get_supported_regions(&self) -> impl Iterator<Item=&str> {
let mut regions = Vec::new(); self.region_to_metadata_map.keys().map(| k | k.as_str())
for (k, _) in self.region_to_metadata_map.iter() {
regions.push(k.as_str());
}
regions
} }
pub fn get_supported_global_network_calling_codes(&self) -> HashSet<i32> { fn get_supported_global_network_calling_codes(&self) -> impl Iterator<Item=i32> {
let mut codes = HashSet::new(); self.country_code_to_non_geographical_metadata_map.keys().map(| k | *k)
for (k, _) in self.country_code_to_non_geographical_metadata_map.iter() {
codes.insert(*k);
}
codes
} }
pub fn get_supported_calling_codes(&self) -> HashSet<i32> { fn get_supported_calling_codes(&self) -> impl Iterator<Item=i32> {
let mut codes = HashSet::new(); self.country_calling_code_to_region_code_map
.iter()
for (k, _) in self.country_calling_code_to_region_code_map.iter() { .map(| (k, _) | *k)
codes.insert(*k);
}
codes
} }
pub fn get_supported_types_for_region( fn get_supported_types_for_region(
&self, &self,
region_code: &str, region_code: &str,
) -> Option<HashSet<PhoneNumberType>> { ) -> Option<HashSet<PhoneNumberType>> {
@@ -164,7 +155,7 @@ impl PhoneNumberUtil {
}) })
} }
pub fn get_supported_types_for_non_geo_entity( fn get_supported_types_for_non_geo_entity(
&self, &self,
country_calling_code: i32, country_calling_code: i32,
) -> Option<HashSet<PhoneNumberType>> { ) -> Option<HashSet<PhoneNumberType>> {
@@ -180,21 +171,21 @@ impl PhoneNumberUtil {
}) })
} }
fn get_extn_patterns_for_matching(&self) -> &str { pub fn get_extn_patterns_for_matching(&self) -> &str {
return &self.reg_exps.extn_patterns_for_matching; return &self.reg_exps.extn_patterns_for_matching;
} }
fn starts_with_plus_chars_pattern(&self, phone_number: &str) -> bool { pub fn starts_with_plus_chars_pattern(&self, phone_number: &str) -> bool {
self.reg_exps self.reg_exps
.plus_chars_pattern .plus_chars_pattern
.matches_start(phone_number) .matches_start(phone_number)
} }
fn contains_only_valid_digits(&self, s: &str) -> bool { pub fn contains_only_valid_digits(&self, s: &str) -> bool {
self.reg_exps.digits_pattern.full_match(s) self.reg_exps.digits_pattern.full_match(s)
} }
fn trim_unwanted_end_chars<'a>(&self, phone_number: &'a str) -> &'a str { pub fn trim_unwanted_end_chars<'a>(&self, phone_number: &'a str) -> &'a str {
let mut bytes_to_trim = 0; let mut bytes_to_trim = 0;
for char in phone_number.chars().rev() { for char in phone_number.chars().rev() {
@@ -216,7 +207,7 @@ impl PhoneNumberUtil {
} }
} }
fn is_format_eligible_for_as_you_type_formatter(&self, format: &str) -> bool { pub fn is_format_eligible_for_as_you_type_formatter(&self, format: &str) -> bool {
// We require that the first // We require that the first
// group is present in the output pattern to ensure no data is lost while // group is present in the output pattern to ensure no data is lost while
// formatting; when we format as you type, this should always be the case. // formatting; when we format as you type, this should always be the case.
@@ -226,20 +217,14 @@ impl PhoneNumberUtil {
.full_match(format); .full_match(format);
} }
fn formatting_rule_has_first_group_only(&self, national_prefix_formatting_rule: &str) -> bool { pub fn formatting_rule_has_first_group_only(&self, national_prefix_formatting_rule: &str) -> bool {
// A pattern that is used to determine if the national prefix formatting rule
// has the first group only, i.e., does not start with the national prefix.
// Note that the pattern explicitly allows for unbalanced parentheses.
let first_group_only_prefix_pattern = self
.reg_exps
.regexp_cache
.get_regex("\\(?\\$1\\)?")
.expect("Invalid constant pattern!");
return national_prefix_formatting_rule.is_empty() return national_prefix_formatting_rule.is_empty()
|| first_group_only_prefix_pattern.full_match(national_prefix_formatting_rule); || self.reg_exps
.formatting_rule_has_first_group_only_regex
.full_match(national_prefix_formatting_rule);
} }
fn get_ndd_prefix_for_region( pub fn get_ndd_prefix_for_region(
&self, &self,
region_code: &str, region_code: &str,
strip_non_digits: bool, strip_non_digits: bool,
@@ -260,11 +245,11 @@ impl PhoneNumberUtil {
} }
/// 'hot' function wrapper for region_to_metadata_map.get /// 'hot' function wrapper for region_to_metadata_map.get
fn get_metadata_for_region(&self, region_code: &str) -> Option<&PhoneMetadata> { pub fn get_metadata_for_region(&self, region_code: &str) -> Option<&PhoneMetadata> {
return self.region_to_metadata_map.get(region_code) return self.region_to_metadata_map.get(region_code)
} }
fn format<'b>( pub fn format<'b>(
&self, &self,
phone_number: &'b PhoneNumber, phone_number: &'b PhoneNumber,
number_format: PhoneNumberFormat, number_format: PhoneNumberFormat,
@@ -319,7 +304,7 @@ impl PhoneNumberUtil {
Ok(Cow::Owned(formatted_number)) Ok(Cow::Owned(formatted_number))
} }
fn get_national_significant_number(phone_number: &PhoneNumber) -> String { pub fn get_national_significant_number(phone_number: &PhoneNumber) -> String {
let zeros_start = if phone_number.italian_leading_zero() { let zeros_start = if phone_number.italian_leading_zero() {
"0".repeat(max(phone_number.number_of_leading_zeros() as usize, 0)) "0".repeat(max(phone_number.number_of_leading_zeros() as usize, 0))
} else { } else {
@@ -338,38 +323,36 @@ impl PhoneNumberUtil {
/// Returns the region code that matches the specific country calling code. In /// Returns the region code that matches the specific country calling code. In
/// the case of no region code being found, the unknown region code will be /// the case of no region code being found, the unknown region code will be
/// returned. /// returned.
fn get_region_code_for_country_code(&self, country_calling_code: i32) -> &str { pub fn get_region_code_for_country_code(&self, country_calling_code: i32) -> &str {
let region_codes = self.get_region_codes_for_country_calling_code(country_calling_code); let region_codes = self.get_region_codes_for_country_calling_code(country_calling_code);
return region_codes return region_codes
.first() .and_then(| mut codes | codes.next())
.map(|v| *v) .map(|v| v)
.unwrap_or(i18n::RegionCode::get_unknown()); .unwrap_or(i18n::RegionCode::get_unknown());
} }
// Returns the region codes that matches the specific country calling code. In /// Returns the region codes that matches the specific country calling code. In
// the case of no region code being found, region_codes will be left empty. /// the case of no region code being found, region_codes will be left empty.
fn get_region_codes_for_country_calling_code( pub fn get_region_codes_for_country_calling_code(
&self, &self,
country_calling_code: i32, country_calling_code: i32,
) -> Vec<&str> { ) -> Option<impl Iterator<Item=&str>> {
let mut acc = Vec::with_capacity(10);
// Create a IntRegionsPair with the country_code passed in, and use it to // Create a IntRegionsPair with the country_code passed in, and use it to
// locate the pair with the same country_code in the sorted vector. // locate the pair with the same country_code in the sorted vector.
self.country_calling_code_to_region_code_map self.country_calling_code_to_region_code_map
.binary_search_by_key(&country_calling_code, |(code, _)| *code) .binary_search_by_key(&country_calling_code, |(code, _)| *code)
.ok()
.map(|index| { .map(|index| {
self.country_calling_code_to_region_code_map[index] self.country_calling_code_to_region_code_map[index]
.1 .1
.iter() .iter()
.for_each(|v| { .map(|v|
acc.push(v.as_str()); v.as_str()
}); )
}) /* suppress Result ignoring */ })
.ok();
acc
} }
fn get_metadata_for_region_or_calling_code( pub fn get_metadata_for_region_or_calling_code(
&self, &self,
country_calling_code: i32, country_calling_code: i32,
region_code: &str, region_code: &str,
@@ -382,7 +365,7 @@ impl PhoneNumberUtil {
}; };
} }
fn format_nsn<'b>( pub fn format_nsn<'b>(
&self, &self,
phone_number: &'b str, phone_number: &'b str,
metadata: &PhoneMetadata, metadata: &PhoneMetadata,
@@ -421,7 +404,7 @@ impl PhoneNumberUtil {
} }
} }
fn choose_formatting_pattern_for_number<'b>( pub fn choose_formatting_pattern_for_number<'b>(
&self, &self,
available_formats: &'b [NumberFormat], available_formats: &'b [NumberFormat],
national_number: &str, national_number: &str,
@@ -535,7 +518,7 @@ impl PhoneNumberUtil {
/// Simple wrapper of FormatNsnUsingPatternWithCarrier for the common case of /// Simple wrapper of FormatNsnUsingPatternWithCarrier for the common case of
/// no carrier code. /// no carrier code.
fn format_nsn_using_pattern<'b>( pub fn format_nsn_using_pattern<'b>(
&self, &self,
national_number: &'b str, national_number: &'b str,
formatting_pattern: &NumberFormat, formatting_pattern: &NumberFormat,
@@ -549,8 +532,8 @@ impl PhoneNumberUtil {
) )
} }
// Returns the formatted extension of a phone number, if the phone number had an /// Returns the formatted extension of a phone number, if the phone number had an
// extension specified else None. /// extension specified else None.
fn get_formatted_extension( fn get_formatted_extension(
phone_number: &PhoneNumber, phone_number: &PhoneNumber,
metadata: &PhoneMetadata, metadata: &PhoneMetadata,
@@ -570,7 +553,7 @@ impl PhoneNumberUtil {
Some(fast_cat::concat_str!(prefix, phone_number.extension())) Some(fast_cat::concat_str!(prefix, phone_number.extension()))
} }
fn format_by_pattern( pub fn format_by_pattern(
&self, &self,
phone_number: &PhoneNumber, phone_number: &PhoneNumber,
number_format: PhoneNumberFormat, number_format: PhoneNumberFormat,
@@ -636,11 +619,11 @@ impl PhoneNumberUtil {
Ok(formatted_number) Ok(formatted_number)
} }
fn format_national_number_with_carrier_code( pub fn format_national_number_with_carrier_code(
&self, &self,
phone_number: &PhoneNumber, phone_number: &PhoneNumber,
carrier_code: &str, carrier_code: &str,
) -> Result<String> { ) -> RegexResult<String> {
let country_calling_code = phone_number.country_code(); let country_calling_code = phone_number.country_code();
let national_significant_number = Self::get_national_significant_number(phone_number); let national_significant_number = Self::get_national_significant_number(phone_number);
let region_code = self.get_region_code_for_country_code(country_calling_code); let region_code = self.get_region_code_for_country_code(country_calling_code);
@@ -673,11 +656,11 @@ impl PhoneNumberUtil {
Ok(formatted_number) Ok(formatted_number)
} }
fn format_national_number_with_preferred_carrier_code( pub fn format_national_number_with_preferred_carrier_code(
&self, &self,
phone_number: &PhoneNumber, phone_number: &PhoneNumber,
fallback_carrier_code: &str, fallback_carrier_code: &str,
) -> Result<String> { ) -> RegexResult<String> {
let carrier_code = if !phone_number.preferred_domestic_carrier_code().is_empty() { let carrier_code = if !phone_number.preferred_domestic_carrier_code().is_empty() {
phone_number.preferred_domestic_carrier_code() phone_number.preferred_domestic_carrier_code()
} else { } else {
@@ -697,12 +680,12 @@ impl PhoneNumberUtil {
.is_ok(); .is_ok();
} }
fn format_number_for_mobile_dialing<'b>( pub fn format_number_for_mobile_dialing<'b>(
&self, &self,
phone_number: &'b PhoneNumber, phone_number: &'b PhoneNumber,
calling_from: &str, calling_from: &str,
with_formatting: bool, with_formatting: bool,
) -> Result<Cow<'b, str>> { ) -> InternalLogicResult<Cow<'b, str>> {
let country_calling_code = phone_number.country_code(); let country_calling_code = phone_number.country_code();
if !self.has_valid_country_calling_code(country_calling_code) { if !self.has_valid_country_calling_code(country_calling_code) {
return if phone_number.has_raw_input() { return if phone_number.has_raw_input() {
@@ -749,7 +732,7 @@ impl PhoneNumberUtil {
// dialled in national format. // dialled in national format.
let region_metadata = self.region_to_metadata_map let region_metadata = self.region_to_metadata_map
.get(calling_from) .get(calling_from)
.unwrap() /* we've checked if number is valid at top of function */; .ok_or(InvalidMetadataForValidRegionError{})?;
let national_number = Self::get_national_significant_number(&number_no_extension); let national_number = Self::get_national_significant_number(&number_no_extension);
let format = if self.can_be_internationally_dialled(&number_no_extension)? let format = if self.can_be_internationally_dialled(&number_no_extension)?
&& test_number_length_with_unknown_type( && test_number_length_with_unknown_type(
@@ -828,7 +811,7 @@ impl PhoneNumberUtil {
} }
} }
fn get_number_type(&self, phone_number: &PhoneNumber) -> RegexResult<PhoneNumberType> { pub fn get_number_type(&self, phone_number: &PhoneNumber) -> InternalLogicResult<PhoneNumberType> {
let region_code = self.get_region_code_for_number(phone_number)?; let region_code = self.get_region_code_for_number(phone_number)?;
let Some(metadata) = self let Some(metadata) = self
.get_metadata_for_region_or_calling_code(phone_number.country_code(), region_code) .get_metadata_for_region_or_calling_code(phone_number.country_code(), region_code)
@@ -839,13 +822,16 @@ impl PhoneNumberUtil {
Ok(self.get_number_type_helper(&national_significant_number, metadata)) Ok(self.get_number_type_helper(&national_significant_number, metadata))
} }
fn get_region_code_for_number(&self, phone_number: &PhoneNumber) -> RegexResult<&str>{ pub fn get_region_code_for_number(&self, phone_number: &PhoneNumber) -> InternalLogicResult<&str>{
let country_calling_code = phone_number.country_code(); let country_calling_code = phone_number.country_code();
let region_codes = self.get_region_codes_for_country_calling_code(country_calling_code); let region_codes = self.get_region_codes_for_country_calling_code(country_calling_code);
if region_codes.len() == 0 { let Some(region_codes) = region_codes
log::trace!("Missing/invalid country calling code ({})", country_calling_code); .map(| codes | codes.collect::<Vec<_>>())
.filter(| codes | codes.len() > 0)
else {
trace!("Missing/invalid country calling code ({})", country_calling_code);
return Ok(i18n::RegionCode::get_unknown()) return Ok(i18n::RegionCode::get_unknown())
} };
if region_codes.len() == 1 { if region_codes.len() == 1 {
return Ok(region_codes[0]) return Ok(region_codes[0])
} else { } else {
@@ -853,16 +839,18 @@ impl PhoneNumberUtil {
} }
} }
fn get_region_code_for_number_from_region_list<'b>( pub fn get_region_code_for_number_from_region_list<'b>(
&self, &self,
phone_number: &PhoneNumber, phone_number: &PhoneNumber,
region_codes: &[&'b str], region_codes: &[&'b str],
) -> RegexResult<&'b str> { ) -> InternalLogicResult<&'b str> {
let national_number = Self::get_national_significant_number(phone_number); let national_number = Self::get_national_significant_number(phone_number);
for code in region_codes { for code in region_codes {
// Metadata cannot be NULL because the region codes come from the country // Metadata cannot be NULL because the region codes come from the country
// calling code map. // calling code map.
let metadata = &self.region_to_metadata_map[*code]; let metadata = &self.region_to_metadata_map
.get(*code)
.ok_or(InvalidMetadataForValidRegionError{})?;
if metadata.has_leading_digits() { if metadata.has_leading_digits() {
if self.reg_exps.regexp_cache if self.reg_exps.regexp_cache
.get_regex(metadata.leading_digits())? .get_regex(metadata.leading_digits())?
@@ -946,7 +934,7 @@ impl PhoneNumberUtil {
return PhoneNumberType::Unknown; return PhoneNumberType::Unknown;
} }
fn is_number_matching_desc( pub fn is_number_matching_desc(
&self, &self,
national_number: &str, national_number: &str,
number_desc: &PhoneNumberDesc number_desc: &PhoneNumberDesc
@@ -963,10 +951,10 @@ impl PhoneNumberUtil {
helper_functions::is_match(&self.matcher_api, national_number, number_desc) helper_functions::is_match(&self.matcher_api, national_number, number_desc)
} }
fn can_be_internationally_dialled( pub fn can_be_internationally_dialled(
&self, &self,
phone_number: &PhoneNumber phone_number: &PhoneNumber
) -> Result<bool> { ) -> InternalLogicResult<bool> {
let region_code = self.get_region_code_for_number(phone_number)?; let region_code = self.get_region_code_for_number(phone_number)?;
let Some(metadata) = self.region_to_metadata_map.get(region_code) else { let Some(metadata) = self.region_to_metadata_map.get(region_code) else {
// Note numbers belonging to non-geographical entities (e.g. +800 numbers) // Note numbers belonging to non-geographical entities (e.g. +800 numbers)
@@ -979,29 +967,29 @@ impl PhoneNumberUtil {
)); ));
} }
fn normalize_diallable_chars_only(&self, phone_number: &str) -> String { pub fn normalize_diallable_chars_only(&self, phone_number: &str) -> String {
normalize_helper( normalize_helper(
&self.reg_exps.diallable_char_mappings, &self.reg_exps.diallable_char_mappings,
true, phone_number true, phone_number
) )
} }
fn normalize_digits_only<'a>(&self, phone_number: &'a str) -> String { pub fn normalize_digits_only<'a>(&self, phone_number: &'a str) -> String {
phone_number.chars() phone_number.chars()
.filter_map(| c | c.to_decimal_utf8()) .filter_map(| c | c.to_decimal_utf8())
.filter_map(| i | char::from_u32(b'0' as u32 + i) ) .filter_map(| i | char::from_u32(b'0' as u32 + i) )
.collect() .collect()
} }
fn format_out_of_country_calling_number<'a>( pub fn format_out_of_country_calling_number<'a>(
&self, &self,
phone_number: &'a PhoneNumber, phone_number: &'a PhoneNumber,
calling_from: &str, calling_from: &str,
) -> RegexResult<Cow<'a, str>> { ) -> InternalLogicResult<Cow<'a, str>> {
let Some(metadata_calling_from) = self.region_to_metadata_map.get(calling_from) else { let Some(metadata_calling_from) = self.region_to_metadata_map.get(calling_from) else {
trace!("Trying to format number from invalid region {calling_from}\ trace!("Trying to format number from invalid region {calling_from}\
. International formatting applied."); . International formatting applied.");
return self.format(phone_number, PhoneNumberFormat::International) return Ok(self.format(phone_number, PhoneNumberFormat::International)?)
}; };
let country_code = phone_number.country_code(); let country_code = phone_number.country_code();
let national_significant_number = Self::get_national_significant_number( let national_significant_number = Self::get_national_significant_number(
@@ -1032,7 +1020,7 @@ impl PhoneNumberUtil {
// those cases return the version including country calling code. // those cases return the version including country calling code.
// Details here: // Details here:
// http://www.petitfute.com/voyage/225-info-pratiques-reunion // http://www.petitfute.com/voyage/225-info-pratiques-reunion
return self.format(phone_number, PhoneNumberFormat::National) return Ok(self.format(phone_number, PhoneNumberFormat::National)?)
} }
// Metadata cannot be NULL because we checked 'IsValidRegionCode()' above. // Metadata cannot be NULL because we checked 'IsValidRegionCode()' above.
let international_prefix = metadata_calling_from.international_prefix(); let international_prefix = metadata_calling_from.international_prefix();
@@ -1054,7 +1042,7 @@ impl PhoneNumberUtil {
// Metadata cannot be NULL because the country_code is valid. // Metadata cannot be NULL because the country_code is valid.
let metadata_for_region = self let metadata_for_region = self
.get_metadata_for_region_or_calling_code(country_code, region_code) .get_metadata_for_region_or_calling_code(country_code, region_code)
.expect("Metadata cannot be NULL because the country_code is valid"); .ok_or(InvalidMetadataForValidRegionError{})?;
let formatted_nsn = self let formatted_nsn = self
.format_nsn( .format_nsn(
@@ -1104,18 +1092,18 @@ impl PhoneNumberUtil {
return format_rule.map(| rule | rule.is_some()); return format_rule.map(| rule | rule.is_some());
} }
fn format_in_original_format<'a>( pub fn format_in_original_format<'a>(
&self, &self,
phone_number: &'a PhoneNumber, phone_number: &'a PhoneNumber,
region_calling_from: &str, region_calling_from: &str,
) -> RegexResult<Cow<'a, str>> { ) -> InternalLogicResult<Cow<'a, str>> {
if phone_number.has_raw_input() && !self.has_formatting_pattern_for_number(phone_number)? { 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 // We check if we have the formatting pattern because without that, we might
// format the number as a group without national prefix. // format the number as a group without national prefix.
return Ok(Cow::Borrowed(phone_number.raw_input())) return Ok(Cow::Borrowed(phone_number.raw_input()))
} }
if !phone_number.has_country_code_source() { if !phone_number.has_country_code_source() {
return self.format(phone_number, PhoneNumberFormat::National) return Ok(self.format(phone_number, PhoneNumberFormat::National)?)
} }
let formatted_number = match phone_number.country_code_source() { let formatted_number = match phone_number.country_code_source() {
CountryCodeSource::FROM_NUMBER_WITH_PLUS_SIGN => CountryCodeSource::FROM_NUMBER_WITH_PLUS_SIGN =>
@@ -1221,7 +1209,7 @@ impl PhoneNumberUtil {
raw_input: &str, raw_input: &str,
national_prefix: &str, national_prefix: &str,
region_code: &str region_code: &str
) -> RegexResult<bool> { ) -> InternalLogicResult<bool> {
let normalized_national_number = self.normalize_digits_only(raw_input); let normalized_national_number = self.normalize_digits_only(raw_input);
if normalized_national_number.starts_with(national_prefix) { if normalized_national_number.starts_with(national_prefix) {
// Some Japanese numbers (e.g. 00777123) might be mistaken to contain // Some Japanese numbers (e.g. 00777123) might be mistaken to contain
@@ -1239,20 +1227,20 @@ impl PhoneNumberUtil {
Ok(false) Ok(false)
} }
fn parse(&self, number_to_parse: &str, default_region: &str) -> ParseResult<PhoneNumber> { pub fn parse(&self, number_to_parse: &str, default_region: &str) -> ParseResult<PhoneNumber> {
self.parse_helper(number_to_parse, default_region, false, true) self.parse_helper(number_to_parse, default_region, false, true)
} }
fn parse_and_keep_raw_input(&self, number_to_parse: &str, default_region: &str) -> ParseResult<PhoneNumber> { pub fn parse_and_keep_raw_input(&self, number_to_parse: &str, default_region: &str) -> ParseResult<PhoneNumber> {
self.parse_helper(number_to_parse, default_region, true, true) self.parse_helper(number_to_parse, default_region, true, true)
} }
fn is_valid_number(&self, phone_number: &PhoneNumber) -> RegexResult<bool> { pub fn is_valid_number(&self, phone_number: &PhoneNumber) -> InternalLogicResult<bool> {
let region_code = self.get_region_code_for_number(phone_number)?; let region_code = self.get_region_code_for_number(phone_number)?;
return Ok(self.is_valid_number_for_region(phone_number, region_code)); return Ok(self.is_valid_number_for_region(phone_number, region_code));
} }
fn is_valid_number_for_region(&self, phone_number: &PhoneNumber, region_code: &str) -> bool { pub fn is_valid_number_for_region(&self, phone_number: &PhoneNumber, region_code: &str) -> bool {
let country_code = phone_number.country_code(); let country_code = phone_number.country_code();
let metadata = let metadata =
self.get_metadata_for_region_or_calling_code(country_code, region_code); self.get_metadata_for_region_or_calling_code(country_code, region_code);
@@ -1266,11 +1254,11 @@ impl PhoneNumberUtil {
} }
} }
fn format_out_of_country_keeping_alpha_chars<'a>( pub fn format_out_of_country_keeping_alpha_chars<'a>(
&self, &self,
phone_number: &'a PhoneNumber, phone_number: &'a PhoneNumber,
calling_from: &str, calling_from: &str,
) -> RegexResult<Cow<'a, str>> { ) -> InternalLogicResult<Cow<'a, str>> {
// If there is no raw input, then we can't keep alpha characters because there // If there is no raw input, then we can't keep alpha characters because there
// aren't any. In this case, we return FormatOutOfCountryCallingNumber. // aren't any. In this case, we return FormatOutOfCountryCallingNumber.
if phone_number.raw_input().is_empty() { if phone_number.raw_input().is_empty() {
@@ -1333,11 +1321,12 @@ impl PhoneNumberUtil {
// leading digits) decide whether a national prefix needs to be used, since // leading digits) decide whether a national prefix needs to be used, since
// we have overridden the pattern to match anything, but that is not the // we have overridden the pattern to match anything, but that is not the
// case in the metadata to date. // case in the metadata to date.
return self.format_nsn_using_pattern( return Ok(self.format_nsn_using_pattern(
&normalized_raw_input, &normalized_raw_input,
&new_format, &new_format,
PhoneNumberFormat::National PhoneNumberFormat::National
).map(| cow | Cow::Owned(cow.into_owned()) ); ).map(| cow | Cow::Owned(cow.into_owned()) )?
);
} }
// If an unsupported region-calling-from is entered, or a country with // If an unsupported region-calling-from is entered, or a country with
@@ -1376,7 +1365,7 @@ impl PhoneNumberUtil {
// Metadata cannot be null because the country code is valid. // Metadata cannot be null because the country code is valid.
let metadata_for_region = self let metadata_for_region = self
.get_metadata_for_region_or_calling_code(country_code, region_code) .get_metadata_for_region_or_calling_code(country_code, region_code)
.expect("Metadata cannot be null because the country code is valid."); .ok_or(InvalidMetadataForValidRegionError{})?;
// Strip any extension // Strip any extension
let (phone_number_without_extension, _) = self let (phone_number_without_extension, _) = self
@@ -1396,7 +1385,7 @@ impl PhoneNumberUtil {
/// Returns whether the value of phoneContext follows the syntax defined in /// Returns whether the value of phoneContext follows the syntax defined in
/// RFC3966. /// RFC3966.
fn is_phone_context_valid( pub fn is_phone_context_valid(
&self, &self,
phone_context: &str phone_context: &str
) -> bool { ) -> bool {
@@ -1414,7 +1403,7 @@ impl PhoneNumberUtil {
/// Converts number_to_parse to a form that we can parse and write it to /// Converts number_to_parse to a form that we can parse and write it to
/// national_number if it is written in RFC3966; otherwise extract a possible /// national_number if it is written in RFC3966; otherwise extract a possible
/// number out of it and write to national_number. /// number out of it and write to national_number.
fn build_national_number_for_parsing( pub fn build_national_number_for_parsing(
&self, number_to_parse: &str &self, number_to_parse: &str
) -> ParseResult<String> { ) -> ParseResult<String> {
let index_of_phone_context = number_to_parse.find(RFC3966_PHONE_CONTEXT); let index_of_phone_context = number_to_parse.find(RFC3966_PHONE_CONTEXT);
@@ -1482,7 +1471,7 @@ impl PhoneNumberUtil {
/// ///
/// Returns the extracted `Some(possibly empty)`, or a `None` if no /// Returns the extracted `Some(possibly empty)`, or a `None` if no
/// phone-context parameter is found. /// phone-context parameter is found.
fn extract_phone_context<'a>( pub fn extract_phone_context<'a>(
number_to_extract_from: &'a str, number_to_extract_from: &'a str,
index_of_phone_context: usize index_of_phone_context: usize
) -> &'a str { ) -> &'a str {
@@ -1514,7 +1503,7 @@ impl PhoneNumberUtil {
/// second extension here makes this actually two phone numbers, (530) 583-6985 /// second extension here makes this actually two phone numbers, (530) 583-6985
/// x302 and (530) 583-6985 x2303. We remove the second extension so that the /// x302 and (530) 583-6985 x2303. We remove the second extension so that the
/// first number is parsed correctly. /// first number is parsed correctly.
fn extract_possible_number<'a>(&self, phone_number: &'a str) -> std::result::Result<&'a str, ExtractNumberError> { pub fn extract_possible_number<'a>(&self, phone_number: &'a str) -> ExtractNumberResult<&'a str> {
// Rust note: skip UTF-8 validation since in rust strings are already UTF-8 valid // Rust note: skip UTF-8 validation since in rust strings are already UTF-8 valid
let mut i: usize = 0; let mut i: usize = 0;
for c in phone_number.chars() { for c in phone_number.chars() {
@@ -1545,11 +1534,11 @@ impl PhoneNumberUtil {
) )
} }
fn is_possible_number(&self, phone_number: &PhoneNumber) -> bool{ pub fn is_possible_number(&self, phone_number: &PhoneNumber) -> bool{
self.is_possible_number_with_reason(phone_number).is_ok() self.is_possible_number_with_reason(phone_number).is_ok()
} }
fn is_possible_number_for_type( pub fn is_possible_number_for_type(
&self, &self,
phone_number: &PhoneNumber, phone_number: &PhoneNumber,
phone_number_type: PhoneNumberType phone_number_type: PhoneNumberType
@@ -1557,7 +1546,7 @@ impl PhoneNumberUtil {
self.is_possible_number_for_type_with_reason(phone_number, phone_number_type).is_ok() self.is_possible_number_for_type_with_reason(phone_number, phone_number_type).is_ok()
} }
fn is_possible_number_for_string( pub fn is_possible_number_for_string(
&self, &self,
phone_number: &str, phone_number: &str,
region_dialing_from: &str region_dialing_from: &str
@@ -1571,14 +1560,14 @@ impl PhoneNumberUtil {
} }
} }
fn is_possible_number_with_reason( pub fn is_possible_number_with_reason(
&self, phone_number: &PhoneNumber &self, phone_number: &PhoneNumber
) -> ValidationResult { ) -> ValidationResult {
self.is_possible_number_for_type_with_reason(phone_number, PhoneNumberType::Unknown) self.is_possible_number_for_type_with_reason(phone_number, PhoneNumberType::Unknown)
} }
fn is_possible_number_for_type_with_reason( pub fn is_possible_number_for_type_with_reason(
&self, phone_number: &PhoneNumber, phone_number_type: PhoneNumberType &self, phone_number: &PhoneNumber, phone_number_type: PhoneNumberType
) -> ValidationResult { ) -> ValidationResult {
let national_number = Self::get_national_significant_number(phone_number); let national_number = Self::get_national_significant_number(phone_number);
@@ -1604,7 +1593,7 @@ impl PhoneNumberUtil {
return test_number_length(&national_number, metadata, phone_number_type); return test_number_length(&national_number, metadata, phone_number_type);
} }
fn truncate_too_long_number(&self, phone_number: &mut PhoneNumber) -> RegexResult<bool> { pub fn truncate_too_long_number(&self, phone_number: &mut PhoneNumber) -> InternalLogicResult<bool> {
if self.is_valid_number(&phone_number)? { if self.is_valid_number(&phone_number)? {
return Ok(true) return Ok(true)
} }
@@ -1629,7 +1618,7 @@ impl PhoneNumberUtil {
// Note if any new field is added to this method that should always be filled // Note if any new field is added to this method that should always be filled
// in, even when keepRawInput is false, it should also be handled in the // in, even when keepRawInput is false, it should also be handled in the
// CopyCoreFieldsOnly() method. // CopyCoreFieldsOnly() method.
fn parse_helper( pub fn parse_helper(
&self, &self,
number_to_parse: &str, number_to_parse: &str,
default_region: &str, default_region: &str,
@@ -1765,7 +1754,7 @@ impl PhoneNumberUtil {
/// method does not require the number to be normalized in advance - but does /// 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 /// assume that leading non-number symbols have been removed, such as by the
/// method `ExtractPossibleNumber`. /// method `ExtractPossibleNumber`.
fn is_viable_phone_number(&self, phone_number: &str) -> bool { pub fn is_viable_phone_number(&self, phone_number: &str) -> bool {
if phone_number.len() < MIN_LENGTH_FOR_NSN { if phone_number.len() < MIN_LENGTH_FOR_NSN {
false false
} else { } else {
@@ -1777,7 +1766,7 @@ impl PhoneNumberUtil {
/// the number to parse starts with a + symbol so that we can attempt to infer /// the number to parse starts with a + symbol so that we can attempt to infer
/// the country from the number. Returns false if it cannot use the region /// the country from the number. Returns false if it cannot use the region
/// provided and the region cannot be inferred. /// provided and the region cannot be inferred.
fn check_region_for_parsing( pub fn check_region_for_parsing(
&self, &self,
number_to_parse: &str, number_to_parse: &str,
default_region: &str default_region: &str
@@ -1790,12 +1779,15 @@ impl PhoneNumberUtil {
/// Strips any extension (as in, the part of the number dialled after the call is /// Strips any extension (as in, the part of the number dialled after the call is
/// connected, usually indicated with extn, ext, x or similar) from the end of /// connected, usually indicated with extn, ext, x or similar) from the end of
/// the number, and returns stripped number and extension. The number passed in should be non-normalized. /// the number, and returns stripped number and extension. The number passed in should be non-normalized.
fn maybe_strip_extension<'a>(&self, phone_number: &'a str) -> (&'a str, Option<&'a str>) { pub fn maybe_strip_extension<'a>(&self, phone_number: &'a str) -> (&'a str, Option<&'a str>) {
let Some(captures) = self.reg_exps.extn_pattern.captures(phone_number) else { let Some(captures) = self.reg_exps.extn_pattern.captures(phone_number) else {
return (phone_number, None); return (phone_number, None);
}; };
let full_capture = captures.get(0).expect("first capture MUST always be not None"); // first capture is always not None, this should not happen, but use this for safety.
let Some(full_capture) = captures.get(0) else {
return (phone_number, None);
};
// Replace the extensions in the original string here. // Replace the extensions in the original string here.
let phone_number_no_extn = &phone_number[..full_capture.start()]; let phone_number_no_extn = &phone_number[..full_capture.start()];
// If we find a potential extension, and the number preceding this is a // If we find a potential extension, and the number preceding this is a
@@ -1810,33 +1802,33 @@ impl PhoneNumberUtil {
(phone_number, None) (phone_number, None)
} }
// Tries to extract a country calling code from a number. Country calling codes /// Tries to extract a country calling code from a number. Country calling codes
// are extracted in the following ways: /// are extracted in the following ways:
// - by stripping the international dialing prefix of the region the person /// - by stripping the international dialing prefix of the region the person
// is dialing from, if this is present in the number, and looking at the next /// is dialing from, if this is present in the number, and looking at the next
// digits /// digits
// - by stripping the '+' sign if present and then looking at the next digits /// - by stripping the '+' sign if present and then looking at the next digits
// - by comparing the start of the number and the country calling code of the /// - by comparing the start of the number and the country calling code of the
// default region. If the number is not considered possible for the numbering /// default region. If the number is not considered possible for the numbering
// plan of the default region initially, but starts with the country calling /// plan of the default region initially, but starts with the country calling
// code of this region, validation will be reattempted after stripping this /// code of this region, validation will be reattempted after stripping this
// country calling code. If this number is considered a possible number, then /// country calling code. If this number is considered a possible number, then
// the first digits will be considered the country calling code and removed as /// the first digits will be considered the country calling code and removed as
// such. /// such.
// ///
// Returns NO_PARSING_ERROR if a country calling code was successfully /// Returns `Ok` if a country calling code was successfully
// extracted or none was present, or the appropriate error otherwise, such as /// extracted or none was present, or the appropriate error otherwise, such as
// if a + was present but it was not followed by a valid country calling code. /// if a + was present but it was not followed by a valid country calling code.
// If NO_PARSING_ERROR is returned, the national_number without the country /// If NO_PARSING_ERROR is returned, the national_number without the country
// calling code is populated, and the country_code of the phone_number passed /// calling code is populated, and the country_code of the phone_number passed
// in is set to the country calling code if found, otherwise to 0. /// in is set to the country calling code if found, otherwise to 0.
fn maybe_extract_country_code<'a>( pub fn maybe_extract_country_code<'a>(
&self, &self,
default_region_metadata: Option<&PhoneMetadata>, default_region_metadata: Option<&PhoneMetadata>,
keep_raw_input: bool, keep_raw_input: bool,
national_number: &'a str, national_number: &'a str,
phone_number: &mut PhoneNumber phone_number: &mut PhoneNumber
) -> std::result::Result<Cow<'a, str>, ParseError> { ) -> ParseResult<Cow<'a, str>> {
// Set the default prefix to be something that will never match if there is no // Set the default prefix to be something that will never match if there is no
// default region. // default region.
let possible_country_idd_prefix = if let Some(default_region_metadata) = default_region_metadata { let possible_country_idd_prefix = if let Some(default_region_metadata) = default_region_metadata {
@@ -1912,11 +1904,11 @@ impl PhoneNumberUtil {
/// Gets a valid fixed-line number for the specified region_code. Returns false /// Gets a valid fixed-line number for the specified region_code. Returns false
/// if no number exists. /// if no number exists.
fn get_example_number(&self, region_code: &str) -> ExampleNumberResult { pub fn get_example_number(&self, region_code: &str) -> ExampleNumberResult {
self.get_example_number_for_type_and_region_code(region_code, PhoneNumberType::FixedLine) self.get_example_number_for_type_and_region_code(region_code, PhoneNumberType::FixedLine)
} }
fn get_invalid_example_number(&self, region_code: &str) -> ExampleNumberResult { pub fn get_invalid_example_number(&self, region_code: &str) -> ExampleNumberResult {
let Some(region_metadata) = self.region_to_metadata_map.get(region_code) else { let Some(region_metadata) = self.region_to_metadata_map.get(region_code) else {
warn!("Invalid or unknown region code ({}) provided.", region_code); warn!("Invalid or unknown region code ({}) provided.", region_code);
return Err(GetExampleNumberError::InvalidMetadataError) return Err(GetExampleNumberError::InvalidMetadataError)
@@ -1968,7 +1960,7 @@ impl PhoneNumberUtil {
// Gets a valid number for the specified region_code and type. Returns false if // Gets a valid number for the specified region_code and type. Returns false if
// no number exists. // no number exists.
fn get_example_number_for_type_and_region_code( pub fn get_example_number_for_type_and_region_code(
&self, &self,
region_code: &str, region_code: &str,
phone_number_type: PhoneNumberType, phone_number_type: PhoneNumberType,
@@ -1987,11 +1979,11 @@ impl PhoneNumberUtil {
Err(GetExampleNumberError::CouldNotGetNumberError) Err(GetExampleNumberError::CouldNotGetNumberError)
} }
fn get_example_number_for_type( pub fn get_example_number_for_type(
&self, &self,
phone_number_type: PhoneNumberType, phone_number_type: PhoneNumberType,
) -> ExampleNumberResult { ) -> ExampleNumberResult {
if let Some(number) = self.get_supported_regions().iter() if let Some(number) = self.get_supported_regions()
.find_map(| region_code | .find_map(| region_code |
self.get_example_number_for_type_and_region_code(region_code, phone_number_type).ok() self.get_example_number_for_type_and_region_code(region_code, phone_number_type).ok()
) { ) {
@@ -2025,7 +2017,7 @@ impl PhoneNumberUtil {
Err(GetExampleNumberError::CouldNotGetNumberError) Err(GetExampleNumberError::CouldNotGetNumberError)
} }
fn get_example_number_for_non_geo_entity( pub fn get_example_number_for_non_geo_entity(
&self, &self,
country_calling_code: i32 country_calling_code: i32
) -> ExampleNumberResult { ) -> ExampleNumberResult {
@@ -2073,7 +2065,7 @@ impl PhoneNumberUtil {
/// Returns true if an international dialing prefix could be removed from the /// Returns true if an international dialing prefix could be removed from the
/// number, otherwise false if the number did not seem to be in international /// number, otherwise false if the number did not seem to be in international
/// format. /// format.
fn maybe_strip_international_prefix_and_normalize<'a>( pub fn maybe_strip_international_prefix_and_normalize<'a>(
&self, &self,
phone_number: &'a str, phone_number: &'a str,
possible_idd_prefix: &str, possible_idd_prefix: &str,
@@ -2125,7 +2117,7 @@ impl PhoneNumberUtil {
/// - Wide-ascii digits are converted to normal ASCII (European) digits. /// - Wide-ascii digits are converted to normal ASCII (European) digits.
/// - Arabic-Indic numerals are converted to European numerals. /// - Arabic-Indic numerals are converted to European numerals.
/// - Spurious alpha characters are stripped. /// - Spurious alpha characters are stripped.
fn normalize(&self, phone_number: &str) -> String { pub fn normalize(&self, phone_number: &str) -> String {
if self.reg_exps.valid_alpha_phone_pattern.is_match(phone_number) { if self.reg_exps.valid_alpha_phone_pattern.is_match(phone_number) {
normalize_helper( normalize_helper(
&self.reg_exps.alpha_phone_mappings, &self.reg_exps.alpha_phone_mappings,
@@ -2139,7 +2131,7 @@ impl PhoneNumberUtil {
/// Strips the IDD from the start of the number if present. Helper function used /// Strips the IDD from the start of the number if present. Helper function used
/// by MaybeStripInternationalPrefixAndNormalize. /// by MaybeStripInternationalPrefixAndNormalize.
fn parse_prefix_as_idd<'a>(&self, phone_number: & 'a str, idd_pattern: Arc<Regex>) -> Option<&'a str> { pub fn parse_prefix_as_idd<'a>(&self, phone_number: & 'a str, idd_pattern: Arc<Regex>) -> Option<&'a str> {
// First attempt to strip the idd_pattern at the start, if present. We make a // First attempt to strip the idd_pattern at the start, if present. We make a
// copy so that we can revert to the original string if necessary. // copy so that we can revert to the original string if necessary.
let Some(idd_pattern_match) = idd_pattern.find_start(&phone_number) else { let Some(idd_pattern_match) = idd_pattern.find_start(&phone_number) else {
@@ -2158,17 +2150,17 @@ impl PhoneNumberUtil {
Some(&phone_number[captured_range_end..]) Some(&phone_number[captured_range_end..])
} }
fn is_number_geographical( pub fn is_number_geographical(
&self, &self,
phone_number: &PhoneNumber phone_number: &PhoneNumber
) -> RegexResult<bool> { ) -> InternalLogicResult<bool> {
Ok(self.is_number_geographical_by_country_code_and_type( Ok(self.is_number_geographical_by_country_code_and_type(
self.get_number_type(phone_number)?, self.get_number_type(phone_number)?,
phone_number.country_code() phone_number.country_code()
)) ))
} }
fn is_number_geographical_by_country_code_and_type( pub fn is_number_geographical_by_country_code_and_type(
&self, &self,
phone_number_type: PhoneNumberType, phone_number_type: PhoneNumberType,
country_calling_code: i32 country_calling_code: i32
@@ -2182,9 +2174,9 @@ impl PhoneNumberUtil {
) )
} }
fn get_length_of_geographical_area_code( pub fn get_length_of_geographical_area_code(
&self, phone_number: &PhoneNumber &self, phone_number: &PhoneNumber
) -> RegexResult<usize> { ) -> InternalLogicResult<usize> {
let region_code = self.get_region_code_for_number(phone_number)?; let region_code = self.get_region_code_for_number(phone_number)?;
let Some(metadata) = self.region_to_metadata_map.get(region_code) else { let Some(metadata) = self.region_to_metadata_map.get(region_code) else {
return Ok(0) return Ok(0)
@@ -2217,9 +2209,9 @@ impl PhoneNumberUtil {
return self.get_length_of_national_destination_code(phone_number); return self.get_length_of_national_destination_code(phone_number);
} }
fn get_length_of_national_destination_code( pub fn get_length_of_national_destination_code(
&self, phone_number: &PhoneNumber &self, phone_number: &PhoneNumber
) -> RegexResult<usize> { ) -> InternalLogicResult<usize> {
let mut copied_proto = phone_number.clone(); let mut copied_proto = phone_number.clone();
if phone_number.has_extension() { if phone_number.has_extension() {
// Clear the extension so it's not included when formatting. // Clear the extension so it's not included when formatting.
@@ -2259,7 +2251,7 @@ impl PhoneNumberUtil {
Ok(captured_groups[ndc_index]) Ok(captured_groups[ndc_index])
} }
fn get_country_mobile_token(&self, country_calling_code: i32) -> Option<char> { pub fn get_country_mobile_token(&self, country_calling_code: i32) -> Option<char> {
self self
.reg_exps .reg_exps
.mobile_token_mappings .mobile_token_mappings
@@ -2274,7 +2266,7 @@ impl PhoneNumberUtil {
/// ///
/// Returns None if national_number doesn't start with a valid country calling code /// Returns None if national_number doesn't start with a valid country calling code
/// Assumes the national_number is at least 3 characters long. /// Assumes the national_number is at least 3 characters long.
fn extract_country_code<'a>(&self, national_number: Cow<'a, str>) -> Option<(Cow<'a, str>, i32)> { pub fn extract_country_code<'a>(&self, national_number: Cow<'a, str>) -> Option<(Cow<'a, str>, i32)> {
if national_number.as_ref().is_empty() || national_number.as_ref().starts_with('0') { if national_number.as_ref().is_empty() || national_number.as_ref().starts_with('0') {
return None return None
} }
@@ -2302,7 +2294,7 @@ impl PhoneNumberUtil {
// to strip any national dialing prefix from. The metadata should be for the // to strip any national dialing prefix from. The metadata should be for the
// region that we think this number is from. Returns true if a national prefix // region that we think this number is from. Returns true if a national prefix
// and/or carrier code was stripped. // and/or carrier code was stripped.
fn maybe_strip_national_prefix_and_carrier_code<'a>( pub fn maybe_strip_national_prefix_and_carrier_code<'a>(
&self, &self,
metadata: &PhoneMetadata, metadata: &PhoneMetadata,
phone_number: &'a str, phone_number: &'a str,
@@ -2327,11 +2319,15 @@ impl PhoneNumberUtil {
let first_capture = captures.as_ref().and_then(| c | c.get(1)); let first_capture = captures.as_ref().and_then(| c | c.get(1));
let second_capture = captures.as_ref().and_then(| c | c.get(2)); let second_capture = captures.as_ref().and_then(| c | c.get(2));
if !transform_rule.is_empty() && let condition = |first_capture: &regex::Match<'_>| {
second_capture.is_some_and(| c | !c.is_empty() && first_capture.is_some()) !transform_rule.is_empty() &&
|| first_capture.is_some_and(| c | !c.is_empty() && second_capture.is_none()) { (second_capture.is_some_and(| c | !c.is_empty()) ||
!first_capture.is_empty() && second_capture.is_none())
};
let first_capture = first_capture.unwrap(); if let Some(first_capture) = first_capture.filter(condition) {
// here we can safe unwrap because first_capture.is_some() anyway
let carrier_code_temp = if second_capture.is_some() { let carrier_code_temp = if second_capture.is_some() {
Some(first_capture.as_str()) Some(first_capture.as_str())
} else { } else {
@@ -2368,7 +2364,7 @@ impl PhoneNumberUtil {
// A helper function to set the values related to leading zeros in a // A helper function to set the values related to leading zeros in a
// PhoneNumber. // PhoneNumber.
fn get_italian_leading_zeros_for_phone_number( pub fn get_italian_leading_zeros_for_phone_number(
national_number: &str national_number: &str
) -> Option<usize> { ) -> Option<usize> {
if national_number.len() < 2 { if national_number.len() < 2 {
@@ -2384,7 +2380,7 @@ impl PhoneNumberUtil {
Some(zero_count) Some(zero_count)
} }
fn convert_alpha_characters_in_number(&self, phone_number: &str) -> String { pub fn convert_alpha_characters_in_number(&self, phone_number: &str) -> String {
normalize_helper( normalize_helper(
&self.reg_exps.alpha_phone_mappings, &self.reg_exps.alpha_phone_mappings,
false, false,
@@ -2392,7 +2388,7 @@ impl PhoneNumberUtil {
) )
} }
fn is_number_match( pub fn is_number_match(
&self, &self,
first_number_in: &PhoneNumber, first_number_in: &PhoneNumber,
second_number_in: &PhoneNumber, second_number_in: &PhoneNumber,
@@ -2439,16 +2435,16 @@ impl PhoneNumberUtil {
return MatchType::NoMatch return MatchType::NoMatch
} }
fn is_number_match_with_two_strings( pub fn is_number_match_with_two_strings(
&self, &self,
first_number: &str, first_number: &str,
second_number: &str second_number: &str
) -> std::result::Result<MatchType, MatchError> { ) -> MatchResult {
match self.parse(first_number, i18n::RegionCode::get_unknown()) { match self.parse(first_number, i18n::RegionCode::get_unknown()) {
Ok(first_number_as_proto) => return self.is_number_match_with_one_string(&first_number_as_proto, second_number), Ok(first_number_as_proto) => return self.is_number_match_with_one_string(&first_number_as_proto, second_number),
Err(err) => { Err(err) => {
if !matches!(err, ParseError::InvalidCountryCodeError) { if !matches!(err, ParseError::InvalidCountryCodeError) {
return Err(MatchError::InvalidNumber(err)) return Err(InvalidNumberError(err))
} }
} }
} }
@@ -2456,7 +2452,7 @@ impl PhoneNumberUtil {
Ok(second_number_as_proto) => return self.is_number_match_with_one_string(&second_number_as_proto, first_number), Ok(second_number_as_proto) => return self.is_number_match_with_one_string(&second_number_as_proto, first_number),
Err(err) => { Err(err) => {
if !matches!(err, ParseError::InvalidCountryCodeError) { if !matches!(err, ParseError::InvalidCountryCodeError) {
return Err(MatchError::InvalidNumber(err)) return Err(InvalidNumberError(err))
} }
let first_number_as_proto = self.parse_helper( let first_number_as_proto = self.parse_helper(
first_number, i18n::RegionCode::get_unknown(), first_number, i18n::RegionCode::get_unknown(),
@@ -2473,11 +2469,11 @@ impl PhoneNumberUtil {
} }
} }
fn is_number_match_with_one_string( pub fn is_number_match_with_one_string(
&self, &self,
first_number: &PhoneNumber, first_number: &PhoneNumber,
second_number: &str second_number: &str
) -> std::result::Result<MatchType, MatchError> { ) -> MatchResult {
// First see if the second number has an implicit country calling code, by // First see if the second number has an implicit country calling code, by
// attempting to parse it. // attempting to parse it.
match self.parse(second_number, i18n::RegionCode::get_unknown()) { match self.parse(second_number, i18n::RegionCode::get_unknown()) {
@@ -2486,7 +2482,7 @@ impl PhoneNumberUtil {
)), )),
Err(err) => { Err(err) => {
if !matches!(err, ParseError::InvalidCountryCodeError) { if !matches!(err, ParseError::InvalidCountryCodeError) {
return Err(MatchError::InvalidNumber(err)); return Err(InvalidNumberError(err));
} }
} }
} }