use unicode_linebreak::{linebreaks, BreakOpportunity};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum LineBreak {
Mandatory,
Allowed,
}
pub struct LineBreaker {
breaks: Vec<(usize, LineBreak)>,
}
impl LineBreaker {
pub fn new(text: &str) -> Self {
let breaks = linebreaks(text)
.map(|(pos, opp)| {
let lb = match opp {
BreakOpportunity::Mandatory => LineBreak::Mandatory,
BreakOpportunity::Allowed => LineBreak::Allowed,
};
(pos, lb)
})
.collect();
Self { breaks }
}
pub fn breaks(&self) -> &[(usize, LineBreak)] {
&self.breaks
}
pub fn iter(&self) -> impl Iterator<Item = &(usize, LineBreak)> {
self.breaks.iter()
}
}
impl IntoIterator for LineBreaker {
type Item = (usize, LineBreak);
type IntoIter = std::vec::IntoIter<(usize, LineBreak)>;
fn into_iter(self) -> Self::IntoIter {
self.breaks.into_iter()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn space_allows_break() {
let lb = LineBreaker::new("hello world");
let breaks: Vec<_> = lb.iter().cloned().collect();
assert!(
!breaks.is_empty(),
"should have at least one break opportunity"
);
}
#[test]
fn newline_is_mandatory() {
let lb = LineBreaker::new("hello\nworld");
let mandatory = lb.iter().any(|(_, kind)| *kind == LineBreak::Mandatory);
assert!(mandatory, "newline should produce a mandatory break");
}
}