normalized_line_endings/normalized.rs
1//! # Normalized iterator implementation
2
3use crate::common::{CR, LF};
4
5/// Trait extension for iterating over characters with normalized line endings.
6pub trait Normalized: Iterator<Item = char> {
7 /// Returns an iterator over characters with normalized line endings.
8 ///
9 /// # Examples
10 ///
11 /// ```
12 /// use normalized_line_endings::Normalized;
13 ///
14 /// let input = "This is a string \n with \r some \n\r\n random newlines\r\r\n\n";
15 /// assert_eq!(
16 /// "This is a string \n with \n some \n\n random newlines\n\n\n",
17 /// input.chars().normalized().collect::<String>()
18 /// );
19 /// ```
20 fn normalized(self) -> impl Iterator<Item = char>;
21}
22
23impl<I> Normalized for I
24where
25 I: Iterator<Item = char>,
26{
27 fn normalized(self) -> impl Iterator<Item = char> {
28 normalized(self)
29 }
30}
31
32/// Returns an iterator over characters with normalized line endings.
33///
34/// # Examples
35///
36/// ```
37/// use normalized_line_endings::normalized;
38///
39/// let input = "This is a string \n with \r some \n\r\n random newlines\r\r\n\n";
40/// assert_eq!(
41/// "This is a string \n with \n some \n\n random newlines\n\n\n",
42/// normalized(input.chars()).collect::<String>()
43/// );
44/// ```
45pub fn normalized(iter: impl Iterator<Item = char>) -> impl Iterator<Item = char> {
46 NormalizedLineEndings { iter, peeked: None }
47}
48
49struct NormalizedLineEndings<I> {
50 iter: I,
51 peeked: Option<char>,
52}
53
54impl<I: Iterator<Item = char>> Iterator for NormalizedLineEndings<I> {
55 type Item = char;
56
57 fn next(&mut self) -> Option<char> {
58 match self.peeked.take().or_else(|| self.iter.next()) {
59 some_lf @ Some(LF) => some_lf,
60 Some(CR) => {
61 self.peeked = self.iter.next().filter(|ch| *ch != LF);
62 Some(LF)
63 }
64 other => other,
65 }
66 }
67}