tor_async_utils/join_read_write.rs
1//! Join a readable and writeable into a single `AsyncRead` + `AsyncWrite`
2
3use std::io::Error;
4use std::pin::Pin;
5use std::task::{Context, Poll};
6
7use futures::{AsyncRead, AsyncWrite};
8use 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]
47pub struct JoinReadWrite<R: AsyncRead, W: AsyncWrite> {
48 /// readable
49 #[pin]
50 r: R,
51 /// writeable
52 #[pin]
53 w: W,
54}
55
56impl<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
69impl<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
79impl<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}