use crate::{ProgressExt, Pulse, StopReason};
use core::fmt;
#[must_use = "call finish to flush and check cancellation; Drop only flushes counts"]
pub struct Paced<'a> {
pulse: Option<&'a dyn Pulse>,
left: u64,
every: u64,
}
impl fmt::Debug for Paced<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Paced")
.field("live", &self.pulse.is_some())
.field("pending", &(self.every - self.left))
.field("every", &self.every)
.finish()
}
}
impl<'a> Paced<'a> {
pub fn new(pulse: &'a dyn Pulse, every: u64) -> Self {
let every = every.max(1);
Self {
pulse: pulse.live(),
left: every,
every,
}
}
#[inline]
#[track_caller]
pub fn step(&mut self, completed: u64) -> Result<(), StopReason> {
let Some(pulse) = self.pulse else {
return Ok(());
};
if completed < self.left {
self.left -= completed;
Ok(())
} else {
let pending = (self.every - self.left).saturating_add(completed);
self.left = self.every;
reach(pulse, pending)
}
}
#[inline]
#[track_caller]
pub fn check(&self) -> Result<(), StopReason> {
match self.pulse {
Some(pulse) => pulse.check(),
None => Ok(()),
}
}
#[track_caller]
pub fn finish(mut self) -> Result<(), StopReason> {
self.flush();
self.check()
}
#[inline]
#[track_caller]
pub fn flush(&mut self) {
if let Some(pulse) = self.pulse
&& self.left != self.every
{
let pending = self.every - self.left;
self.left = self.every;
pulse.advance(pending);
}
}
}
#[cold]
#[inline(never)]
#[track_caller]
fn reach(pulse: &dyn Pulse, pending: u64) -> Result<(), StopReason> {
pulse.advance(pending);
pulse.check()
}
impl Drop for Paced<'_> {
#[inline]
fn drop(&mut self) {
self.flush();
}
}
impl<'a> dyn Pulse + 'a {
pub fn paced(&self, every: u64) -> Paced<'_> {
Paced::new(self, every)
}
}
impl crate::Child<'_> {
pub fn paced(&self, every: u64) -> Paced<'_> {
Paced::new(self.pulse(), every)
}
}
impl crate::SharedPulse {
pub fn paced(&self, every: u64) -> Paced<'_> {
Paced::new(self.as_pulse(), every)
}
}