use std::time::{
Duration,
Instant,
};
use crate::{
model::{
ProgressCounters,
ProgressEvent,
ProgressPhase,
ProgressStage,
},
reporter::ProgressReporter,
};
pub struct Progress<'a> {
reporter: &'a dyn ProgressReporter,
started_at: Instant,
report_interval: Duration,
next_running_at: Instant,
stage: Option<ProgressStage>,
}
impl<'a> Progress<'a> {
#[inline]
pub fn new(reporter: &'a dyn ProgressReporter, report_interval: Duration) -> Self {
Self::from_start(reporter, report_interval, Instant::now())
}
#[inline]
pub fn from_start(
reporter: &'a dyn ProgressReporter,
report_interval: Duration,
started_at: Instant,
) -> Self {
Self {
reporter,
started_at,
report_interval,
next_running_at: next_instant(started_at, report_interval),
stage: None,
}
}
#[inline]
pub fn with_stage(mut self, stage: ProgressStage) -> Self {
self.stage = Some(stage);
self
}
#[inline]
pub fn without_stage(mut self) -> Self {
self.stage = None;
self
}
#[inline]
pub fn report_started(&self, counters: ProgressCounters) {
self.report(ProgressPhase::Started, counters);
}
#[inline]
pub fn report_running(&self, counters: ProgressCounters) {
self.report(ProgressPhase::Running, counters);
}
pub fn report_running_if_due(&mut self, counters: ProgressCounters) -> bool {
let now = Instant::now();
if now < self.next_running_at {
return false;
}
self.report_with_elapsed(
ProgressPhase::Running,
counters,
now.saturating_duration_since(self.started_at),
);
self.next_running_at = next_instant(now, self.report_interval);
true
}
#[inline]
pub fn report_finished(&self, counters: ProgressCounters) {
self.report(ProgressPhase::Finished, counters);
}
#[inline]
pub fn report_failed(&self, counters: ProgressCounters) {
self.report(ProgressPhase::Failed, counters);
}
#[inline]
pub fn report_canceled(&self, counters: ProgressCounters) {
self.report(ProgressPhase::Canceled, counters);
}
#[inline]
pub fn report(&self, phase: ProgressPhase, counters: ProgressCounters) {
self.report_with_elapsed(phase, counters, self.elapsed());
}
pub fn report_with_elapsed(
&self,
phase: ProgressPhase,
counters: ProgressCounters,
elapsed: Duration,
) {
let event = self.event_with_elapsed(phase, counters, elapsed);
self.reporter.report(&event);
}
#[inline]
pub fn elapsed(&self) -> Duration {
self.started_at.elapsed()
}
#[inline]
pub const fn started_at(&self) -> Instant {
self.started_at
}
#[inline]
pub const fn report_interval(&self) -> Duration {
self.report_interval
}
#[inline]
pub const fn stage(&self) -> Option<&ProgressStage> {
self.stage.as_ref()
}
fn event_with_elapsed(
&self,
phase: ProgressPhase,
counters: ProgressCounters,
elapsed: Duration,
) -> ProgressEvent {
let event = ProgressEvent::from_phase(phase, counters, elapsed);
match self.stage.clone() {
Some(stage) => event.with_stage(stage),
None => event,
}
}
}
fn next_instant(base: Instant, interval: Duration) -> Instant {
base.checked_add(interval).unwrap_or(base)
}