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 79 80 81 82
#![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
struct Normalized<I> {
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"
/// );
/// ```
#[inline]
pub fn normalized(iter: impl Iterator<Item = char>) -> impl Iterator<Item = char> {
Normalized {
iter,
prev_was_cr: false,
}
}
impl<I> Iterator for Normalized<I>
where
I: Iterator<Item = char>,
{
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
}
}
}
}
// tests
#[cfg(test)]
mod tests {
use std::iter::FromIterator;
#[test]
fn 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(super::normalized(input.chars())),
"This is a string \n with \n some \n\n random newlines\n\n\n"
);
}
}