use std::ops::Deref;
use std::sync::Arc;
#[cfg(feature = "global_static")]
use std::sync::LazyLock;
use crate::alternate_formats::AlternateFormats;
use crate::enums::Region;
use crate::interfaces::AsOriginal;
use crate::phonenumber_matcher::leniency::Leniency;
use crate::phonenumber_matcher::matcher_internal::{
PhoneNumberMatcher, PhoneNumberMatcherFallible,
};
use crate::phonenumber_matcher::matcher_regex::MatcherRegex;
use crate::phonenumberutil::phonenumberutil_internal::PhoneNumberUtilInternal;
#[cfg(feature = "global_static")]
use crate::{PHONE_NUMBER_UTIL, PhoneNumberUtil};
#[derive(Clone)]
pub struct PhoneNumberMatcherFactory<
U: AsOriginal<PhoneNumberUtilInternal>,
T: Deref<Target = U> + Clone,
> {
regexps: Arc<MatcherRegex>,
alternate_formats: Option<Arc<AlternateFormats>>,
phone_util: T,
}
impl<U: AsOriginal<PhoneNumberUtilInternal>, T: Deref<Target = U> + Clone>
PhoneNumberMatcherFactory<U, T>
{
#[cfg(feature = "builtin_metadata")]
pub fn new_for_util(phone_util: T) -> Self {
Self::new_for_util_with_formats(phone_util, Some(Arc::new(AlternateFormats::new())))
}
pub fn new_for_util_with_formats(
phone_util: T,
formats: Option<Arc<AlternateFormats>>,
) -> Self {
Self {
alternate_formats: formats,
phone_util,
regexps: Arc::new(MatcherRegex::new()),
}
}
pub fn matcher_builder<'a, 'f>(&'f self, text: &'a str) -> MatcherBuilder<'a, 'f, U, T> {
MatcherBuilder::new(self, text)
}
pub fn create_matcher<'a>(
&self,
text: &'a str,
leniency: Leniency,
max_tries: u64,
preferred_region: Option<Region>,
) -> PhoneNumberMatcher<'a, U, T> {
PhoneNumberMatcher::new_for_util(
self.phone_util.clone(),
self.regexps.clone(),
text,
preferred_region,
leniency,
max_tries,
self.alternate_formats.clone(),
)
}
pub fn create_matcher_fallible<'a>(
&self,
text: &'a str,
leniency: Leniency,
max_tries: u64,
preferred_region: Option<Region>,
) -> PhoneNumberMatcherFallible<'a, U, T> {
PhoneNumberMatcherFallible::new_for_util(
self.phone_util.clone(),
self.regexps.clone(),
text,
preferred_region,
leniency,
max_tries,
self.alternate_formats.clone(),
)
}
pub fn create_matcher_auto<'a>(
&self,
text: &'a str,
leniency: Leniency,
max_tries: u64,
initial_region: Option<Region>,
) -> PhoneNumberMatcher<'a, U, T> {
PhoneNumberMatcher::new_for_util_auto_region(
self.phone_util.clone(),
self.regexps.clone(),
text,
initial_region,
leniency,
max_tries,
self.alternate_formats.clone(),
)
}
pub fn create_matcher_auto_fallible<'a>(
&self,
text: &'a str,
leniency: Leniency,
max_tries: u64,
initial_region: Option<Region>,
) -> PhoneNumberMatcherFallible<'a, U, T> {
PhoneNumberMatcherFallible::new_for_util_auto_region(
self.phone_util.clone(),
self.regexps.clone(),
text,
initial_region,
leniency,
max_tries,
self.alternate_formats.clone(),
)
}
pub fn create_matcher_with_regions<'a>(
&self,
text: &'a str,
leniency: Leniency,
max_tries: u64,
regions: impl IntoIterator<Item = Region>,
initial_region: Option<Region>,
) -> PhoneNumberMatcher<'a, U, T> {
PhoneNumberMatcher::new_for_util_with_regions(
self.phone_util.clone(),
self.regexps.clone(),
text,
sorted_regions(regions),
initial_region,
leniency,
max_tries,
self.alternate_formats.clone(),
)
}
pub fn create_matcher_with_regions_fallible<'a>(
&self,
text: &'a str,
leniency: Leniency,
max_tries: u64,
regions: impl IntoIterator<Item = Region>,
initial_region: Option<Region>,
) -> PhoneNumberMatcherFallible<'a, U, T> {
PhoneNumberMatcherFallible::new_for_util_with_regions(
self.phone_util.clone(),
self.regexps.clone(),
text,
sorted_regions(regions),
initial_region,
leniency,
max_tries,
self.alternate_formats.clone(),
)
}
}
fn sorted_regions(regions: impl IntoIterator<Item = Region>) -> Arc<[Region]> {
let mut v: Vec<Region> = regions.into_iter().collect();
v.sort_unstable();
v.dedup();
v.into()
}
#[cfg(feature = "global_static")]
impl PhoneNumberMatcherFactory<PhoneNumberUtil, &'static PhoneNumberUtil> {
pub fn new() -> Self {
Self::new_for_util(&PHONE_NUMBER_UTIL)
}
}
#[cfg(feature = "global_static")]
impl Default for PhoneNumberMatcherFactory<PhoneNumberUtil, &'static PhoneNumberUtil> {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
enum RegionMode {
Fixed(Option<Region>),
Auto(Option<Region>),
Subset {
regions: Arc<[Region]>,
hint: Option<Region>,
},
}
pub struct MatcherBuilder<
'a,
'f,
U: AsOriginal<PhoneNumberUtilInternal>,
T: Deref<Target = U> + Clone,
> {
factory: &'f PhoneNumberMatcherFactory<U, T>,
text: &'a str,
leniency: Leniency,
max_tries: u64,
region_mode: RegionMode,
}
impl<'a, 'f, U: AsOriginal<PhoneNumberUtilInternal>, T: Deref<Target = U> + Clone>
MatcherBuilder<'a, 'f, U, T>
{
fn new(factory: &'f PhoneNumberMatcherFactory<U, T>, text: &'a str) -> Self {
Self {
factory,
text,
leniency: Leniency::Valid, max_tries: u64::MAX, region_mode: RegionMode::Fixed(None), }
}
pub fn leniency(mut self, leniency: Leniency) -> Self {
self.leniency = leniency;
self
}
pub fn max_tries(mut self, max_tries: u64) -> Self {
self.max_tries = max_tries;
self
}
pub fn preferred_region(mut self, region: impl Into<Option<Region>>) -> Self {
self.region_mode = RegionMode::Fixed(region.into());
self
}
pub fn auto_region(mut self) -> Self {
self.region_mode = RegionMode::Auto(None);
self
}
pub fn auto_region_with_hint(mut self, region: impl Into<Option<Region>>) -> Self {
self.region_mode = RegionMode::Auto(region.into());
self
}
pub fn regions(mut self, regions: impl IntoIterator<Item = Region>) -> Self {
self.region_mode = RegionMode::Subset {
regions: sorted_regions(regions),
hint: None,
};
self
}
pub fn regions_with_hint(
mut self,
regions: impl IntoIterator<Item = Region>,
hint: impl Into<Option<Region>>,
) -> Self {
self.region_mode = RegionMode::Subset {
regions: sorted_regions(regions),
hint: hint.into(),
};
self
}
pub fn build(self) -> PhoneNumberMatcher<'a, U, T> {
match self.region_mode {
RegionMode::Fixed(region) => {
self.factory
.create_matcher(self.text, self.leniency, self.max_tries, region)
}
RegionMode::Auto(hint) => self.factory.create_matcher_auto(
self.text,
self.leniency,
self.max_tries,
hint,
),
RegionMode::Subset { regions, hint } => self.factory.create_matcher_with_regions(
self.text,
self.leniency,
self.max_tries,
regions.iter().copied(),
hint,
),
}
}
pub fn build_fallible(self) -> PhoneNumberMatcherFallible<'a, U, T> {
match self.region_mode {
RegionMode::Fixed(region) => self.factory.create_matcher_fallible(
self.text,
self.leniency,
self.max_tries,
region,
),
RegionMode::Auto(hint) => self.factory.create_matcher_auto_fallible(
self.text,
self.leniency,
self.max_tries,
hint,
),
RegionMode::Subset { regions, hint } => {
self.factory.create_matcher_with_regions_fallible(
self.text,
self.leniency,
self.max_tries,
regions.iter().copied(),
hint,
)
}
}
}
}
#[cfg(feature = "global_static")]
pub static PHONE_MATCHER_FACTORY: LazyLock<
PhoneNumberMatcherFactory<PhoneNumberUtil, &'static PhoneNumberUtil>,
> = LazyLock::new(PhoneNumberMatcherFactory::new);
#[cfg(feature = "global_static")]
pub trait FindNumberExt {
fn find_phone_numbers(
&self,
) -> PhoneNumberMatcher<'_, PhoneNumberUtil, &'static PhoneNumberUtil>;
fn find_phone_numbers_with_leniency(
&self,
leniency: Leniency,
) -> PhoneNumberMatcher<'_, PhoneNumberUtil, &'static PhoneNumberUtil>;
fn find_phone_numbers_with_preferred_region(
&self,
region: impl Into<Option<Region>>,
) -> PhoneNumberMatcher<'_, PhoneNumberUtil, &'static PhoneNumberUtil>;
fn find_phone_numbers_with_regions(
&self,
regions: impl IntoIterator<Item = Region>,
) -> PhoneNumberMatcher<'_, PhoneNumberUtil, &'static PhoneNumberUtil>;
fn find_phone_numbers_auto_region(
&self,
) -> PhoneNumberMatcher<'_, PhoneNumberUtil, &'static PhoneNumberUtil>;
fn phone_number_matcher_builder(
&self,
) -> MatcherBuilder<'_, 'static, PhoneNumberUtil, &'static PhoneNumberUtil>;
}
#[cfg(feature = "global_static")]
impl FindNumberExt for str {
fn find_phone_numbers(
&self,
) -> PhoneNumberMatcher<'_, PhoneNumberUtil, &'static PhoneNumberUtil> {
self.phone_number_matcher_builder().build()
}
fn find_phone_numbers_with_leniency(
&self,
leniency: Leniency,
) -> PhoneNumberMatcher<'_, PhoneNumberUtil, &'static PhoneNumberUtil> {
self.phone_number_matcher_builder()
.leniency(leniency)
.build()
}
fn find_phone_numbers_with_preferred_region(
&self,
region: impl Into<Option<Region>>,
) -> PhoneNumberMatcher<'_, PhoneNumberUtil, &'static PhoneNumberUtil> {
self.phone_number_matcher_builder()
.preferred_region(region)
.build()
}
fn find_phone_numbers_with_regions(
&self,
regions: impl IntoIterator<Item = Region>,
) -> PhoneNumberMatcher<'_, PhoneNumberUtil, &'static PhoneNumberUtil> {
self.phone_number_matcher_builder().regions(regions).build()
}
fn find_phone_numbers_auto_region(
&self,
) -> PhoneNumberMatcher<'_, PhoneNumberUtil, &'static PhoneNumberUtil> {
self.phone_number_matcher_builder().auto_region().build()
}
fn phone_number_matcher_builder(
&self,
) -> MatcherBuilder<'_, 'static, PhoneNumberUtil, &'static PhoneNumberUtil> {
PHONE_MATCHER_FACTORY.matcher_builder(self)
}
}