#![no_std]
#![deny(clippy::all, clippy::pedantic, clippy::nursery, clippy::cargo)]
#![allow(clippy::cast_sign_loss)]
use core::iter::FusedIterator;
use core::slice;
use core::str::{from_utf8_unchecked, Lines};
pub trait SplitParagraphs {
fn paragraphs(&self) -> Paragraphs;
}
#[must_use = "iterators are lazy and do nothing unless consumed"]
#[derive(Clone, Debug)]
pub struct Paragraphs<'a> {
lines: Lines<'a>,
}
impl SplitParagraphs for str {
#[inline]
fn paragraphs(&self) -> Paragraphs {
Paragraphs {
lines: self.lines(),
}
}
}
impl<'a> Iterator for Paragraphs<'a> {
type Item = &'a str;
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
(0, self.lines.size_hint().1.map(|n| (n + 1) / 2))
}
#[inline]
fn next(&mut self) -> Option<Self::Item> {
let first_line = self.lines.next()?;
let first_non_empty_line = if first_line.trim().is_empty() {
loop {
let line = self.lines.next()?;
if !line.trim().is_empty() {
break line;
}
}
} else {
first_line
};
let mut last_non_empty_line = first_non_empty_line;
loop {
let Some(line) = self.lines.next() else {
break;
};
if line.trim().is_empty() {
break;
}
last_non_empty_line = line;
}
let result: &str = unsafe {
from_utf8_unchecked(slice::from_raw_parts(
first_non_empty_line.as_ptr(),
(last_non_empty_line
.as_ptr()
.offset_from(first_non_empty_line.as_ptr()) as usize)
.unchecked_add(last_non_empty_line.len()),
))
};
Some(result)
}
}
impl DoubleEndedIterator for Paragraphs<'_> {
#[inline]
fn next_back(&mut self) -> Option<Self::Item> {
let last_line = self.lines.next_back()?;
let last_non_empty_line = if last_line.trim().is_empty() {
loop {
let line = self.lines.next_back()?;
if !line.trim().is_empty() {
break line;
}
}
} else {
last_line
};
let mut first_non_empty_line = last_non_empty_line;
loop {
let Some(line) = self.lines.next_back() else {
break;
};
if line.trim().is_empty() {
break;
}
first_non_empty_line = line;
}
let result: &str = unsafe {
from_utf8_unchecked(slice::from_raw_parts(
first_non_empty_line.as_ptr(),
(last_non_empty_line
.as_ptr()
.offset_from(first_non_empty_line.as_ptr()) as usize)
.unchecked_add(last_non_empty_line.len()),
))
};
Some(result)
}
}
impl FusedIterator for Paragraphs<'_> {}