1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#![warn(missing_docs)]
//!
//! Normalize line endings
//!
//! This crate provides a `normalize` method that takes a char iterator and returns
//! a new one with `\n` for all line endings

/// This struct wraps a `std::io::Chars` to normalize line endings.
///
/// Implements `Iterator<Item=char>` so can be used in place
pub struct Normalized<I>
where I: Iterator<Item=char> {
    iter: I,
    prev_was_cr: bool
}

/// Take a Chars and return similar struct with normalized line endings
///
/// # Example
/// ```
/// use std::iter::FromIterator;
/// use normalize_line_endings::normalized;
///
/// let input = "This is a string \n with \r some \n\r\n random newlines\r\r\n\n";
/// assert_eq!(
///     &String::from_iter(normalized(input.chars())),
///     "This is a string \n with \n some \n\n random newlines\n\n\n"
/// );
/// ```
pub fn normalized<I>(iter: I) -> Normalized<I>
where I: Iterator<Item=char> {
    Normalized { iter: iter, prev_was_cr: false }
}

impl<I: Iterator<Item=char>> Iterator for Normalized<I> {
    type Item = char;
    fn next(&mut self) -> Option<char> {
        match self.iter.next() {
            Some('\n') if self.prev_was_cr => {
                self.prev_was_cr = false;
                match self.iter.next() {
                    Some('\r') => {
                        self.prev_was_cr = true;
                        Some('\n')
                    },
                    any => {
                        self.prev_was_cr = false;
                        any
                    }
                }
            },
            Some('\r') => {
                self.prev_was_cr = true;
                Some('\n')
            },
            any => {
                self.prev_was_cr = false;
                any
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use std::io::prelude::*;
    use super::normalized;
    use std::iter::FromIterator;

    #[test]
    fn normalize() {
        let input = "This is a string \n with \r some \n\r\n random newlines\r\r\n\n";
        assert_eq!(
            &String::from_iter(normalized(input.chars())),
            "This is a string \n with \n some \n\n random newlines\n\n\n"
        );
    }
}