1
#![cfg_attr(docsrs, feature(doc_cfg))]
2
#![doc = include_str!("../README.md")]
3
// @@ begin lint list maintained by maint/add_warning @@
4
#![allow(renamed_and_removed_lints)] // @@REMOVE_WHEN(ci_arti_stable)
5
#![allow(unknown_lints)] // @@REMOVE_WHEN(ci_arti_nightly)
6
#![warn(missing_docs)]
7
#![warn(noop_method_call)]
8
#![warn(unreachable_pub)]
9
#![warn(clippy::all)]
10
#![deny(clippy::await_holding_lock)]
11
#![deny(clippy::cargo_common_metadata)]
12
#![deny(clippy::cast_lossless)]
13
#![deny(clippy::checked_conversions)]
14
#![allow(clippy::cognitive_complexity)] // See arti#2556
15
#![deny(clippy::debug_assert_with_mut_call)]
16
#![deny(clippy::exhaustive_enums)]
17
#![deny(clippy::exhaustive_structs)]
18
#![deny(clippy::expl_impl_clone_on_copy)]
19
#![deny(clippy::fallible_impl_from)]
20
#![deny(clippy::implicit_clone)]
21
#![deny(clippy::large_stack_arrays)]
22
#![warn(clippy::manual_ok_or)]
23
#![deny(clippy::missing_docs_in_private_items)]
24
#![warn(clippy::needless_borrow)]
25
#![warn(clippy::needless_pass_by_value)]
26
#![warn(clippy::option_option)]
27
#![warn(clippy::or_fun_call)]
28
#![deny(clippy::print_stderr)]
29
#![deny(clippy::print_stdout)]
30
#![warn(clippy::rc_buffer)]
31
#![deny(clippy::ref_option_ref)]
32
#![warn(clippy::semicolon_if_nothing_returned)]
33
#![warn(clippy::trait_duplication_in_bounds)]
34
#![deny(clippy::unchecked_time_subtraction)]
35
#![deny(clippy::unnecessary_wraps)]
36
#![warn(clippy::unseparated_literal_suffix)]
37
#![deny(clippy::unwrap_used)]
38
#![deny(clippy::mod_module_files)]
39
#![allow(clippy::let_unit_value)] // This can reasonably be done for explicitness
40
#![allow(clippy::uninlined_format_args)]
41
#![allow(clippy::significant_drop_in_scrutinee)] // arti/-/merge_requests/588/#note_2812945
42
#![allow(clippy::result_large_err)] // temporary workaround for arti#587
43
#![allow(clippy::needless_raw_string_hashes)] // complained-about code is fine, often best
44
#![allow(clippy::needless_lifetimes)] // See arti#1765
45
#![allow(mismatched_lifetime_syntaxes)] // temporary workaround for arti#2060
46
#![allow(clippy::collapsible_if)] // See arti#2342
47
#![deny(clippy::unused_async)]
48
#![deny(clippy::string_slice)] // See arti#2571
49
#![allow(recursion_depth_exceeding_limit)] // arti#2715, rust/issues/159228
50
//! <!-- @@ end lint list maintained by maint/add_warning @@ -->
51

            
52
mod arc_io_result;
53
mod copy_buf;
54
mod copy_buf_bidi;
55
pub mod eof;
56
mod fuse_buf_reader;
57
use std::{
58
    future::Future,
59
    pin::Pin,
60
    task::{Context, Poll},
61
};
62

            
63
pub use copy_buf::{CopyBuf, copy_buf};
64
pub use copy_buf_bidi::{CopyBufBidirectional, copy_buf_bidirectional};
65
pub use eof::EofStrategy;
66

            
67
use futures::{AsyncRead, AsyncWrite, io::BufReader};
68
use pin_project::pin_project;
69

            
70
/// Return a future to copy bytes from `reader` to `writer`.
71
///
72
/// See [`copy_buf()`] for full details.
73
///
74
/// Unlike `copy_buf`, this function does not require that `reader` implements AsyncBufRead:
75
/// it wraps `reader` internally in a new `BufReader` with default capacity.
76
///
77
/// ## Limitations
78
///
79
/// If an error occurs during transmission, buffered data that was read from `reader`
80
/// but not written to `writer` will be lost.
81
/// To avoid this, use [`copy_buf()`].
82
///
83
/// Similarly, if you drop this future while it is still pending,
84
/// any buffered data will be lost.
85
///
86
/// See the crate-level documentation for further
87
/// [discussion of this function's limitations](crate#Limitations).
88
pub fn copy<R, W>(reader: R, writer: W) -> Copy<R, W>
89
where
90
    R: AsyncRead,
91
    W: AsyncWrite,
92
{
93
    let reader = BufReader::new(reader);
94
    Copy(copy_buf(reader, writer))
95
}
96

            
97
/// Return a future to copies bytes from `stream_a` to `stream_b`,
98
/// and from `stream_b` to `stream_a`.
99
///
100
/// See [`copy_buf_bidirectional()`] for full details.
101
///
102
/// Unlike `copy_buf_bidirectional`, this function does not require that either stream implements AsyncBufRead:
103
/// it wraps them internally in a new `BufReader` with default capacity.
104
///
105
/// ## Limitations
106
///
107
/// If an error occurs during transmission, data that was read from one stream,
108
/// but not written to the other, will be lost.
109
/// To avoid this, use [`copy_buf_bidirectional()`].
110
///
111
/// Similarly, if you drop this future while it is still pending,
112
/// any buffered data will be lost.
113
///
114
/// See the crate-level documentation for further
115
/// [discussion of this function's limitations](crate#Limitations).
116
pub fn copy_bidirectional<A, B, AE, BE>(
117
    stream_a: A,
118
    stream_b: B,
119
    on_a_eof: AE,
120
    on_b_eof: BE,
121
) -> CopyBidirectional<A, B, AE, BE>
122
where
123
    A: AsyncRead + AsyncWrite,
124
    B: AsyncRead + AsyncWrite,
125
    AE: EofStrategy<B>,
126
    BE: EofStrategy<A>,
127
{
128
    let stream_a = BufReader::new(stream_a);
129
    let stream_b = BufReader::new(stream_b);
130
    CopyBidirectional(copy_buf_bidirectional(
131
        stream_a,
132
        stream_b,
133
        eof::BufReaderEofWrapper(on_a_eof),
134
        eof::BufReaderEofWrapper(on_b_eof),
135
    ))
136
}
137

            
138
/// A future returned by [`copy`].
139
#[derive(Debug)]
140
#[pin_project]
141
#[must_use = "futures do nothing unless you `.await` or poll them"]
142
pub struct Copy<R, W>(#[pin] CopyBuf<BufReader<R>, W>);
143

            
144
/// A future returned by [`copy_bidirectional`].
145
#[derive(Debug)]
146
#[pin_project]
147
#[must_use = "futures do nothing unless you `.await` or poll them"]
148
pub struct CopyBidirectional<A, B, AE, BE>(
149
    #[pin]
150
    CopyBufBidirectional<
151
        BufReader<A>,
152
        BufReader<B>,
153
        eof::BufReaderEofWrapper<AE>,
154
        eof::BufReaderEofWrapper<BE>,
155
    >,
156
);
157

            
158
// Note: There is intentionally no `into_inner` implementation for these types,
159
// since returning the original streams would discard any buffered data.
160

            
161
impl<R, W> Future for Copy<R, W>
162
where
163
    R: AsyncRead,
164
    W: AsyncWrite,
165
{
166
    type Output = std::io::Result<u64>;
167

            
168
    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
169
        self.project().0.poll(cx)
170
    }
171
}
172

            
173
impl<A, B, AE, BE> Future for CopyBidirectional<A, B, AE, BE>
174
where
175
    A: AsyncRead + AsyncWrite,
176
    B: AsyncRead + AsyncWrite,
177
    AE: EofStrategy<B>,
178
    BE: EofStrategy<A>,
179
{
180
    type Output = std::io::Result<(u64, u64)>;
181

            
182
    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
183
        self.project().0.poll(cx)
184
    }
185
}
186

            
187
#[cfg(test)]
188
mod test {
189
    // @@ begin test lint list maintained by maint/add_warning @@
190
    #![allow(clippy::bool_assert_comparison)]
191
    #![allow(clippy::clone_on_copy)]
192
    #![allow(clippy::dbg_macro)]
193
    #![allow(clippy::mixed_attributes_style)]
194
    #![allow(clippy::print_stderr)]
195
    #![allow(clippy::print_stdout)]
196
    #![allow(clippy::single_char_pattern)]
197
    #![allow(clippy::unwrap_used)]
198
    #![allow(clippy::unchecked_time_subtraction)]
199
    #![allow(clippy::useless_vec)]
200
    #![allow(clippy::needless_pass_by_value)]
201
    #![allow(clippy::string_slice)] // See arti#2571
202
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
203

            
204
    use super::*;
205
    use std::io;
206

            
207
    /// A struct that implements AsyncRead and AsyncWrite, but always returns an error.
208
    #[derive(Debug, Clone)]
209
    pub(crate) struct ErrorRW(pub(crate) io::ErrorKind);
210

            
211
    impl AsyncRead for ErrorRW {
212
        fn poll_read(
213
            self: Pin<&mut Self>,
214
            _cx: &mut Context<'_>,
215
            _buf: &mut [u8],
216
        ) -> Poll<io::Result<usize>> {
217
            Poll::Ready(Err(io::Error::from(self.0)))
218
        }
219
    }
220

            
221
    impl AsyncWrite for ErrorRW {
222
        fn poll_write(
223
            self: Pin<&mut Self>,
224
            _cx: &mut Context<'_>,
225
            _buf: &[u8],
226
        ) -> Poll<io::Result<usize>> {
227
            Poll::Ready(Err(io::Error::from(self.0)))
228
        }
229

            
230
        fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
231
            Poll::Ready(Err(io::Error::from(self.0)))
232
        }
233

            
234
        fn poll_close(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
235
            Poll::Ready(Err(io::Error::from(self.0)))
236
        }
237
    }
238

            
239
    /// A struct that implements AsyncRead, but never returns any data.
240
    ///
241
    /// (This reader is always _pending_.)
242
    pub(crate) struct PausedRead;
243

            
244
    impl AsyncRead for PausedRead {
245
        fn poll_read(
246
            self: Pin<&mut Self>,
247
            _cx: &mut Context<'_>,
248
            _buf: &mut [u8],
249
        ) -> Poll<io::Result<usize>> {
250
            Poll::Pending
251
        }
252
    }
253

            
254
    /// A read-write pair, stapled into a Read+Write stream.
255
    #[pin_project]
256
    pub(crate) struct RWPair<R, W>(#[pin] pub(crate) R, #[pin] pub(crate) W);
257

            
258
    impl<R: AsyncRead, W> AsyncRead for RWPair<R, W> {
259
        fn poll_read(
260
            self: Pin<&mut Self>,
261
            cx: &mut Context<'_>,
262
            buf: &mut [u8],
263
        ) -> Poll<io::Result<usize>> {
264
            self.project().0.poll_read(cx, buf)
265
        }
266
    }
267

            
268
    impl<R, W: AsyncWrite> AsyncWrite for RWPair<R, W> {
269
        fn poll_write(
270
            self: Pin<&mut Self>,
271
            cx: &mut Context<'_>,
272
            buf: &[u8],
273
        ) -> Poll<io::Result<usize>> {
274
            self.project().1.poll_write(cx, buf)
275
        }
276

            
277
        fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
278
            self.project().1.poll_flush(cx)
279
        }
280

            
281
        fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
282
            self.project().1.poll_close(cx)
283
        }
284
    }
285
}