2

tokio::io::copy doesn't provide progress update callbacks. When copy takes much time to complete, it is good to report progress of the copy operation. Is there a way to get status/progress when copy happens?

kmdreko
  • 42,554
  • 6
  • 57
  • 106
Vjy
  • 2,106
  • 3
  • 22
  • 34

1 Answers1

0

The tokio::io::copy function simply works off the AsyncRead and AsyncWrite traits. I'm not sure if there's anything pre-built, but its not too bad to write your own adapter to display progress:

use std::io::Result;
use std::pin::Pin;
use std::task::{Context, Poll};
use std::time::Duration;

use pin_project::pin_project;
use tokio::io::{AsyncRead, ReadBuf};
use tokio::time::{interval, Interval};

#[pin_project]
pub struct ProgressReadAdapter<R: AsyncRead> {
    #[pin]
    inner: R,
    interval: Interval,
    interval_bytes: usize,
}

impl<R: AsyncRead> ProgressReadAdapter<R> {
    pub fn new(inner: R) -> Self {
        Self {
            inner,
            interval: interval(Duration::from_millis(100)),
            interval_bytes: 0,
        }
    }
}

impl<R: AsyncRead> AsyncRead for ProgressReadAdapter<R> {
    fn poll_read(
        self: Pin<&mut Self>,
        cx: &mut Context<'_>,
        buf: &mut ReadBuf<'_>,
    ) -> Poll<Result<()>> {
        let this = self.project();

        let before = buf.filled().len();
        let result = this.inner.poll_read(cx, buf);
        let after = buf.filled().len();

        *this.interval_bytes += after - before;
        match this.interval.poll_tick(cx) {
            Poll::Pending => {}
            Poll::Ready(_) => {
                println!("reading at {} bytes per second", *this.interval_bytes * 10);
                *this.interval_bytes = 0;
            }
        };

        result
    }
}

Which can then be used like this:

use tokio::fs::File;

#[tokio::main]
async fn main() -> Result<()> {
    let mut file1 = File::open("foo.bin").await?;
    let mut file2 = File::create("bar.bin").await?;

    let mut file1 = ProgressReadAdapter::new(file1);

    tokio::io::copy(&mut file1, &mut file2).await?;

    Ok(())
}
reading at 38338560 bytes per second
reading at 315146240 bytes per second
reading at 422625280 bytes per second
reading at 497827840 bytes per second
reading at 428605440 bytes per second

You could probably make this look nicer by hooking it up to indicatif or at least make it more configurable, but I'll leave that as an exercise to the reader.

kmdreko
  • 42,554
  • 6
  • 57
  • 106