async_copy_progress/
lib.rs

1//! [![github]](https://github.com/mkroening/async-copy-progress) [![crates-io]](https://crates.io/crates/async-copy-progress) [![docs-rs]](https://docs.rs/async-copy-progress)
2//!
3//! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github
4//! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust
5//! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logoColor=white&logo=data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoIGZpbGw9IiNmNWY1ZjUiIGQ9Ik00ODguNiAyNTAuMkwzOTIgMjE0VjEwNS41YzAtMTUtOS4zLTI4LjQtMjMuNC0zMy43bC0xMDAtMzcuNWMtOC4xLTMuMS0xNy4xLTMuMS0yNS4zIDBsLTEwMCAzNy41Yy0xNC4xIDUuMy0yMy40IDE4LjctMjMuNCAzMy43VjIxNGwtOTYuNiAzNi4yQzkuMyAyNTUuNSAwIDI2OC45IDAgMjgzLjlWMzk0YzAgMTMuNiA3LjcgMjYuMSAxOS45IDMyLjJsMTAwIDUwYzEwLjEgNS4xIDIyLjEgNS4xIDMyLjIgMGwxMDMuOS01MiAxMDMuOSA1MmMxMC4xIDUuMSAyMi4xIDUuMSAzMi4yIDBsMTAwLTUwYzEyLjItNi4xIDE5LjktMTguNiAxOS45LTMyLjJWMjgzLjljMC0xNS05LjMtMjguNC0yMy40LTMzLjd6TTM1OCAyMTQuOGwtODUgMzEuOXYtNjguMmw4NS0zN3Y3My4zek0xNTQgMTA0LjFsMTAyLTM4LjIgMTAyIDM4LjJ2LjZsLTEwMiA0MS40LTEwMi00MS40di0uNnptODQgMjkxLjFsLTg1IDQyLjV2LTc5LjFsODUtMzguOHY3NS40em0wLTExMmwtMTAyIDQxLjQtMTAyLTQxLjR2LS42bDEwMi0zOC4yIDEwMiAzOC4ydi42em0yNDAgMTEybC04NSA0Mi41di03OS4xbDg1LTM4Ljh2NzUuNHptMC0xMTJsLTEwMiA0MS40LTEwMi00MS40di0uNmwxMDItMzguMiAxMDIgMzguMnYuNnoiPjwvcGF0aD48L3N2Zz4K
6//!
7//! <br>
8//!
9//! Asynchronous copying with progress callbacks.
10//!
11//! See [`copy`] for details.
12//!
13//! <br>
14//!
15//! # Example
16//!
17//! ```
18//! # spin_on::spin_on(async {
19//! # use std::sync::atomic::{AtomicU64, Ordering};
20//! let mut reader: &[u8] = b"hello";
21//! let mut writer: Vec<u8> = vec![];
22//!
23//! let progress = AtomicU64::new(0);
24//! let report_progress = |amt| progress.store(amt, Ordering::Relaxed);
25//!
26//! async_copy_progress::copy(&mut reader, &mut writer, report_progress).await?;
27//!
28//! assert_eq!(&b"hello"[..], &writer[..]);
29//! assert_eq!(5, progress.load(Ordering::Relaxed));
30//! # std::io::Result::Ok(()) });
31//! ```
32
33#![deny(rust_2018_idioms)]
34
35use std::{
36    future::Future,
37    io,
38    pin::Pin,
39    task::{Context, Poll},
40};
41
42use futures_core::ready;
43use futures_io::{AsyncBufRead, AsyncWrite};
44use pin_project_lite::pin_project;
45
46/// Creates a future which copies all the bytes from one object to another while
47/// reporting the progress.
48///
49/// The returned future will copy all the bytes read from `reader` into the
50/// `writer` specified. After each write, `report_progress` is called with the
51/// current amount of copied bytes. This future will only complete once the
52/// `reader` has hit EOF and all bytes have been written to and flushed from the
53/// `writer` provided.
54///
55/// On success the number of bytes is returned.
56///
57/// <br>
58///
59/// # Errors
60///
61/// This function will return an error immediately if any call to
62/// [`poll_fill_buf`], [`poll_write`] or [`poll_flush`] returns an error.
63///
64/// [`poll_fill_buf`]: AsyncBufRead::poll_fill_buf
65/// [`poll_write`]: AsyncWrite::poll_write
66/// [`poll_flush`]: AsyncWrite::poll_flush
67///
68/// <br>
69///
70/// # Example
71///
72/// ```
73/// # spin_on::spin_on(async {
74/// # use std::sync::atomic::{AtomicU64, Ordering};
75/// let mut reader: &[u8] = b"hello";
76/// let mut writer: Vec<u8> = vec![];
77///
78/// let progress = AtomicU64::new(0);
79/// let report_progress = |amt| progress.store(amt, Ordering::Relaxed);
80///
81/// async_copy_progress::copy(&mut reader, &mut writer, report_progress).await?;
82///
83/// assert_eq!(&b"hello"[..], &writer[..]);
84/// assert_eq!(5, progress.load(Ordering::Relaxed));
85/// # std::io::Result::Ok(()) });
86/// ```
87pub fn copy<R, W, F>(reader: R, writer: W, report_progress: F) -> Copy<R, W, F>
88where
89    R: AsyncBufRead,
90    W: AsyncWrite,
91    F: FnMut(u64),
92{
93    Copy {
94        reader,
95        writer,
96        amt: 0,
97        report_progress,
98    }
99}
100
101pin_project! {
102    /// Future for the [`copy`] function.
103    #[derive(Debug)]
104    #[must_use = "futures do nothing unless you `.await` or poll them"]
105    pub struct Copy<R, W, F> {
106        #[pin]
107        reader: R,
108        #[pin]
109        writer: W,
110        amt: u64,
111        report_progress: F,
112    }
113}
114
115impl<R, W, F> Future for Copy<R, W, F>
116where
117    R: AsyncBufRead,
118    W: AsyncWrite,
119    F: FnMut(u64),
120{
121    type Output = io::Result<u64>;
122
123    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
124        let mut this = self.project();
125        loop {
126            let buffer = ready!(this.reader.as_mut().poll_fill_buf(cx))?;
127            if buffer.is_empty() {
128                ready!(this.writer.poll_flush(cx))?;
129                return Poll::Ready(Ok(*this.amt));
130            }
131
132            let i = ready!(this.writer.as_mut().poll_write(cx, buffer))?;
133            if i == 0 {
134                return Poll::Ready(Err(io::ErrorKind::WriteZero.into()));
135            }
136            *this.amt += i as u64;
137            this.reader.as_mut().consume(i);
138            (this.report_progress)(*this.amt);
139        }
140    }
141}