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}