partial_io/
read.rs

1// Copyright (c) The partial-io Contributors
2// SPDX-License-Identifier: MIT
3
4//! This module contains a reader wrapper that breaks its inputs up according to
5//! a provided iterator.
6
7use std::{
8    cmp, fmt,
9    io::{self, Read, Write},
10};
11
12use crate::{make_ops, PartialOp};
13
14/// A reader wrapper that breaks inner `Read` instances up according to the
15/// provided iterator.
16///
17/// # Examples
18///
19/// ```rust
20/// use std::io::{Cursor, Read};
21///
22/// use partial_io::{PartialOp, PartialRead};
23///
24/// let reader = Cursor::new(vec![1, 2, 3, 4]);
25/// let iter = ::std::iter::repeat(PartialOp::Limited(1));
26/// let mut partial_reader = PartialRead::new(reader, iter);
27/// let mut out = vec![0; 256];
28///
29/// let size = partial_reader.read(&mut out).unwrap();
30/// assert_eq!(size, 1);
31/// assert_eq!(&out[..1], &[1]);
32/// ```
33pub struct PartialRead<R> {
34    inner: R,
35    ops: Box<dyn Iterator<Item = PartialOp> + Send>,
36}
37
38impl<R> PartialRead<R>
39where
40    R: Read,
41{
42    /// Creates a new `PartialRead` wrapper over the reader with the specified `PartialOp`s.
43    pub fn new<I>(inner: R, iter: I) -> Self
44    where
45        I: IntoIterator<Item = PartialOp> + 'static,
46        I::IntoIter: Send,
47    {
48        PartialRead {
49            inner,
50            ops: make_ops(iter),
51        }
52    }
53
54    /// Sets the `PartialOp`s for this reader.
55    pub fn set_ops<I>(&mut self, iter: I) -> &mut Self
56    where
57        I: IntoIterator<Item = PartialOp> + 'static,
58        I::IntoIter: Send,
59    {
60        self.ops = make_ops(iter);
61        self
62    }
63
64    /// Acquires a reference to the underlying reader.
65    pub fn get_ref(&self) -> &R {
66        &self.inner
67    }
68
69    /// Acquires a mutable reference to the underlying reader.
70    pub fn get_mut(&mut self) -> &mut R {
71        &mut self.inner
72    }
73
74    /// Consumes this wrapper, returning the underlying reader.
75    pub fn into_inner(self) -> R {
76        self.inner
77    }
78}
79
80impl<R> Read for PartialRead<R>
81where
82    R: Read,
83{
84    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
85        match self.ops.next() {
86            Some(PartialOp::Limited(n)) => {
87                let len = cmp::min(n, buf.len());
88                self.inner.read(&mut buf[..len])
89            }
90            Some(PartialOp::Err(err)) => Err(io::Error::new(
91                err,
92                "error during read, generated by partial-io",
93            )),
94            Some(PartialOp::Unlimited) | None => self.inner.read(buf),
95        }
96    }
97}
98
99// Forwarding impl to support duplex structs.
100impl<R> Write for PartialRead<R>
101where
102    R: Read + Write,
103{
104    #[inline]
105    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
106        self.inner.write(buf)
107    }
108
109    #[inline]
110    fn flush(&mut self) -> io::Result<()> {
111        self.inner.flush()
112    }
113}
114
115impl<R> fmt::Debug for PartialRead<R>
116where
117    R: fmt::Debug,
118{
119    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
120        f.debug_struct("PartialRead")
121            .field("inner", &self.inner)
122            .finish()
123    }
124}
125
126#[cfg(test)]
127mod tests {
128    use super::*;
129
130    use std::fs::File;
131
132    use crate::tests::assert_send;
133
134    #[test]
135    fn test_sendable() {
136        assert_send::<PartialRead<File>>();
137    }
138}