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
10
#[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
2
    pub fn new(r: R, w: W) -> Self {
59
2
        JoinReadWrite { r, w }
60
2
    }
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
6
    fn poll_read(
71
6
        self: Pin<&mut Self>,
72
6
        c: &mut Context,
73
6
        out: &mut [u8],
74
6
    ) -> Poll<Result<usize, Error>> {
75
6
        self.project().r.poll_read(c, out)
76
6
    }
77
}
78

            
79
impl<R: AsyncRead, W: AsyncWrite> AsyncWrite for JoinReadWrite<R, W> {
80
2
    fn poll_write(
81
2
        self: Pin<&mut Self>,
82
2
        c: &mut Context,
83
2
        data: &[u8],
84
2
    ) -> Poll<Result<usize, Error>> {
85
2
        self.project().w.poll_write(c, data)
86
2
    }
87

            
88
2
    fn poll_flush(self: Pin<&mut Self>, c: &mut Context) -> Poll<Result<(), Error>> {
89
2
        self.project().w.poll_flush(c)
90
2
    }
91

            
92
    fn poll_close(self: Pin<&mut Self>, c: &mut Context) -> Poll<Result<(), Error>> {
93
        self.project().w.poll_close(c)
94
    }
95
}