use std::{
collections::VecDeque,
io::{self, Read},
marker,
};
#[derive(Debug)]
pub enum ErrorAction {
Raise(io::Error),
RaiseAndSkip(io::Error),
Retry,
RetryAndSkip,
}
#[derive(Debug)]
enum ReaderSource<R, I>
where
R: Read,
I: Iterator<Item = R>,
{
Single(R),
Multi(I),
}
pub struct ChainReader<'a, R, F, I = std::iter::Empty<R>>
where
R: Read,
I: Iterator<Item = R>,
F: FnMut(io::Error) -> ErrorAction,
{
current: Option<R>,
sources: VecDeque<ReaderSource<R, I>>,
error_handle: F,
_marker: marker::PhantomData<&'a R>,
}
impl<R, F, I> ChainReader<'_, R, F, I>
where
R: Read,
I: Iterator<Item = R>,
F: FnMut(io::Error) -> ErrorAction,
{
pub fn new(error_handle: F) -> Self {
Self {
current: None,
sources: VecDeque::new(),
error_handle,
_marker: marker::PhantomData,
}
}
pub fn replace_handle(&mut self, error_handle: F) {
self.error_handle = error_handle;
}
pub fn push(&mut self, reader: R) {
self.sources.push_back(ReaderSource::Single(reader));
}
pub fn push_iter<II: IntoIterator<IntoIter = I>>(&mut self, iter: II) {
self.sources
.push_back(ReaderSource::Multi(iter.into_iter()));
}
fn next(&mut self) -> bool {
while let Some(source) = self.sources.pop_front() {
match source {
ReaderSource::Single(reader) => {
self.current = Some(reader);
return true;
}
ReaderSource::Multi(mut iter) => {
if let Some(reader) = iter.next() {
self.current = Some(reader);
self.sources.push_front(ReaderSource::Multi(iter));
return true;
}
}
}
}
self.current = None;
false
}
}
impl<R, F, I> Read for ChainReader<'_, R, F, I>
where
R: Read,
I: Iterator<Item = R>,
F: FnMut(io::Error) -> ErrorAction,
{
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
loop {
if self.current.is_none() && !self.next() {
return Ok(0);
}
match self.current.as_mut().unwrap().read(buf) {
Ok(0) => {
if !self.next() {
return Ok(0);
}
}
Ok(n) => return Ok(n),
Err(e) => match (self.error_handle)(e) {
ErrorAction::Raise(e) => return Err(e),
ErrorAction::RaiseAndSkip(e) => {
self.next();
return Err(e);
}
ErrorAction::Retry => {}
ErrorAction::RetryAndSkip => {
self.next();
}
},
}
}
}
}