add format_out_of_country_keeping_alpha_chars fn

This commit is contained in:
Vlasislav Kashin
2025-07-09 16:12:34 +03:00
parent 0ac5d451f5
commit 1307c8cbba

View File

@@ -1268,127 +1268,131 @@ impl PhoneNumberUtil {
} }
} }
fn FormatOutOfCountryKeepingAlphaChars<'a>( 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,
) -> Cow<'a, str> { ) -> RegexResult<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 (number.raw_input().empty()){ if phone_number.raw_input().is_empty() {
return self.format_out_of_country_calling_number(phone_number, calling_from); return self.format_out_of_country_calling_number(phone_number, calling_from)
FormatOutOfCountryCallingNumber(number, calling_from, formatted_number);
return;
} }
int country_code = number.country_code();
if (!HasValidCountryCallingCode(country_code)) let country_code = phone_number.country_code();
{ if !self.has_valid_country_calling_code(country_code) {
formatted_number->assign(number.raw_input()); return Ok(phone_number.raw_input().into())
return;
} }
// Strip any prefix such as country calling code, IDD, that was present. We do // Strip any prefix such as country calling code, IDD, that was present. We do
// this by comparing the number in raw_input with the parsed number. // this by comparing the number in raw_input with the parsed number.
string raw_input_copy(number.raw_input());
// Normalize punctuation. We retain number grouping symbols such as " " only. // Normalize punctuation. We retain number grouping symbols such as " " only.
NormalizeHelper(reg_exps_->all_plus_number_grouping_symbols_, true, let mut normalized_raw_input = helper_functions::normalize_helper(
&raw_input_copy); &self.reg_exps.all_plus_number_grouping_symbols,
true,
phone_number.raw_input()
);
// Now we trim everything before the first three digits in the parsed number. // Now we trim everything before the first three digits in the parsed number.
// We choose three because all valid alpha numbers have 3 digits at the start // We choose three because all valid alpha numbers have 3 digits at the start
// - if it does not, then we don't trim anything at all. Similarly, if the // - if it does not, then we don't trim anything at all. Similarly, if the
// national number was less than three digits, we don't trim anything at all. // national number was less than three digits, we don't trim anything at all.
string national_number; let national_number = Self::get_national_significant_number(phone_number);
GetNationalSignificantNumber(number, &national_number); if national_number.len() > 3 {
if (national_number.length() > 3) let first_national_number_digit = normalized_raw_input
{ .find(&national_number[0..3]);
size_t first_national_number_digit = if let Some(first_national_number_digit) = first_national_number_digit {
raw_input_copy.find(national_number.substr(0, 3)); normalized_raw_input.drain(0..first_national_number_digit);
if (first_national_number_digit != string::npos)
{
raw_input_copy = raw_input_copy.substr(first_national_number_digit);
} }
} }
const PhoneMetadata *metadata = GetMetadataForRegion(calling_from); let metadata = self.region_to_metadata_map.get(calling_from);
if (country_code == kNanpaCountryCode) if country_code == NANPA_COUNTRY_CODE {
{ if self.nanpa_regions.contains(calling_from) {
if (IsNANPACountry(calling_from)) let mut buf = itoa::Buffer::new();
{
StrAppend(formatted_number, country_code, " ", raw_input_copy); return Ok(fast_cat::concat_str!(
return; buf.format(country_code), " ", &normalized_raw_input
).into());
} }
} }
else if (metadata && else if let Some(metadata) = metadata.filter(
country_code == GetCountryCodeForValidRegion(calling_from)) |metadata| country_code == metadata.country_code()
{ ) {
const NumberFormat *formatting_pattern = let Some(formatting_pattern) = self.choose_formatting_pattern_for_number(
ChooseFormattingPatternForNumber(metadata->number_format(), &metadata.number_format, &national_number
national_number); )? else {
if (!formatting_pattern)
{
// If no pattern above is matched, we format the original input. // If no pattern above is matched, we format the original input.
formatted_number->assign(raw_input_copy); return Ok(normalized_raw_input.into())
return; };
} let mut new_format = formatting_pattern.clone();
NumberFormat new_format;
new_format.MergeFrom(*formatting_pattern);
// The first group is the first group of digits that the user wrote // The first group is the first group of digits that the user wrote
// together. // together.
new_format.set_pattern("(\\d+)(.*)"); new_format.set_pattern("(\\d+)(.*)".to_owned());
// Here we just concatenate them back together after the national prefix // Here we just concatenate them back together after the national prefix
// has been fixed. // has been fixed.
new_format.set_format("$1$2"); new_format.set_format("$1$2".to_owned());
// Now we format using this pattern instead of the default pattern, but // Now we format using this pattern instead of the default pattern, but
// with the national prefix prefixed if necessary. // with the national prefix prefixed if necessary.
// This will not work in the cases where the pattern (and not the // This will not work in the cases where the pattern (and not the
// 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.
FormatNsnUsingPattern(raw_input_copy, new_format, NATIONAL, return self.format_nsn_using_pattern(
formatted_number); &normalized_raw_input,
return; &new_format,
PhoneNumberFormat::National
).map(| cow | Cow::Owned(cow.into_owned()) );
} }
string international_prefix_for_formatting;
// If an unsupported region-calling-from is entered, or a country with // If an unsupported region-calling-from is entered, or a country with
// multiple international prefixes, the international format of the number is // multiple international prefixes, the international format of the number is
// returned, unless there is a preferred international prefix. // returned, unless there is a preferred international prefix.
if (metadata) let international_prefix_for_formatting = metadata.map(| metadata | {
{ let international_prefix = metadata.international_prefix();
const string &international_prefix = metadata->international_prefix(); if self.reg_exps.single_international_prefix
international_prefix_for_formatting = .full_match(international_prefix) {
reg_exps_->single_international_prefix_->FullMatch(international_prefix) international_prefix
? international_prefix } else {
: metadata->preferred_international_prefix(); metadata.preferred_international_prefix()
} }
if (!international_prefix_for_formatting.empty()) });
{ let formatted_number = if let Some(international_prefix_for_formatting) = international_prefix_for_formatting {
StrAppend(formatted_number, international_prefix_for_formatting, " ", let mut buf = itoa::Buffer::new();
country_code, " ", raw_input_copy); fast_cat::concat_str!(
} international_prefix_for_formatting, " ",
else buf.format(country_code), " ", &normalized_raw_input
{ )
} else {
// Invalid region entered as country-calling-from (so no metadata was found // Invalid region entered as country-calling-from (so no metadata was found
// for it) or the region chosen has multiple international dialling // for it) or the region chosen has multiple international dialling
// prefixes. // prefixes.
if (!IsValidRegionCode(calling_from)) if !self.region_to_metadata_map.contains_key(calling_from) {
{ trace!(
VLOG(1) << "Trying to format number from invalid region " << calling_from "Trying to format number from invalid region {}. International formatting applied.",
<< ". International formatting applied."; calling_from
);
} }
formatted_number->assign(raw_input_copy); let mut formatted_number = normalized_raw_input;
PrefixNumberWithCountryCallingCode(country_code, INTERNATIONAL, prefix_number_with_country_calling_code(country_code, PhoneNumberFormat::International, &mut formatted_number);
formatted_number); formatted_number
} };
std::string region_code; let region_code = self.get_region_code_for_country_code(country_code);
GetRegionCodeForCountryCode(country_code, &region_code);
// Metadata cannot be null because the country code is valid. // Metadata cannot be null because the country code is valid.
const PhoneMetadata *metadata_for_region = let metadata_for_region = self
GetMetadataForRegionOrCallingCode(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.");
// Strip any extension // Strip any extension
std::string extension; let (phone_number_without_extension, extension) = self
MaybeStripExtension(formatted_number, &extension); .maybe_strip_extension(&formatted_number);
// Append the formatted extension // Append the formatted extension
MaybeAppendFormattedExtension(number, *metadata_for_region, INTERNATIONAL, let extension = Self::get_formatted_extension(phone_number, metadata_for_region, PhoneNumberFormat::International);
formatted_number); Ok(
if let Some(extension) = extension {
fast_cat::concat_str!(phone_number_without_extension, &extension)
} else {
phone_number_without_extension.to_string()
}
.into()
)
} }