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
use io::BufRead;
use std::io;
/// Line endings for lines of output to standard out or standard error
pub enum LineEnding {
/// Linefeed line ending (`\n` or `0x0a`).
/// This is the standard Linux ending
Lf,
/// Carriage return (`\r` or `0x0d`).
/// This is used for some MacOs programs
Cr,
/// Carriage Return + Line feed (`\r\n` or `\0x0a\x0d`).
/// This is the standard windows/internet ending
Crlf,
}
impl LineEnding {
/// Returns true if and only if `self` is a line feed
pub fn is_line_feed(&self) -> bool {
matches!(self, LineEnding::Lf)
}
/// Returns true if and only if `self` is a carriage return
pub fn is_carriage_return(&self) -> bool {
matches!(self, LineEnding::Cr)
}
/// Returns true if and only if `self` is a carriage return followed by a line feed
pub fn is_carriage_return_line_feed(&self) -> bool {
matches!(self, LineEnding::Crlf)
}
}
pub fn get_line<R>(reader: &mut R) -> io::Result<Option<(LineEnding, Vec<u8>)>>
where
R: BufRead,
{
let mut read_bytes = Vec::new();
let mut buf = reader.fill_buf()?;
let mut num_consumed = 0;
let mut seen_cr = false;
loop {
if buf.is_empty() {
if !read_bytes.is_empty() {
return Ok(Some((LineEnding::Lf, read_bytes)));
}
return Ok(None);
}
for byte in buf {
num_consumed += 1;
if *byte == b'\r' {
seen_cr = true;
} else if *byte == b'\n' {
reader.consume(num_consumed);
if seen_cr {
return Ok(Some((LineEnding::Crlf, read_bytes)));
}
return Ok(Some((LineEnding::Lf, read_bytes)));
} else if seen_cr {
reader.consume(num_consumed - 1);
return Ok(Some((LineEnding::Cr, read_bytes)));
} else {
seen_cr = false;
read_bytes.push(*byte);
}
}
reader.consume(num_consumed);
num_consumed = 0;
buf = reader.fill_buf()?;
}
}