//! Shmancy iterator adapters pub use chars::Chars; pub use flatten::Flatten; pub mod chars { //! Converts an [Iterator] into an //! [Iterator]> /// Invalid unicode codepoint found when iterating over [Chars] #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct BadUnicode(pub u32); impl std::error::Error for BadUnicode {} impl std::fmt::Display for BadUnicode { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let Self(code) = self; write!(f, "Bad unicode: {code}") } } /// Converts an [Iterator] into an /// [Iterator] #[derive(Clone, Debug)] pub struct Chars>(pub I); impl> Iterator for Chars { type Item = Result; fn next(&mut self) -> Option { let Self(bytes) = self; let start = bytes.next()? as u32; let (mut out, count) = match start { start if start & 0x80 == 0x00 => (start, 0), // ASCII valid range start if start & 0xe0 == 0xc0 => (start & 0x1f, 1), // 1 continuation byte start if start & 0xf0 == 0xe0 => (start & 0x0f, 2), // 2 continuation bytes start if start & 0xf8 == 0xf0 => (start & 0x07, 3), // 3 continuation bytes _ => return None, }; for _ in 0..count { let cont = bytes.next()? as u32; if cont & 0xc0 != 0x80 { return None; } out = out << 6 | (cont & 0x3f); } Some(char::from_u32(out).ok_or(BadUnicode(out))) } } } pub mod flatten { //! Flattens an [Iterator] returning [`Result`](Result) or [`Option`](Option) //! into a *non-[FusedIterator](std::iter::FusedIterator)* over `T` /// Flattens an [Iterator] returning [`Result`](Result) or [`Option`](Option) /// into a *non-[FusedIterator](std::iter::FusedIterator)* over `T` #[derive(Clone, Debug)] pub struct Flatten>(pub I); impl>> Iterator for Flatten, I> { type Item = T; fn next(&mut self) -> Option { self.0.next()?.ok() } } impl>> Iterator for Flatten, I> { type Item = T; fn next(&mut self) -> Option { self.0.next()? } } }