read_write_ext/
lib.rs

1//! # read-write-ext
2//! [![crates.io version](https://img.shields.io/crates/v/read-write-ext.svg)](https://crates.io/crates/read-write-ext)
3//! [![license: Apache 2.0](https://gitlab.com/leonhard-llc/fixed-buffer-rs/-/raw/main/license-apache-2.0.svg)](http://www.apache.org/licenses/LICENSE-2.0)
4//! [![unsafe forbidden](https://gitlab.com/leonhard-llc/fixed-buffer-rs/-/raw/main/unsafe-forbidden-success.svg)](https://github.com/rust-secure-code/safety-dance/)
5//! [![pipeline status](https://gitlab.com/leonhard-llc/fixed-buffer-rs/badges/main/pipeline.svg)](https://gitlab.com/leonhard-llc/fixed-buffer-rs/-/pipelines)
6//!
7//! `ReadWriteExt` trait with `chain_after` and `take_rw` for `std::io::Read + Write` structs.
8//!
9//! # Features
10//! - `forbid(unsafe_code)`
11//! - Depends only on `std`.
12//! - Good test coverage (100%)
13//! - Like [`std::io::Read::chain`](https://doc.rust-lang.org/std/io/trait.Read.html#method.chain)
14//!   and [`std::io::Read::take`](https://doc.rust-lang.org/std/io/trait.Read.html#method.take)
15//!   but also passes through writes.
16//! - Useful with `Read + Write` objects like
17//!   [`std::net::TcpStream`](https://doc.rust-lang.org/stable/std/net/struct.TcpStream.html)
18//!   and [`rustls::Stream`](https://docs.rs/rustls/latest/rustls/struct.Stream.html).
19//!
20//! # Changelog
21//! - v1.0.0 - Stable API.
22//! - v0.1.1 - `take_rw` to take `u64` like `std::io::Read::take`.
23//! - v0.1.0 - Initial release.  Moved code from `fixed-buffer`.
24//!
25//! # TO DO
26//!
27//! # Release Process
28//! 1. Edit `Cargo.toml` and bump version number.
29//! 2. Run `../release.sh`
30#![forbid(unsafe_code)]
31
32/// A wrapper for a pair of structs, R and RW.
33///
34/// Implements `std::io::Read`.  Reads from `R` until it is empty, then reads from `RW`.
35///
36/// Implements `std::io::Write`.  Passes all writes through to `RW`.
37///
38/// This is like [`std::io::Chain`](https://doc.rust-lang.org/std/io/struct.Chain.html)
39/// that also passes through writes.
40pub struct ReadWriteChain<R: std::io::Read, RW: std::io::Read + std::io::Write> {
41    reader: Option<R>,
42    read_writer: RW,
43}
44impl<R: std::io::Read, RW: std::io::Read + std::io::Write> ReadWriteChain<R, RW> {
45    /// See [`ReadWriteChain`](struct.ReadWriteChain.html).
46    pub fn new(reader: R, read_writer: RW) -> ReadWriteChain<R, RW> {
47        Self {
48            reader: Some(reader),
49            read_writer,
50        }
51    }
52}
53impl<R: std::io::Read, RW: std::io::Read + std::io::Write> std::io::Read for ReadWriteChain<R, RW> {
54    fn read(&mut self, buf: &mut [u8]) -> Result<usize, std::io::Error> {
55        if let Some(ref mut reader) = self.reader {
56            match reader.read(buf) {
57                Ok(0) => {
58                    // EOF
59                    self.reader = None;
60                }
61                Ok(num_read) => return Ok(num_read),
62                Err(e) => return Err(e),
63            }
64        }
65        self.read_writer.read(buf)
66    }
67}
68impl<R: std::io::Read, RW: std::io::Read + std::io::Write> std::io::Write
69    for ReadWriteChain<R, RW>
70{
71    fn write(&mut self, buf: &[u8]) -> Result<usize, std::io::Error> {
72        self.read_writer.write(buf)
73    }
74
75    fn flush(&mut self) -> Result<(), std::io::Error> {
76        self.read_writer.flush()
77    }
78}
79
80/// Wraps a `std::io::Read + std::io::Write` struct.
81/// Passes through reads and writes to the struct.
82/// Limits the number of bytes that can be read.
83///
84/// This is like [`std::io::Take`](https://doc.rust-lang.org/std/io/struct.Take.html)
85/// that also passes through writes.
86pub struct ReadWriteTake<RW: std::io::Read + std::io::Write> {
87    read_writer: RW,
88    remaining_bytes: u64,
89}
90impl<RW: std::io::Read + std::io::Write> ReadWriteTake<RW> {
91    /// See [`ReadWriteTake`](struct.ReadWriteTake.html).
92    pub fn new(read_writer: RW, len: u64) -> ReadWriteTake<RW> {
93        Self {
94            read_writer,
95            remaining_bytes: len,
96        }
97    }
98}
99impl<RW: std::io::Read + std::io::Write> std::io::Read for ReadWriteTake<RW> {
100    fn read(&mut self, buf: &mut [u8]) -> Result<usize, std::io::Error> {
101        if self.remaining_bytes == 0 {
102            return Ok(0);
103        }
104        let num_to_read =
105            usize::try_from(self.remaining_bytes.min(buf.len() as u64)).unwrap_or(usize::MAX);
106        let dest = &mut buf[0..num_to_read];
107        match self.read_writer.read(dest) {
108            Ok(num_read) => {
109                self.remaining_bytes -= num_read as u64;
110                Ok(num_read)
111            }
112            Err(e) => Err(e),
113        }
114    }
115}
116impl<RW: std::io::Read + std::io::Write> std::io::Write for ReadWriteTake<RW> {
117    fn write(&mut self, buf: &[u8]) -> Result<usize, std::io::Error> {
118        self.read_writer.write(buf)
119    }
120
121    fn flush(&mut self) -> Result<(), std::io::Error> {
122        self.read_writer.flush()
123    }
124}
125
126pub trait ReadWriteExt: std::io::Read + std::io::Write {
127    /// Returns a struct that implements `std::io::Read` and `std::io::Write`.
128    ///
129    /// It reads from `reader` until it is empty, then reads from `self`.
130    ///
131    /// It passes all writes through to `self`.
132    ///
133    /// This is like [`std::io::Read::chain`](https://doc.rust-lang.org/std/io/trait.Read.html#method.chain)
134    /// that also passes through writes.
135    fn chain_after<R: std::io::Read>(&mut self, reader: R) -> ReadWriteChain<R, &mut Self> {
136        ReadWriteChain::new(reader, self)
137    }
138
139    /// Wraps a struct that implements `std::io::Read` and `std::io::Write`.
140    ///
141    /// The returned struct passes through reads and writes to the struct.
142    ///
143    /// It limits the number of bytes that can be read.
144    ///
145    /// This is like [`std::io::Read::take`](https://doc.rust-lang.org/std/io/trait.Read.html#method.take)
146    /// that also passes through writes.
147    fn take_rw(&mut self, len: u64) -> ReadWriteTake<&mut Self> {
148        ReadWriteTake::new(self, len)
149    }
150}
151impl<RW: std::io::Read + std::io::Write + ?Sized> ReadWriteExt for RW {}