1use std::io::{self, BufRead};
6
7pub trait BufReadExt: BufRead {
9 fn lending_lines(&mut self) -> LendingLines<'_, Self>
15 where
16 Self: Sized,
17 {
18 LendingLines { reader: self, buffer: String::new() }
19 }
20}
21
22impl<R: BufRead> BufReadExt for R {}
23
24pub struct LendingLines<'a, R: BufRead> {
28 reader: &'a mut R,
29 buffer: String,
30}
31
32impl<'a, R: BufRead> LendingLines<'a, R> {
33 pub fn next(&mut self) -> Option<io::Result<&str>> {
36 self.buffer.clear();
37 match self.reader.read_line(&mut self.buffer) {
38 Ok(0) => None,
39 Ok(_) => {
40 let mut len = self.buffer.len();
41 if len > 0 && self.buffer.as_bytes()[len - 1] == b'\n' {
42 len -= 1;
43 if len > 0 && self.buffer.as_bytes()[len - 1] == b'\r' {
44 len -= 1;
45 }
46 }
47 Some(Ok(&self.buffer[..len]))
48 }
49 Err(e) => Some(Err(e)),
50 }
51 }
52}
53
54#[cfg(test)]
55mod tests {
56 use super::*;
57 use std::io::Cursor;
58
59 #[test]
60 fn test_lending_lines_empty() {
61 let data = "";
62 let mut cursor = Cursor::new(data);
63 let mut lines = cursor.lending_lines();
64
65 assert!(lines.next().is_none());
66 }
67
68 #[test]
69 fn test_lending_lines() {
70 let data = "line1\nline2\r\nline3";
71 let mut cursor = Cursor::new(data);
72 let mut lines = cursor.lending_lines();
73
74 assert_eq!(lines.next().unwrap().unwrap(), "line1");
75 assert_eq!(lines.next().unwrap().unwrap(), "line2");
76 assert_eq!(lines.next().unwrap().unwrap(), "line3");
77 assert!(lines.next().is_none());
78 }
79}