common/
input.rs

1use crate::utils::str_from_utf8;
2use std::io::{BufRead, Result};
3
4pub enum Terminator {
5    Newline { required: bool },
6    Byte { value: u8, required: bool },
7    None,
8}
9
10pub struct Splitter<I: BufRead> {
11    input: I,
12    terminator: Terminator,
13    buffer: Vec<u8>,
14}
15
16impl<I: BufRead> Splitter<I> {
17    pub fn new(input: I, terminator: Terminator) -> Self {
18        Self {
19            input,
20            terminator,
21            buffer: Vec::new(),
22        }
23    }
24
25    pub fn read(&mut self) -> Result<Option<(&str, usize)>> {
26        self.buffer.clear();
27
28        let mut size = match self.terminator {
29            Terminator::Newline { .. } => self.input.read_until(b'\n', &mut self.buffer)?,
30            Terminator::Byte { value, .. } => self.input.read_until(value, &mut self.buffer)?,
31            Terminator::None => self.input.read_to_end(&mut self.buffer)?,
32        };
33
34        if size > 0 {
35            let orig_size = size;
36
37            let valid = match &self.terminator {
38                Terminator::Newline { required } => {
39                    if size > 0 && self.buffer[size - 1] == b'\n' {
40                        size -= 1;
41                        if size > 0 && self.buffer[size - 1] == b'\r' {
42                            size -= 1;
43                        }
44                        true
45                    } else {
46                        !required
47                    }
48                }
49                Terminator::Byte { value, required } => {
50                    if size > 0 && self.buffer[size - 1] == *value {
51                        size -= 1;
52                        true
53                    } else {
54                        !required
55                    }
56                }
57                Terminator::None => true,
58            };
59
60            if valid {
61                return str_from_utf8(&self.buffer[..size]).map(|str| Some((str, orig_size)));
62            }
63        }
64
65        Ok(None)
66    }
67}
68
69#[cfg(test)]
70mod tests {
71    use super::*;
72    use crate::testing::unpack_io_error;
73    use test_case::test_case;
74
75    const NONE: Terminator = Terminator::None;
76    const NL_REQ: Terminator = Terminator::Newline { required: true };
77    const NL_OPT: Terminator = Terminator::Newline { required: false };
78    const B0_REQ: Terminator = Terminator::Byte {
79        value: 0,
80        required: true,
81    };
82    const B0_OPT: Terminator = Terminator::Byte {
83        value: 0,
84        required: false,
85    };
86
87    #[test_case(NL_REQ, "",                   0, None                    ; "newline required lf empty")]
88    #[test_case(NL_REQ, "abc\0\n\0def",       0, Some(("abc\0", 5))      ; "newline required lf between 0")]
89    #[test_case(NL_REQ, "abc\0\n\0def",       1, None                    ; "newline required lf between 1")]
90    #[test_case(NL_REQ, "abc\0\n\0def\n",     0, Some(("abc\0", 5))      ; "newline required lf between end 0")]
91    #[test_case(NL_REQ, "abc\0\n\0def\n",     1, Some(("\0def", 5))      ; "newline required lf between end 1")]
92    #[test_case(NL_REQ, "abc\0\n\0def\n",     2, None                    ; "newline required lf between end 2")]
93    #[test_case(NL_REQ, "\n\n",               0, Some(("", 1))           ; "newline required lf consecutive 0")]
94    #[test_case(NL_REQ, "\n\n",               1, Some(("", 1))           ; "newline required lf consecutive 1")]
95    #[test_case(NL_REQ, "\n\n",               2, None                    ; "newline required lf consecutive 2")]
96    #[test_case(NL_REQ, "abc\0\r\n\0def",     0, Some(("abc\0", 6))      ; "newline required cr lf between 0")]
97    #[test_case(NL_REQ, "abc\0\r\n\0def",     1, None                    ; "newline required cr lf between 1")]
98    #[test_case(NL_REQ, "abc\0\r\n\0def\r\n", 0, Some(("abc\0", 6))      ; "newline required cr lf between end 0")]
99    #[test_case(NL_REQ, "abc\0\r\n\0def\r\n", 1, Some(("\0def", 6))      ; "newline required cr lf between end 1")]
100    #[test_case(NL_REQ, "abc\0\r\n\0def\r\n", 2, None                    ; "newline required cr lf between end 2")]
101    #[test_case(NL_REQ, "\r\n\n\r\n",         0, Some(("", 2))           ; "newline required cr lf consecutive 0")]
102    #[test_case(NL_REQ, "\r\n\n\r\n",         1, Some(("", 1))           ; "newline required cr lf consecutive 1")]
103    #[test_case(NL_REQ, "\r\n\n\r\n",         2, Some(("", 2))           ; "newline required cr lf consecutive 2")]
104    #[test_case(NL_REQ, "\r\n\n\r\n",         3, None                    ; "newline required cr lf consecutive 3")]
105    #[test_case(NL_OPT, "",                   0, None                    ; "newline optional lf empty")]
106    #[test_case(NL_OPT, "abc\0\n\0def",       0, Some(("abc\0", 5))      ; "newline optional lf between 0")]
107    #[test_case(NL_OPT, "abc\0\n\0def",       1, Some(("\0def", 4))      ; "newline optional lf between 1")]
108    #[test_case(NL_OPT, "abc\0\n\0def",       2, None                    ; "newline optional lf between 2")]
109    #[test_case(NL_OPT, "abc\0\n\0def\n",     0, Some(("abc\0", 5))      ; "newline optional lf between end 0")]
110    #[test_case(NL_OPT, "abc\0\n\0def\n",     1, Some(("\0def", 5))      ; "newline optional lf between end 1")]
111    #[test_case(NL_OPT, "abc\0\n\0def\n",     2, None                    ; "newline optional lf between end 2")]
112    #[test_case(NL_OPT, "\n\n",               0, Some(("", 1))           ; "newline optional lf consecutive 0")]
113    #[test_case(NL_OPT, "\n\n",               1, Some(("", 1))           ; "newline optional lf consecutive 1")]
114    #[test_case(NL_OPT, "\n\n",               2, None                    ; "newline optional lf consecutive 2")]
115    #[test_case(NL_OPT, "abc\0\r\n\0def",     0, Some(("abc\0", 6))      ; "newline optional cr lf between 0")]
116    #[test_case(NL_OPT, "abc\0\r\n\0def",     1, Some(("\0def", 4))      ; "newline optional cr lf between 1")]
117    #[test_case(NL_OPT, "abc\0\r\n\0def",     2, None                    ; "newline optional cr lf between 2")]
118    #[test_case(NL_OPT, "abc\0\r\n\0def\r\n", 0, Some(("abc\0", 6))      ; "newline optional cr lf between end 0")]
119    #[test_case(NL_OPT, "abc\0\r\n\0def\r\n", 1, Some(("\0def", 6))      ; "newline optional cr lf between end 1")]
120    #[test_case(NL_OPT, "abc\0\r\n\0def\r\n", 2, None                    ; "newline optional cr lf between end 2")]
121    #[test_case(NL_OPT, "\r\n\n\r\n",         0, Some(("", 2))           ; "newline optional cr lf consecutive 0")]
122    #[test_case(NL_OPT, "\r\n\n\r\n",         1, Some(("", 1))           ; "newline optional cr lf consecutive 1")]
123    #[test_case(NL_OPT, "\r\n\n\r\n",         2, Some(("", 2))           ; "newline optional cr lf consecutive 2")]
124    #[test_case(NL_OPT, "\r\n\n\r\n",         3, None                    ; "newline optional cr lf consecutive 3")]
125    #[test_case(B0_REQ, "",                   0, None                    ; "byte required empty")]
126    #[test_case(B0_REQ, "abc\n\0\ndef",       0, Some(("abc\n", 5))      ; "byte required between 0")]
127    #[test_case(B0_REQ, "abc\n\0\ndef",       1, None                    ; "byte required between 1")]
128    #[test_case(B0_REQ, "abc\n\0\ndef\0",     0, Some(("abc\n", 5))      ; "byte required between end 0")]
129    #[test_case(B0_REQ, "abc\n\0\ndef\0",     1, Some(("\ndef", 5))      ; "byte required between end 1")]
130    #[test_case(B0_REQ, "abc\n\0\ndef\0",     2, None                    ; "byte required between end 2")]
131    #[test_case(B0_REQ, "\0\0",               0, Some(("", 1))           ; "byte required consecutive 0")]
132    #[test_case(B0_REQ, "\0\0",               1, Some(("", 1))           ; "byte required consecutive 1")]
133    #[test_case(B0_REQ, "\0\0",               2, None                    ; "byte required consecutive 2")]
134    #[test_case(B0_OPT, "",                   0, None                    ; "byte optional empty")]
135    #[test_case(B0_OPT, "abc\n\0\ndef",       0, Some(("abc\n", 5))      ; "byte optional between 0")]
136    #[test_case(B0_OPT, "abc\n\0\ndef",       1, Some(("\ndef", 4))      ; "byte optional between 1")]
137    #[test_case(B0_OPT, "abc\n\0\ndef",       2, None                    ; "byte optional between 2")]
138    #[test_case(B0_OPT, "abc\n\0\ndef\0",     0, Some(("abc\n", 5))      ; "byte optional between end 0")]
139    #[test_case(B0_OPT, "abc\n\0\ndef\0",     1, Some(("\ndef", 5))      ; "byte optional between end 1")]
140    #[test_case(B0_OPT, "abc\n\0\ndef\0",     2, None                    ; "byte optional between end 2")]
141    #[test_case(B0_OPT, "\0\0",               0, Some(("", 1))           ; "byte optional consecutive 0")]
142    #[test_case(B0_OPT, "\0\0",               1, Some(("", 1))           ; "byte optional consecutive 1")]
143    #[test_case(B0_OPT, "\0\0",               2, None                    ; "byte optional consecutive 2")]
144    #[test_case(NONE,   "",                   0, None                    ; "none empty")]
145    #[test_case(NONE,   "abc\n\0def",         0, Some(("abc\n\0def", 8)) ; "none nonempty 0")]
146    #[test_case(NONE,   "abc\n\0def",         1, None                    ; "none nonempty 1")]
147    fn read(terminator: Terminator, input: &str, position: usize, result: Option<(&str, usize)>) {
148        let mut splitter = Splitter::new(input.as_bytes(), terminator);
149        for _ in 0..position {
150            splitter.read().unwrap_or_default();
151        }
152        assert_eq!(splitter.read().map_err(unpack_io_error), Ok(result));
153    }
154}