use crate::error::{Error, WithError as _, error};
use super::{MARKER_END, MARKER_SEP};
#[derive(Debug, PartialEq, Eq)]
pub enum ParcelItem {
Data(Vec<u8>),
Separator,
End,
}
pub struct ParcelDecoder<R: std::io::Read> {
pub(crate) reader: R,
finished: bool,
pub(crate) ignore_end: bool,
}
impl<R: std::io::Read> ParcelDecoder<R> {
pub fn new(reader: R) -> Self {
Self {
reader,
finished: false,
ignore_end: false,
}
}
pub fn get_one(&mut self) -> Result<ParcelItem, Error> {
let mut len_buf: [u8; 4] = [0; 4];
self.reader
.read_exact(&mut len_buf)
.with_err_msg("could not read length")?;
if len_buf == MARKER_END {
return Ok(ParcelItem::End);
}
if len_buf == MARKER_SEP {
return Ok(ParcelItem::Separator);
}
let length_raw_str = str::from_utf8(&len_buf).with_err_msg("length is not valid utf-8")?;
let length = usize::from_str_radix(length_raw_str, 16)
.with_err(|| error!("length is not a valid number: {length_raw_str}"))?;
let mut data = vec![0; length - 4];
self.reader
.read_exact(&mut data)
.with_err_msg("could not read data")?;
Ok(ParcelItem::Data(data))
}
}
impl<R: std::io::Read> Iterator for ParcelDecoder<R> {
type Item = Result<ParcelItem, Error>;
fn next(&mut self) -> Option<Self::Item> {
if self.finished {
return None;
}
let item = self.get_one().with_err_msg("get next item");
match item {
Ok(ParcelItem::End) if !self.ignore_end => {
self.finished = true;
None
}
Err(err) if self.ignore_end && err.message.contains("failed to fill whole buffer") => {
self.finished = true;
None
}
res => Some(res),
}
}
}