use std::{
io::Write,
sync::{
Arc,
Mutex,
},
};
use super::format::format_duration;
use crate::{
model::ProgressEvent,
reporter::ProgressReporter,
};
#[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 const fn writer(&self) -> &Arc<Mutex<W>> {
&self.writer
}
}
impl<W> ProgressReporter for WriterProgressReporter<W>
where
W: Write + Send,
{
fn report(&self, event: &ProgressEvent) {
let mut writer = self
.writer
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
writeln!(writer, "{}", format_event(event)).expect("progress reporter should write event");
}
}
fn format_event(event: &ProgressEvent) -> String {
let counters = event.counters();
let progress = match (counters.completed_count(), counters.total_count()) {
(completed, Some(total)) => format!(
"{completed}/{total} ({:.2}%)",
counters.progress_percent().unwrap_or(100.0)
),
(completed, None) => format!("{completed} completed"),
};
let active = counters.active_count();
let failed = counters.failed_count();
let elapsed = format_duration(event.elapsed());
match event.stage() {
Some(stage) => format!(
"{} [{}] {progress}, active {active}, failed {failed}, elapsed {elapsed}",
event.phase(),
stage.name(),
),
None => format!(
"{} {progress}, active {active}, failed {failed}, elapsed {elapsed}",
event.phase(),
),
}
}