1use super::word::{Word, WordError};
2
3#[derive(Debug)]
15pub struct Sentence<'a> {
16 data: &'a [u8],
17 position: usize,
18}
19
20impl<'a> Sentence<'a> {
21 pub fn new(data: &'a [u8]) -> Self {
27 Self { data, position: 0 }
28 }
29}
30
31impl<'a> Iterator for Sentence<'a> {
32 type Item = Result<Word<'a>, SentenceError>;
33
34 fn next(&mut self) -> Option<Self::Item> {
45 if self.position >= self.data.len() {
46 return None;
47 }
48
49 let mut start = self.position;
50
51 match read_length(&self.data[start..]) {
52 Ok((lenght, bytes_read)) => {
53 if lenght == 0 {
55 return None;
56 }
57 start += bytes_read;
59
60 let end = start + lenght as usize;
62
63 let word = || -> Result<Word, SentenceError> {
64 let data = &self
66 .data
67 .get(start..end)
68 .ok_or(SentenceError::PrefixLength)?;
69 let word = Word::try_from(*data).map_err(SentenceError::from)?;
70
71 Ok(word)
72 }();
73
74 self.position = end;
76
77 Some(word)
78 }
79 Err(e) => Some(Err(e)),
80 }
81 }
82}
83
84#[derive(Debug, PartialEq)]
88pub enum SentenceError {
89 WordError(WordError),
91 PrefixLength,
94 }
99
100impl From<WordError> for SentenceError {
101 fn from(e: WordError) -> Self {
102 Self::WordError(e)
103 }
104}
105
106fn read_length(data: &[u8]) -> Result<(u32, usize), SentenceError> {
108 let mut c: u32 = data[0] as u32;
109 if c & 0x80 == 0x00 {
110 Ok((c, 1))
111 } else if c & 0xC0 == 0x80 {
112 c &= !0xC0;
113 c <<= 8;
114 c += data[1] as u32;
115 return Ok((c, 2));
116 } else if c & 0xE0 == 0xC0 {
117 c &= !0xE0;
118 c <<= 8;
119 c += data[1] as u32;
120 c <<= 8;
121 c += data[2] as u32;
122 return Ok((c, 3));
123 } else if c & 0xF0 == 0xE0 {
124 c &= !0xF0;
125 c <<= 8;
126 c += data[1] as u32;
127 c <<= 8;
128 c += data[2] as u32;
129 c <<= 8;
130 c += data[3] as u32;
131 return Ok((c, 4));
132 } else if c & 0xF8 == 0xF0 {
133 c = data[1] as u32;
134 c <<= 8;
135 c += data[2] as u32;
136 c <<= 8;
137 c += data[3] as u32;
138 c <<= 8;
139 c += data[4] as u32;
140 return Ok((c, 5));
141 } else {
142 Err(SentenceError::PrefixLength)
143 }
144}
145
146#[cfg(test)]
147mod tests {
148 use crate::protocol::word::{Word, WordCategory};
149
150 use super::*;
151
152 #[test]
153 fn test_sentence_iterator() {
154 let data: &[u8] = &[
155 0x05, b'!', b'd', b'o', b'n', b'e', 0x08, b'.', b't', b'a', b'g', b'=', b'1', b'2', b'3', 0x0C, b'=', b'n', b'a', b'm', b'e', b'=', b'e', b't', b'h', b'e', b'r',
158 b'1', 0x00, ];
161
162 let mut sentence = Sentence::new(data);
163
164 assert_eq!(
165 sentence.next().unwrap().unwrap(),
166 Word::Category(WordCategory::Done)
167 );
168
169 assert_eq!(sentence.next().unwrap().unwrap(), Word::Tag(123));
170
171 assert_eq!(
172 sentence.next().unwrap().unwrap(),
173 Word::Attribute(("name", Some("ether1")).into())
174 );
175
176 assert_eq!(sentence.next(), None);
177 }
178
179 #[test]
180 fn test_sentence_category_error() {
181 let data: &[u8] = &[
183 0x0A, b'.', b't', b'a', b'g', b'=', b'1', b'2', b'3', 0x0D, b'=', b'n', b'a', b'm', b'e', b'=', b'e', b't', b'h', b'e', b'r',
185 b'1', ];
187
188 let mut sentence = Sentence::new(data);
189
190 assert!(sentence.next().unwrap().is_err());
191 }
192
193 #[test]
194 fn test_sentence_length_error() {
195 let data: &[u8] = &[
197 0xF8, b'.', b't', b'a', b'g', b'=', b'1', b'2', b'3', ];
199
200 let mut sentence = Sentence::new(data);
201
202 assert!(sentence.next().unwrap().is_err());
203 }
204
205 #[test]
206 fn test_complete_sentence_parsing() {
207 let data: &[u8] = &[
208 0x05, b'!', b'd', b'o', b'n', b'e', 0x08, b'.', b't', b'a', b'g', b'=', b'1', b'2', b'3', 0x0C, b'=', b'n', b'a', b'm', b'e', b'=', b'e', b't', b'h', b'e', b'r',
211 b'1', 0x00, ];
214
215 let mut sentence = Sentence::new(data);
216
217 assert_eq!(
218 sentence.next().unwrap().unwrap(),
219 Word::Category(WordCategory::Done)
220 );
221
222 assert_eq!(sentence.next().unwrap().unwrap(), Word::Tag(123));
223
224 assert_eq!(
225 sentence.next().unwrap().unwrap(),
226 Word::Attribute(("name", Some("ether1")).into())
227 );
228
229 assert_eq!(sentence.next(), None);
230 }
231
232 #[test]
233 fn test_sentence_with_invalid_length() {
234 let data: &[u8] = &[
235 0xF8, b'.', b't', b'a', b'g', b'=', b'1', b'2', b'3', ];
237
238 let mut sentence = Sentence::new(data);
239
240 assert!(sentence.next().unwrap().is_err());
241 }
242
243 #[test]
244 fn test_sentence_without_category() {
245 let data: &[u8] = &[
246 0x0A, b'.', b't', b'a', b'g', b'=', b'1', b'2', b'3', 0x0D, b'=', b'n', b'a', b'm', b'e', b'=', b'e', b't', b'h', b'e', b'r',
248 b'1', ];
250
251 let mut sentence = Sentence::new(data);
252
253 assert!(sentence.next().unwrap().is_err());
254 }
255
256 #[test]
257 fn test_mixed_words_sentence() {
258 let data: &[u8] = &[
259 0x03, b'!', b'r', b'e', 0x04, b'=', b'a', b'=', b'b', 0x08, b'.', b't', b'a', b'g', b'=', b'4', b'5', b'6', 0x00, ];
264
265 let mut sentence = Sentence::new(data);
266
267 assert_eq!(
268 sentence.next().unwrap().unwrap(),
269 Word::Category(WordCategory::Reply)
270 );
271
272 assert_eq!(
273 sentence.next().unwrap().unwrap(),
274 Word::Attribute(("a", Some("b")).into())
275 );
276
277 assert_eq!(sentence.next().unwrap().unwrap(), Word::Tag(456));
278
279 assert_eq!(sentence.next(), None);
280 }
281
282 #[test]
283 fn test_sentence_with_fatal_message() {
284 let data: &[u8] = &[
285 0x06, b'!', b'f', b'a', b't', b'a', b'l', 0x0B, b's', b'e', b'r', b'v', b'e', b'r',
286 b' ', b'd', b'o', b'w', b'n', 0x00, ];
289
290 let mut sentence = Sentence::new(data);
291
292 assert_eq!(
293 sentence.next().unwrap().unwrap(),
294 Word::Category(WordCategory::Fatal)
295 );
296
297 assert_eq!(
298 sentence.next().unwrap().unwrap(),
299 Word::Message("server down")
300 );
301
302 assert_eq!(sentence.next(), None);
303 }
304
305 #[test]
306 fn test_complete_sentence_with_extra_data() {
307 let data: &[u8] = &[
308 0x05, b'!', b'd', b'o', b'n', b'e', 0x08, b'.', b't', b'a', b'g', b'=', b'1', b'2', b'3', 0x0C, b'=', b'n', b'a', b'm', b'e', b'=', b'e', b't', b'h', b'e', b'r',
311 b'1', 0x00, 0x07, b'!', b'd', b'o', b'n', b'e', ];
315
316 let mut sentence = Sentence::new(data);
317
318 assert_eq!(
319 sentence.next().unwrap().unwrap(),
320 Word::Category(WordCategory::Done)
321 );
322
323 assert_eq!(sentence.next().unwrap().unwrap(), Word::Tag(123));
324
325 assert_eq!(
326 sentence.next().unwrap().unwrap(),
327 Word::Attribute(("name", Some("ether1")).into())
328 );
329
330 assert_eq!(sentence.next(), None);
331
332 assert_eq!(sentence.next(), None);
334 }
335}