Skip to main content

thin_fetch/parcel/
decoder.rs

1use crate::error::{Error, WithError as _, error};
2
3use super::{MARKER_END, MARKER_SEP};
4
5#[derive(Debug, PartialEq, Eq)]
6pub enum ParcelItem {
7    Data(Vec<u8>),
8    Separator,
9    End,
10}
11
12pub struct ParcelDecoder<R: std::io::Read> {
13    pub(crate) reader: R,
14    finished: bool,
15    pub(crate) ignore_end: bool,
16}
17
18impl<R: std::io::Read> ParcelDecoder<R> {
19    pub fn new(reader: R) -> Self {
20        Self {
21            reader,
22            finished: false,
23            ignore_end: false,
24        }
25    }
26
27    /// Get single raw parcel item from the response, including [`ParcelItem::End`].
28    ///
29    /// # Errors
30    ///
31    /// Returns an error when the length cannot be read/parsed, or when data cannot be read.
32    pub fn get_one(&mut self) -> Result<ParcelItem, Error> {
33        let mut len_buf: [u8; 4] = [0; 4];
34
35        self.reader
36            .read_exact(&mut len_buf)
37            .with_err_msg("could not read length")?;
38
39        if len_buf == MARKER_END {
40            return Ok(ParcelItem::End);
41        }
42
43        if len_buf == MARKER_SEP {
44            return Ok(ParcelItem::Separator);
45        }
46
47        let length_raw_str = str::from_utf8(&len_buf).with_err_msg("length is not valid utf-8")?;
48
49        let length = usize::from_str_radix(length_raw_str, 16)
50            .with_err(|| error!("length is not a valid number: {length_raw_str}"))?;
51
52        let mut data = vec![0; length - 4];
53        self.reader
54            .read_exact(&mut data)
55            .with_err_msg("could not read data")?;
56
57        Ok(ParcelItem::Data(data))
58    }
59}
60
61impl<R: std::io::Read> Iterator for ParcelDecoder<R> {
62    type Item = Result<ParcelItem, Error>;
63
64    fn next(&mut self) -> Option<Self::Item> {
65        if self.finished {
66            return None;
67        }
68
69        let item = self.get_one().with_err_msg("get next item");
70
71        match item {
72            Ok(ParcelItem::End) if !self.ignore_end => {
73                self.finished = true;
74                None
75            }
76            Err(err) if self.ignore_end && err.message.contains("failed to fill whole buffer") => {
77                self.finished = true;
78                None
79            }
80            res => Some(res),
81        }
82    }
83}