pub trait ProgressDisplay {
fn increment(&mut self, delta: u64);
fn set_completion_value(&mut self, position: u64);
fn finish(&mut self);
fn start(self, completion_value: u64) -> ProgressTask<Self>
where
Self: Sized,
{
ProgressTask::new(self, completion_value)
}
}
pub struct ProgressTask<P: ProgressDisplay> {
progress: P,
}
impl<P: ProgressDisplay> Drop for ProgressTask<P> {
fn drop(&mut self) {
self.progress.finish();
}
}
impl<P: ProgressDisplay> ProgressTask<P> {
pub fn new(mut progress: P, completion_value: u64) -> Self {
progress.set_completion_value(completion_value);
Self { progress }
}
pub fn increment(&mut self, delta: u64) {
self.progress.increment(delta);
}
pub(super) fn wrap_reader<R>(&mut self, reader: R) -> ProgressReader<R, &mut Self> {
ProgressReader {
reader,
progress_task: self,
}
}
}
pub(super) struct ProgressReader<R, P> {
reader: R,
progress_task: P,
}
impl<R, P> ProgressReader<R, P> {
pub(super) fn new(reader: R, progress_task: P) -> Self {
Self {
reader,
progress_task,
}
}
}
impl<R: std::io::Read, P: IncrementProgress> std::io::Read for ProgressReader<R, P> {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
let read = self.reader.read(buf)?;
self.progress_task
.increment(read.try_into().map_err(std::io::Error::other)?);
Ok(read)
}
}
trait IncrementProgress {
fn increment(&mut self, delta: u64);
}
impl<P: ProgressDisplay> IncrementProgress for ProgressTask<P> {
fn increment(&mut self, delta: u64) {
self.progress.increment(delta);
}
}
impl<P: ProgressDisplay> IncrementProgress for &mut ProgressTask<P> {
fn increment(&mut self, delta: u64) {
self.progress.increment(delta);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Debug, Default)]
struct MockProgress {
pos: u64,
completion_value: Option<u64>,
has_completed: bool,
increment_after_completion: bool,
}
impl MockProgress {
fn new() -> Self {
Default::default()
}
}
impl ProgressDisplay for &mut MockProgress {
fn set_completion_value(&mut self, value: u64) {
self.completion_value = Some(value);
}
fn increment(&mut self, delta: u64) {
self.pos += delta;
if self.has_completed {
self.increment_after_completion = true;
}
}
fn finish(&mut self) {
self.has_completed = true;
}
}
#[derive(Debug, Default)]
struct MockReader {
buf: Vec<u8>,
chunk_size: usize,
buf_pos: usize,
}
impl MockReader {
fn new(buf: Vec<u8>) -> Self {
Self {
buf,
..Default::default()
}
}
}
impl std::io::Read for MockReader {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
let end = usize::max(self.buf_pos + self.chunk_size, self.buf.len());
let chunk = &self.buf[self.buf_pos..end];
buf[..end - self.buf_pos].clone_from_slice(chunk);
self.buf_pos += chunk.len();
Ok(chunk.len())
}
}
#[test]
fn test_progress_tasks_completes_display() {
let mut progress = MockProgress::new();
{
let mut task = (&mut progress).start(10);
task.increment(1);
}
assert!(!progress.increment_after_completion);
(&mut progress).increment(1);
assert!(progress.increment_after_completion);
}
#[test]
fn test_no_increment_after_completion_check_works() {
let mut progress = MockProgress::new();
{
let task = (&mut progress).start(10);
let reader = MockReader::new(vec![0; 10]);
let mut progress_reader = ProgressReader::new(reader, task);
let mut buffer = Vec::new();
use std::io::Read;
progress_reader.read_to_end(&mut buffer).unwrap();
}
assert!(progress.has_completed);
assert_eq!(progress.completion_value, Some(10));
}
#[test]
fn test_progress_task_does_not_complete_during_move() {
let mut progress = MockProgress::new();
{
let task = (&mut progress).start(10);
let reader = MockReader::new(vec![0; 9]);
let mut progress_reader = ProgressReader::new(reader, task);
let mut buffer = Vec::new();
use std::io::Read;
progress_reader.read_to_end(&mut buffer).unwrap();
}
assert!(!progress.increment_after_completion);
assert!(progress.has_completed);
assert_eq!(progress.completion_value, Some(10));
assert_eq!(progress.pos, 9);
}
}