use icu_properties::{CodePointMapData, props::LineBreak};
use icu_segmenter::{LineSegmenter, iterators::LineBreakIterator, scaffold::Utf8};
use std::ops::Range;
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Ord, PartialOrd, Hash)]
pub enum Status {
#[default]
New,
ResizeLevelRuns,
LevelRuns,
Wrapped,
Ready,
}
impl Status {
#[inline]
pub fn is_ready(&self) -> bool {
*self == Status::Ready
}
}
pub struct OwningVecIter<T: Clone> {
v: Vec<T>,
i: usize,
}
impl<T: Clone> OwningVecIter<T> {
pub fn new(v: Vec<T>) -> Self {
let i = 0;
OwningVecIter { v, i }
}
}
impl<T: Clone> Iterator for OwningVecIter<T> {
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
if self.i < self.v.len() {
let item = self.v[self.i].clone();
self.i += 1;
Some(item)
} else {
None
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let len = self.v.len() - self.i;
(len, Some(len))
}
}
impl<T: Clone> ExactSizeIterator for OwningVecIter<T> {}
impl<T: Clone> std::iter::FusedIterator for OwningVecIter<T> {}
pub(crate) fn ends_with_hard_break(text: &str) -> bool {
text.chars().next_back().is_some_and(|c| {
matches!(
CodePointMapData::<LineBreak>::new().get(c),
LineBreak::MandatoryBreak
| LineBreak::CarriageReturn
| LineBreak::LineFeed
| LineBreak::NextLine
)
})
}
pub struct LineIterator<'a> {
break_iter: LineBreakIterator<'static, 'a, Utf8>,
text: &'a str,
start: usize,
}
impl<'a> LineIterator<'a> {
#[inline]
pub fn new(text: &'a str) -> Self {
let segmenter = LineSegmenter::new_auto(Default::default());
let mut break_iter = segmenter.segment_str(text);
assert_eq!(break_iter.next(), Some(0)); LineIterator {
break_iter,
text,
start: 0,
}
}
}
impl<'a> Iterator for LineIterator<'a> {
type Item = Range<usize>;
fn next(&mut self) -> Option<Self::Item> {
while let Some(index) = self.break_iter.next() {
if ends_with_hard_break(&self.text[..index]) || index == self.text.len() {
let range = self.start..index;
self.start = index;
return Some(range);
}
}
None
}
}