ChainReader

Struct ChainReader 

Source
pub struct ChainReader<'a, R, F, I = Empty<R>>
where R: Read, I: Iterator<Item = R>, F: FnMut(Error) -> ErrorAction,
{ /* private fields */ }
Expand description

A sequential chaining reader that combines multiple Read instances with configurable error handling.

§Behavior

  • Readers are consumed in FIFO (first-in, first-out) order.
  • Automatically switches to the next reader when the current one reaches EOF (returns Ok(0)).
  • Allows custom error handling to decide whether to retry or skip on I/O errors.
  • Supports both single readers and iterators that produce readers.

§Comparison with io::Chain

  • Supports a dynamic queue of readers instead of a fixed pair.
  • Configurable error handling strategies (see ErrorAction for details)
  • Automatically advances to the next reader on EOF.
  • Handles both single readers and iterators of readers.

§Examples

use chain_reader::{ChainReader, ErrorAction};
use std::io::{self, Read};

// Define a custom reader type that can handle both stdin and files
enum Readers {
    Stdin,
    File(std::fs::File),
    Cursor(io::Cursor<String>),
}

impl Read for Readers {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        match self {
            Readers::Stdin => io::stdin().read(buf),
            Readers::File(it) => it.read(buf),
            Readers::Cursor(it) => it.read(buf),
        }
    }
}

fn main() -> io::Result<()> {
    // Create a ChainReader that starts with stdin
    let mut chain = ChainReader::new(
        |e| match e.kind() {
            io::ErrorKind::Interrupted => ErrorAction::Retry,
            _ => ErrorAction::RetryAndSkip,
        },
    );


    chain.push(Readers::Stdin);
    // Add a file to the chain
    chain.push(Readers::File(std::fs::File::open("Cargo.toml")?));
    chain.push_iter(vec![
        Readers::Cursor(io::Cursor::new("hello ".to_string())),
        Readers::Cursor(io::Cursor::new("world!".to_string())),
    ]);

    // Read from the chain - first from stdin, then from the file
    let mut content = Vec::new();
    chain.read_to_end(&mut content)?;

    Ok(())
}

Implementations§

Source§

impl<R, F, I> ChainReader<'_, R, F, I>
where R: Read, I: Iterator<Item = R>, F: FnMut(Error) -> ErrorAction,

Source

pub fn new(error_handle: F) -> Self

Creates a new ChainReader with the given error handler and an empty reader queue.

Readers can be added to the queue using the Self::push and Self::push_iter methods. The error_handle callback determines behavior on I/O errors during reading.

§Parameters
  • error_handle: Error handling strategy callback
Source

pub fn replace_handle(&mut self, error_handle: F)

Replaces the current error handler with a new one.

This method allows changing the error handling strategy at runtime.

§Parameters
  • error_handle: New error handling strategy callback
Source

pub fn push(&mut self, reader: R)

Appends a single reader to the end of the processing queue.

The reader will be processed after all existing readers in the queue have either reached EOF or been skipped due to errors.

§Parameters
  • reader: Reader to add to the end of the queue
Source

pub fn push_iter<II: IntoIterator<IntoIter = I>>(&mut self, iter: II)

Appends an iterator of readers to the end of the processing queue.

The readers produced by the iterator will be processed in order after all existing readers in the queue have been processed.

§Parameters
  • iter: Iterator that produces readers to add to the end of the queue

Trait Implementations§

Source§

impl<R, F, I> Read for ChainReader<'_, R, F, I>
where R: Read, I: Iterator<Item = R>, F: FnMut(Error) -> ErrorAction,

Source§

fn read(&mut self, buf: &mut [u8]) -> Result<usize>

Reads data from the current reader, handling errors and EOF according to the configured strategy.

This implementation follows the chain reading pattern:

  1. Attempt to read from the current reader
  2. On success, return the read data
  3. On EOF, automatically advance to the next reader
  4. On error, invoke the error handler to determine the appropriate action

The method will continue this process until either:

  • Data is successfully read
  • All readers are exhausted (returning Ok(0))
  • An unhandled error occurs (returning Err)
§Parameters
  • buf: Buffer to read data into
§Returns
  • Ok(n): Successfully read n bytes
  • Err(e): IO error that couldn’t be handled by the error strategy
1.36.0 · Source§

fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> Result<usize, Error>

Like read, except that it reads into a slice of buffers. Read more
Source§

fn is_read_vectored(&self) -> bool

🔬This is a nightly-only experimental API. (can_vector)
Determines if this Reader has an efficient read_vectored implementation. Read more
1.0.0 · Source§

fn read_to_end(&mut self, buf: &mut Vec<u8>) -> Result<usize, Error>

Reads all bytes until EOF in this source, placing them into buf. Read more
1.0.0 · Source§

fn read_to_string(&mut self, buf: &mut String) -> Result<usize, Error>

Reads all bytes until EOF in this source, appending them to buf. Read more
1.6.0 · Source§

fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), Error>

Reads the exact number of bytes required to fill buf. Read more
Source§

fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> Result<(), Error>

🔬This is a nightly-only experimental API. (read_buf)
Pull some bytes from this source into the specified buffer. Read more
Source§

fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> Result<(), Error>

🔬This is a nightly-only experimental API. (read_buf)
Reads the exact number of bytes required to fill cursor. Read more
1.0.0 · Source§

fn by_ref(&mut self) -> &mut Self
where Self: Sized,

Creates a “by reference” adaptor for this instance of Read. Read more
1.0.0 · Source§

fn bytes(self) -> Bytes<Self>
where Self: Sized,

Transforms this Read instance to an Iterator over its bytes. Read more
1.0.0 · Source§

fn chain<R>(self, next: R) -> Chain<Self, R>
where R: Read, Self: Sized,

Creates an adapter which will chain this stream with another. Read more
1.0.0 · Source§

fn take(self, limit: u64) -> Take<Self>
where Self: Sized,

Creates an adapter which will read at most limit bytes from it. Read more

Auto Trait Implementations§

§

impl<'a, R, F, I> Freeze for ChainReader<'a, R, F, I>
where F: Freeze, R: Freeze,

§

impl<'a, R, F, I> RefUnwindSafe for ChainReader<'a, R, F, I>

§

impl<'a, R, F, I> Send for ChainReader<'a, R, F, I>
where F: Send, R: Send + Sync, I: Send,

§

impl<'a, R, F, I> Sync for ChainReader<'a, R, F, I>
where F: Sync, R: Sync, I: Sync,

§

impl<'a, R, F, I> Unpin for ChainReader<'a, R, F, I>
where F: Unpin, R: Unpin, I: Unpin,

§

impl<'a, R, F, I> UnwindSafe for ChainReader<'a, R, F, I>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.