use std::path::PathBuf;
use tokio::sync::mpsc;
use crate::profiler::Entry;
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum Progress {
Planned {
directories: usize,
small_files: usize,
small_bytes: u64,
large_files: usize,
large_bytes: u64,
small_file_threshold: u64,
},
Started {
bytes_total: Option<u64>,
entries_total: usize,
},
EntryStarted {
entry: Entry,
},
EntryProgress {
entry: Entry,
bytes_copied: u64,
},
EntryCompleted {
entry: Entry,
},
EntryFailed {
entry: Entry,
},
DirectoriesStarted {
total: usize,
},
DirectoryCompleted {
path: PathBuf,
},
DirectoryFailed {
path: PathBuf,
},
}
#[derive(Clone)]
pub(crate) struct ProgressReporter {
tx: mpsc::UnboundedSender<Progress>,
}
impl ProgressReporter {
pub(crate) fn new(tx: mpsc::UnboundedSender<Progress>) -> Self {
Self { tx }
}
#[cfg(test)]
pub(crate) fn noop() -> Self {
let (tx, _rx) = mpsc::unbounded_channel();
Self { tx }
}
pub(crate) fn send(&self, progress: Progress) {
let _ = self.tx.send(progress);
}
}
#[cfg(test)]
mod tests {
use std::path::PathBuf;
use super::*;
fn entry(name: &str) -> Entry {
Entry {
path: PathBuf::from(name),
relative_path: PathBuf::from(name),
size: 1,
modified: None,
}
}
#[test]
fn send_after_receiver_dropped_does_not_panic() {
let (tx, rx) = mpsc::unbounded_channel();
let reporter = ProgressReporter::new(tx);
drop(rx);
reporter.send(Progress::EntryStarted { entry: entry("a") });
}
#[tokio::test]
async fn sends_are_received_in_order() {
let (tx, mut rx) = mpsc::unbounded_channel();
let reporter = ProgressReporter::new(tx);
reporter.send(Progress::Started {
bytes_total: Some(10),
entries_total: 2,
});
reporter.send(Progress::EntryStarted { entry: entry("a") });
reporter.send(Progress::EntryCompleted { entry: entry("a") });
drop(reporter);
assert!(matches!(rx.recv().await, Some(Progress::Started { .. })));
assert!(matches!(
rx.recv().await,
Some(Progress::EntryStarted { .. })
));
assert!(matches!(
rx.recv().await,
Some(Progress::EntryCompleted { .. })
));
assert!(rx.recv().await.is_none());
}
}