partial-io 0.5.4

Helpers to test partial, interrupted and would-block I/O operations, with support for property-based testing through proptest and quickcheck.
Documentation
// Copyright (c) The partial-io Contributors
// SPDX-License-Identifier: MIT

//! This module contains a reader wrapper that breaks its inputs up according to
//! a provided iterator.

use std::{
    cmp, fmt,
    io::{self, Read, Write},
};

use crate::{make_ops, PartialOp};

/// A reader wrapper that breaks inner `Read` instances up according to the
/// provided iterator.
///
/// # Examples
///
/// ```rust
/// use std::io::{Cursor, Read};
///
/// use partial_io::{PartialOp, PartialRead};
///
/// let reader = Cursor::new(vec![1, 2, 3, 4]);
/// let iter = ::std::iter::repeat(PartialOp::Limited(1));
/// let mut partial_reader = PartialRead::new(reader, iter);
/// let mut out = vec![0; 256];
///
/// let size = partial_reader.read(&mut out).unwrap();
/// assert_eq!(size, 1);
/// assert_eq!(&out[..1], &[1]);
/// ```
pub struct PartialRead<R> {
    inner: R,
    ops: Box<dyn Iterator<Item = PartialOp> + Send>,
}

impl<R> PartialRead<R>
where
    R: Read,
{
    /// Creates a new `PartialRead` wrapper over the reader with the specified `PartialOp`s.
    pub fn new<I>(inner: R, iter: I) -> Self
    where
        I: IntoIterator<Item = PartialOp> + 'static,
        I::IntoIter: Send,
    {
        PartialRead {
            inner,
            ops: make_ops(iter),
        }
    }

    /// Sets the `PartialOp`s for this reader.
    pub fn set_ops<I>(&mut self, iter: I) -> &mut Self
    where
        I: IntoIterator<Item = PartialOp> + 'static,
        I::IntoIter: Send,
    {
        self.ops = make_ops(iter);
        self
    }

    /// Acquires a reference to the underlying reader.
    pub fn get_ref(&self) -> &R {
        &self.inner
    }

    /// Acquires a mutable reference to the underlying reader.
    pub fn get_mut(&mut self) -> &mut R {
        &mut self.inner
    }

    /// Consumes this wrapper, returning the underlying reader.
    pub fn into_inner(self) -> R {
        self.inner
    }
}

impl<R> Read for PartialRead<R>
where
    R: Read,
{
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        match self.ops.next() {
            Some(PartialOp::Limited(n)) => {
                let len = cmp::min(n, buf.len());
                self.inner.read(&mut buf[..len])
            }
            Some(PartialOp::Err(err)) => Err(io::Error::new(
                err,
                "error during read, generated by partial-io",
            )),
            Some(PartialOp::Unlimited) | None => self.inner.read(buf),
        }
    }
}

// Forwarding impl to support duplex structs.
impl<R> Write for PartialRead<R>
where
    R: Read + Write,
{
    #[inline]
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.inner.write(buf)
    }

    #[inline]
    fn flush(&mut self) -> io::Result<()> {
        self.inner.flush()
    }
}

impl<R> fmt::Debug for PartialRead<R>
where
    R: fmt::Debug,
{
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("PartialRead")
            .field("inner", &self.inner)
            .finish()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    use std::fs::File;

    use crate::tests::assert_send;

    #[test]
    fn test_sendable() {
        assert_send::<PartialRead<File>>();
    }
}