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}