use alloc::vec::Vec;
use icu_locale_core::LanguageIdentifier;
use icu_provider::prelude::*;
use crate::indices::*;
use crate::provider::*;
use crate::scaffold::*;
#[non_exhaustive]
#[derive(Copy, Clone, PartialEq, Eq, Debug, Default)]
pub struct SentenceBreakOptions<'a> {
pub content_locale: Option<&'a LanguageIdentifier>,
pub invariant_options: SentenceBreakInvariantOptions,
}
#[non_exhaustive]
#[derive(Copy, Clone, PartialEq, Eq, Debug, Default)]
pub struct SentenceBreakInvariantOptions {}
#[derive(Debug)]
pub struct SentenceBreakIterator<'data, 's, Y: RuleBreakType>(
SentenceBreakIteratorInner<'data, 's, Y>,
);
#[derive(Debug)]
enum SentenceBreakIteratorInner<'data, 's, Y: RuleBreakType> {
V1(crate::rule_segmenter_v1::RuleBreakIterator<'data, 's, Y>),
#[cfg(feature = "unstable")]
V2(crate::rule_segmenter_v2::RuleBreakIterator<'data, 's, Y>),
}
impl<Y: RuleBreakType> Iterator for SentenceBreakIterator<'_, '_, Y> {
type Item = usize;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
match &mut self.0 {
SentenceBreakIteratorInner::V1(iter) => iter.next(),
#[cfg(feature = "unstable")]
SentenceBreakIteratorInner::V2(iter) => iter.next(),
}
}
}
#[derive(Debug)]
pub struct SentenceSegmenter(SentenceSegmenterInner);
#[derive(Debug)]
enum SentenceSegmenterInner {
V1 {
payload: DataPayload<SegmenterBreakSentenceV1>,
payload_locale_override: Option<DataPayload<SegmenterBreakSentenceOverrideV1>>,
},
#[cfg(feature = "unstable")]
V2 {
payload: DataPayload<SegmenterBreakSentenceV2>,
tailoring: Option<DataPayload<SegmenterBreakSentenceOverrideV2>>,
},
}
#[derive(Clone, Debug, Copy)]
pub struct SentenceSegmenterBorrowed<'data>(SentenceSegmenterBorrowedInner<'data>);
#[derive(Clone, Debug, Copy)]
enum SentenceSegmenterBorrowedInner<'data> {
V1 {
data: &'data RuleBreakData<'data>,
locale_override: Option<&'data RuleBreakDataOverride<'data>>,
},
#[cfg(feature = "unstable")]
V2 {
data: &'data SegmenterStateMachine<'data>,
tailoring: Option<&'data SegmenterStateMachineOverride<'data>>,
},
}
impl SentenceSegmenter {
#[cfg(feature = "compiled_data")]
#[expect(clippy::new_ret_no_self)]
pub const fn new(
_options: SentenceBreakInvariantOptions,
) -> SentenceSegmenterBorrowed<'static> {
SentenceSegmenterBorrowed(SentenceSegmenterBorrowedInner::V1 {
data: Baked::SINGLETON_SEGMENTER_BREAK_SENTENCE_V1,
locale_override: None,
})
}
icu_provider::gen_buffer_data_constructors!(
(options: SentenceBreakOptions) -> error: DataError,
functions: [
try_new,
try_new_with_buffer_provider,
try_new_unstable,
Self
]
);
#[doc = icu_provider::gen_buffer_unstable_docs!(UNSTABLE, Self::try_new)]
pub fn try_new_unstable<D>(
provider: &D,
options: SentenceBreakOptions,
) -> Result<Self, DataError>
where
D: DataProvider<SegmenterBreakSentenceV1>
+ DataProvider<SegmenterBreakSentenceOverrideV1>
+ ?Sized,
{
let payload = provider.load(Default::default())?.payload;
let payload_locale_override = if let Some(locale) = options.content_locale {
let locale = DataLocale::from(locale);
let req = DataRequest {
id: DataIdentifierBorrowed::for_locale(&locale),
metadata: {
let mut metadata = DataRequestMetadata::default();
metadata.silent = true;
metadata
},
};
provider
.load(req)
.allow_identifier_not_found()?
.map(|r| r.payload)
} else {
None
};
Ok(Self(SentenceSegmenterInner::V1 {
payload,
payload_locale_override,
}))
}
#[cfg(feature = "compiled_data")]
#[cfg(feature = "unstable")]
pub const fn new_neo(
_options: SentenceBreakInvariantOptions,
) -> SentenceSegmenterBorrowed<'static> {
SentenceSegmenterBorrowed(SentenceSegmenterBorrowedInner::V2 {
data: Baked::SINGLETON_SEGMENTER_BREAK_SENTENCE_V2,
tailoring: None,
})
}
#[cfg(feature = "compiled_data")]
#[cfg(feature = "unstable")]
pub fn try_new_neo(options: SentenceBreakOptions) -> Result<Self, DataError> {
Self::try_new_neo_unstable(&Baked, options)
}
#[cfg(feature = "unstable")]
#[doc = icu_provider::gen_buffer_unstable_docs!(UNSTABLE, Self::try_new_neo)]
pub fn try_new_neo_unstable<D>(
provider: &D,
options: SentenceBreakOptions,
) -> Result<Self, DataError>
where
D: DataProvider<SegmenterBreakSentenceV2>
+ DataProvider<SegmenterBreakSentenceOverrideV2>
+ ?Sized,
{
let payload = provider.load(Default::default())?.payload;
let tailoring = if let Some(locale) = options.content_locale {
provider
.load(DataRequest {
id: DataIdentifierBorrowed::for_locale(&DataLocale::from(locale)),
metadata: {
let mut metadata = DataRequestMetadata::default();
metadata.silent = true;
metadata
},
})
.allow_identifier_not_found()?
.map(|r| r.payload)
} else {
None
};
Ok(Self(SentenceSegmenterInner::V2 { payload, tailoring }))
}
pub fn as_borrowed(&self) -> SentenceSegmenterBorrowed<'_> {
match &self.0 {
SentenceSegmenterInner::V1 {
payload,
payload_locale_override,
} => SentenceSegmenterBorrowed(SentenceSegmenterBorrowedInner::V1 {
data: payload.get(),
locale_override: payload_locale_override.as_ref().map(|p| p.get()),
}),
#[cfg(feature = "unstable")]
SentenceSegmenterInner::V2 { payload, tailoring } => {
SentenceSegmenterBorrowed(SentenceSegmenterBorrowedInner::V2 {
data: payload.get(),
tailoring: tailoring.as_ref().map(|p| p.get()),
})
}
}
}
}
impl<'data> SentenceSegmenterBorrowed<'data> {
fn segment<'s, Y: RuleBreakType>(
self,
iter: Y::IterAttr<'s>,
len: usize,
) -> SentenceBreakIterator<'data, 's, Y> {
match self.0 {
SentenceSegmenterBorrowedInner::V1 {
data,
locale_override,
} => SentenceBreakIterator(SentenceBreakIteratorInner::V1(
crate::rule_segmenter_v1::RuleBreakIterator {
iter,
len,
current_pos_data: None,
result_cache: Vec::new(),
data,
complex: None,
boundary_property: 0,
locale_override,
handle_complex: crate::rule_segmenter_v1::empty_handle_complex,
},
)),
#[cfg(feature = "unstable")]
SentenceSegmenterBorrowedInner::V2 { data, tailoring } => {
SentenceBreakIterator(SentenceBreakIteratorInner::V2(
crate::rule_segmenter_v2::RuleBreakIterator::new(iter, data, tailoring, None),
))
}
}
}
pub fn segment_str<'s>(self, input: &'s str) -> SentenceBreakIterator<'data, 's, Utf8> {
self.segment(input.char_indices(), input.len())
}
pub fn segment_utf8<'s>(
self,
input: &'s [u8],
) -> SentenceBreakIterator<'data, 's, PotentiallyIllFormedUtf8> {
self.segment(Utf8CharIndices::new(input), input.len())
}
pub fn segment_latin1<'s>(self, input: &'s [u8]) -> SentenceBreakIterator<'data, 's, Latin1> {
self.segment(Latin1Indices::new(input), input.len())
}
pub fn segment_utf16<'s>(self, input: &'s [u16]) -> SentenceBreakIterator<'data, 's, Utf16> {
self.segment(Utf16Indices::new(input), input.len())
}
}
impl SentenceSegmenterBorrowed<'static> {
pub const fn static_to_owned(self) -> SentenceSegmenter {
SentenceSegmenter(match self.0 {
SentenceSegmenterBorrowedInner::V1 {
data,
locale_override,
} => SentenceSegmenterInner::V1 {
payload: DataPayload::from_static_ref(data),
payload_locale_override: if let Some(d) = locale_override {
Some(DataPayload::from_static_ref(d))
} else {
None
},
},
#[cfg(feature = "unstable")]
SentenceSegmenterBorrowedInner::V2 { data, tailoring } => SentenceSegmenterInner::V2 {
payload: DataPayload::from_static_ref(data),
tailoring: if let Some(d) = tailoring {
Some(DataPayload::from_static_ref(d))
} else {
None
},
},
})
}
}
#[test]
fn empty_string() {
let segmenter = SentenceSegmenter::new(Default::default());
let breaks: Vec<usize> = segmenter.segment_str("").collect();
assert_eq!(breaks, [0]);
}
#[test]
fn empty_string_neo() {
let segmenter = SentenceSegmenter::new_neo(Default::default());
let breaks: Vec<usize> = segmenter.segment_str("").collect();
assert_eq!(breaks, [0]);
}