use std::{
io::Write,
sync::{
Arc,
Mutex,
},
time::Duration,
};
use super::{
ProgressReporter,
progress_format::{
format_duration,
progress_percent,
},
};
#[derive(Debug)]
pub struct WriterProgressReporter<W> {
writer: Arc<Mutex<W>>,
}
impl<W> WriterProgressReporter<W> {
#[inline]
pub fn new(writer: Arc<Mutex<W>>) -> Self {
Self { writer }
}
#[inline]
pub fn from_writer(writer: W) -> Self {
Self::new(Arc::new(Mutex::new(writer)))
}
#[inline]
pub fn writer(&self) -> &Arc<Mutex<W>> {
&self.writer
}
}
impl<W> ProgressReporter for WriterProgressReporter<W>
where
W: Write + Send,
{
fn start(&self, total_count: usize) {
self.write_line(format_args!("Starting {total_count} tasks..."));
}
fn process(
&self,
total_count: usize,
active_count: usize,
completed_count: usize,
elapsed: Duration,
) {
self.write_line(format_args!(""));
self.write_line(format_args!(
"--------------------------------------------------"
));
self.write_line(format_args!("Waiting for all batch tasks to finish..."));
self.write_line(format_args!("Total tasks: {total_count}"));
self.write_line(format_args!("Current active tasks: {active_count}"));
self.write_line(format_args!("Current completed tasks: {completed_count}"));
self.write_line(format_args!(
"Current tasks in queue: {}",
total_count.saturating_sub(completed_count + active_count)
));
self.write_line(format_args!(
"Progress: {:.2}%",
progress_percent(completed_count, total_count)
));
self.write_process_speed(
completed_count,
total_count.saturating_sub(completed_count),
elapsed,
);
}
fn finish(&self, total_count: usize, elapsed: Duration) {
self.write_line(format_args!("All {total_count} tasks are finished."));
self.write_line(format_args!(
"Processed {total_count} tasks in {}.",
format_duration(elapsed)
));
}
}
impl<W> WriterProgressReporter<W>
where
W: Write,
{
fn write_line(&self, args: std::fmt::Arguments<'_>) {
let mut writer = self
.writer
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
writeln!(writer, "{args}").expect("progress reporter should write one line");
}
fn write_process_speed(
&self,
completed_count: usize,
remaining_count: usize,
elapsed: Duration,
) {
if completed_count == 0 {
self.write_line(format_args!("No task processed."));
return;
}
let elapsed_ms = elapsed.as_secs_f64() * 1000.0;
let speed = elapsed_ms / completed_count as f64;
let tasks_per_minute =
completed_count as f64 * 60.0 * 1000.0 / elapsed_ms.max(f64::EPSILON);
self.write_line(format_args!(
"Average speed: {speed:.2} ms/task, i.e., {tasks_per_minute:.2} tasks/min"
));
let remaining = Duration::from_secs_f64(remaining_count as f64 * speed / 1000.0);
self.write_line(format_args!(
"Estimated remaining time: {}",
format_duration(remaining)
));
}
}