Skip to main content

tokio_util/io/
inspect.rs

1use pin_project_lite::pin_project;
2use std::io::{IoSlice, Result};
3use std::pin::Pin;
4use std::task::{ready, Context, Poll};
5
6use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
7
8pin_project! {
9    /// An adapter that lets you inspect the data that's being read.
10    ///
11    /// This is useful for things like hashing data as it's read in.
12    pub struct InspectReader<R, F> {
13        #[pin]
14        reader: R,
15        f: F,
16    }
17}
18
19impl<R, F> InspectReader<R, F> {
20    /// Create a new `InspectReader`, wrapping `reader` and calling `f` for the
21    /// new data supplied by each read call.
22    ///
23    /// The closure will only be called with an empty slice if the inner reader
24    /// returns without reading data into the buffer. This happens at EOF, or if
25    /// `poll_read` is called with a zero-size buffer.
26    pub fn new(reader: R, f: F) -> InspectReader<R, F>
27    where
28        R: AsyncRead,
29        F: FnMut(&[u8]),
30    {
31        InspectReader { reader, f }
32    }
33
34    /// Acquires a reference to the underlying reader.
35    pub fn get_ref(&self) -> &R {
36        &self.reader
37    }
38
39    /// Acquires a mutable reference to the underlying reader.
40    ///
41    /// I/O performed directly on the underlying reader is not inspected.
42    pub fn get_mut(&mut self) -> &mut R {
43        &mut self.reader
44    }
45
46    /// Acquires a pinned mutable reference to the underlying reader.
47    ///
48    /// I/O performed directly on the underlying reader is not inspected.
49    pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut R> {
50        self.project().reader
51    }
52
53    /// Consumes the `InspectReader`, returning the wrapped reader
54    pub fn into_inner(self) -> R {
55        self.reader
56    }
57}
58
59impl<R: AsyncRead, F: FnMut(&[u8])> AsyncRead for InspectReader<R, F> {
60    fn poll_read(
61        self: Pin<&mut Self>,
62        cx: &mut Context<'_>,
63        buf: &mut ReadBuf<'_>,
64    ) -> Poll<Result<()>> {
65        let me = self.project();
66        let filled_length = buf.filled().len();
67        ready!(me.reader.poll_read(cx, buf))?;
68        (me.f)(&buf.filled()[filled_length..]);
69        Poll::Ready(Ok(()))
70    }
71}
72
73impl<R: AsyncWrite, F> AsyncWrite for InspectReader<R, F> {
74    fn poll_write(
75        self: Pin<&mut Self>,
76        cx: &mut Context<'_>,
77        buf: &[u8],
78    ) -> Poll<std::result::Result<usize, std::io::Error>> {
79        self.project().reader.poll_write(cx, buf)
80    }
81
82    fn poll_flush(
83        self: Pin<&mut Self>,
84        cx: &mut Context<'_>,
85    ) -> Poll<std::result::Result<(), std::io::Error>> {
86        self.project().reader.poll_flush(cx)
87    }
88
89    fn poll_shutdown(
90        self: Pin<&mut Self>,
91        cx: &mut Context<'_>,
92    ) -> Poll<std::result::Result<(), std::io::Error>> {
93        self.project().reader.poll_shutdown(cx)
94    }
95
96    fn poll_write_vectored(
97        self: Pin<&mut Self>,
98        cx: &mut Context<'_>,
99        bufs: &[IoSlice<'_>],
100    ) -> Poll<Result<usize>> {
101        self.project().reader.poll_write_vectored(cx, bufs)
102    }
103
104    fn is_write_vectored(&self) -> bool {
105        self.reader.is_write_vectored()
106    }
107}
108
109pin_project! {
110    /// An adapter that lets you inspect the data that's being written.
111    ///
112    /// This is useful for things like hashing data as it's written out.
113    pub struct InspectWriter<W, F> {
114        #[pin]
115        writer: W,
116        f: F,
117    }
118}
119
120impl<W, F> InspectWriter<W, F> {
121    /// Create a new `InspectWriter`, wrapping `write` and calling `f` for the
122    /// data successfully written by each write call.
123    ///
124    /// The closure `f` will never be called with an empty slice. A vectored
125    /// write can result in multiple calls to `f` - at most one call to `f` per
126    /// buffer supplied to `poll_write_vectored`.
127    pub fn new(writer: W, f: F) -> InspectWriter<W, F>
128    where
129        W: AsyncWrite,
130        F: FnMut(&[u8]),
131    {
132        InspectWriter { writer, f }
133    }
134
135    /// Acquires a reference to the underlying writer.
136    pub fn get_ref(&self) -> &W {
137        &self.writer
138    }
139
140    /// Acquires a mutable reference to the underlying writer.
141    ///
142    /// I/O performed directly on the underlying writer is not inspected.
143    pub fn get_mut(&mut self) -> &mut W {
144        &mut self.writer
145    }
146
147    /// Acquires a pinned mutable reference to the underlying writer.
148    ///
149    /// I/O performed directly on the underlying writer is not inspected.
150    pub fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut W> {
151        self.project().writer
152    }
153
154    /// Consumes the `InspectWriter`, returning the wrapped writer
155    pub fn into_inner(self) -> W {
156        self.writer
157    }
158}
159
160impl<W: AsyncWrite, F: FnMut(&[u8])> AsyncWrite for InspectWriter<W, F> {
161    fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll<Result<usize>> {
162        let me = self.project();
163        let res = me.writer.poll_write(cx, buf);
164        if let Poll::Ready(Ok(count)) = res {
165            if count != 0 {
166                (me.f)(&buf[..count]);
167            }
168        }
169        res
170    }
171
172    fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<()>> {
173        let me = self.project();
174        me.writer.poll_flush(cx)
175    }
176
177    fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<()>> {
178        let me = self.project();
179        me.writer.poll_shutdown(cx)
180    }
181
182    fn poll_write_vectored(
183        self: Pin<&mut Self>,
184        cx: &mut Context<'_>,
185        bufs: &[IoSlice<'_>],
186    ) -> Poll<Result<usize>> {
187        let me = self.project();
188        let res = me.writer.poll_write_vectored(cx, bufs);
189        if let Poll::Ready(Ok(mut count)) = res {
190            for buf in bufs {
191                if count == 0 {
192                    break;
193                }
194                let size = count.min(buf.len());
195                if size != 0 {
196                    (me.f)(&buf[..size]);
197                    count -= size;
198                }
199            }
200        }
201        res
202    }
203
204    fn is_write_vectored(&self) -> bool {
205        self.writer.is_write_vectored()
206    }
207}
208
209impl<W: AsyncRead, F> AsyncRead for InspectWriter<W, F> {
210    fn poll_read(
211        self: Pin<&mut Self>,
212        cx: &mut Context<'_>,
213        buf: &mut ReadBuf<'_>,
214    ) -> Poll<std::io::Result<()>> {
215        self.project().writer.poll_read(cx, buf)
216    }
217}