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