/ crates / tor-async-utils / src / join_read_write.rs
join_read_write.rs
 1  //! Join a readable and writeable into a single `AsyncRead` + `AsyncWrite`
 2  
 3  use std::io::Error;
 4  use std::pin::Pin;
 5  use std::task::{Context, Poll};
 6  
 7  use futures::{AsyncRead, AsyncWrite};
 8  use pin_project::pin_project;
 9  
10  /// Async readable/writeable that dispatches reads to `R` and writes to `W`
11  ///
12  /// `AsyncRead` is forwarded to `R`.
13  /// `AsyncWrite` is forwarded to `W`.
14  ///
15  /// [`JoinReadWrite::new()`] is the converse of
16  /// [`AsyncReadExt::split()`](futures::AsyncReadExt::split).
17  /// But, if `R` and `W` came from splitting a single `AsyncRead + AsyncWrite`,
18  /// you probably want the `reunite` or `unsplit` method, instead of `JoinReadWrite`.
19  ///
20  /// Does *not* implement any kind of flushing behaviour when switching between reading and writing.
21  ///
22  /// # Example
23  ///
24  /// ```
25  /// # #[tokio::main]
26  /// # async fn main() {
27  /// use tor_async_utils::JoinReadWrite;
28  /// use futures::{AsyncReadExt as _, AsyncWriteExt as _};
29  ///
30  /// let read = b"hello\n";
31  /// let mut read = &read[..];
32  /// let mut write = Vec::<u8>::new();
33  ///
34  /// let mut joined = JoinReadWrite::new(read, write);
35  ///
36  /// let mut got = String::new();
37  /// let _: usize = joined.read_to_string(&mut got).await.unwrap();
38  /// assert_eq!(got, "hello\n");
39  ///
40  /// let () = joined.write_all(b"some data").await.unwrap();
41  ///
42  /// let (r, w) = joined.into_parts();
43  /// assert_eq!(w, b"some data");
44  /// # }
45  /// ```
46  #[pin_project]
47  pub struct JoinReadWrite<R: AsyncRead, W: AsyncWrite> {
48      /// readable
49      #[pin]
50      r: R,
51      /// writeable
52      #[pin]
53      w: W,
54  }
55  
56  impl<R: AsyncRead, W: AsyncWrite> JoinReadWrite<R, W> {
57      /// Join an `AsyncRead` and an `AsyncWrite` into a single `impl AsyncRead + AsyncWrite`
58      pub fn new(r: R, w: W) -> Self {
59          JoinReadWrite { r, w }
60      }
61  
62      /// Dismantle a `JoinReadWrite` into its constituent `AsyncRead` and `AsyncWrite`
63      pub fn into_parts(self) -> (R, W) {
64          let JoinReadWrite { r, w } = self;
65          (r, w)
66      }
67  }
68  
69  impl<R: AsyncRead, W: AsyncWrite> AsyncRead for JoinReadWrite<R, W> {
70      fn poll_read(
71          self: Pin<&mut Self>,
72          c: &mut Context,
73          out: &mut [u8],
74      ) -> Poll<Result<usize, Error>> {
75          self.project().r.poll_read(c, out)
76      }
77  }
78  
79  impl<R: AsyncRead, W: AsyncWrite> AsyncWrite for JoinReadWrite<R, W> {
80      fn poll_write(
81          self: Pin<&mut Self>,
82          c: &mut Context,
83          data: &[u8],
84      ) -> Poll<Result<usize, Error>> {
85          self.project().w.poll_write(c, data)
86      }
87  
88      fn poll_flush(self: Pin<&mut Self>, c: &mut Context) -> Poll<Result<(), Error>> {
89          self.project().w.poll_flush(c)
90      }
91  
92      fn poll_close(self: Pin<&mut Self>, c: &mut Context) -> Poll<Result<(), Error>> {
93          self.project().w.poll_close(c)
94      }
95  }