mod view;
use std::{
any::TypeId,
sync::{Arc, RwLock},
};
use crate::{Progress, Step};
pub use view::{ProgressStepView, ProgressView, StepDuration};
#[derive(Clone, Default)]
pub struct DefaultProgress {
steps: Arc<RwLock<InnerProgress>>,
}
impl Progress for DefaultProgress {
fn update(&self, sub_progress: impl Step) {
self.update(sub_progress);
}
}
struct InnerProgress {
steps: Vec<InnerStep>,
durations: Vec<(String, jiff::SignedDuration, jiff::SignedDuration)>,
finished_at: Option<jiff::Timestamp>,
start_time: jiff::Timestamp,
}
struct InnerStep {
type_id: TypeId,
step: Box<dyn Step>,
started_at: jiff::Timestamp,
time_spent_in_children: jiff::SignedDuration,
}
impl Default for InnerProgress {
fn default() -> Self {
Self {
steps: vec![],
durations: vec![],
finished_at: None,
start_time: jiff::Timestamp::now(),
}
}
}
impl DefaultProgress {
pub fn update<P: Step>(&self, sub_progress: P) {
let mut inner = self.steps.write().unwrap();
let InnerProgress {
steps,
durations,
finished_at: _,
start_time: _,
} = &mut *inner;
let now = jiff::Timestamp::now();
let step_type = TypeId::of::<P>();
if let Some(idx) = steps.iter().position(|step| step.type_id == step_type) {
push_steps_durations(steps, durations, now, idx);
steps.truncate(idx);
}
steps.push(InnerStep {
type_id: step_type,
step: Box::new(sub_progress),
started_at: now,
time_spent_in_children: jiff::SignedDuration::ZERO,
});
}
pub fn finish(&self) {
let mut inner = self.steps.write().unwrap();
let InnerProgress {
steps,
durations,
finished_at,
start_time: _,
} = &mut *inner;
if finished_at.is_some() {
return;
}
let now = jiff::Timestamp::now();
*finished_at = Some(now);
push_steps_durations(steps, durations, now, 0);
steps.clear();
}
pub fn is_finished(&self) -> bool {
let inner = self.steps.read().unwrap();
inner.finished_at.is_some()
}
}
fn push_steps_durations(
steps: &[InnerStep],
durations: &mut Vec<(String, jiff::SignedDuration, jiff::SignedDuration)>,
now: jiff::Timestamp,
idx: usize,
) {
let mut father_duration: Option<jiff::SignedDuration> = None;
for (i, step) in steps.iter().skip(idx).enumerate().rev() {
let full_name = steps
.iter()
.take(idx + i + 1)
.map(|step| step.step.name())
.collect::<Vec<_>>()
.join(" > ");
let total_duration = now.duration_since(step.started_at);
let self_duration = match father_duration {
Some(father) => total_duration - father,
None => total_duration,
};
durations.push((full_name, total_duration, self_duration));
father_duration = Some(total_duration);
}
}