use std::time::{Duration, Instant};
use crate::progressive::Progressive;
const RATE_SAMPLE_INTERVAL: Duration = Duration::from_millis(100);
const RATE_ALPHA: f64 = 0.3;
pub(crate) struct State {
pub(crate) label: Option<String>,
pub(crate) message: Option<String>,
pub(crate) progress: Option<f64>,
pub(crate) bytes_done: u64,
pub(crate) bytes_total: Option<u64>,
pub(crate) rate: Option<f64>,
rate_sample: Option<(Instant, u64)>,
pub(crate) with_elapsed_time: bool,
pub(crate) start: Option<Instant>,
}
impl State {
pub(crate) fn new() -> Self {
Self {
label: None,
message: None,
progress: None,
bytes_done: 0,
bytes_total: None,
rate: None,
rate_sample: None,
with_elapsed_time: false,
start: None,
}
}
pub(crate) fn set_label(&mut self, label: String) {
self.label = Some(label);
}
pub(crate) fn set_message(&mut self, message: String) {
self.message = Some(message);
}
pub(crate) fn set_progress(&mut self, progress: f64) {
self.progress = Some(progress);
}
pub(crate) fn set_bytes_total(&mut self, total: u64) {
self.bytes_total = Some(total);
}
pub(crate) fn add_bytes(&mut self, delta: u64) {
self.bytes_done = self.bytes_done.saturating_add(delta);
let now = Instant::now();
match self.rate_sample {
None => self.rate_sample = Some((now, self.bytes_done)),
Some((last_time, last_bytes)) => {
let dt = now.duration_since(last_time);
if dt >= RATE_SAMPLE_INTERVAL {
let bytes_delta = self.bytes_done.saturating_sub(last_bytes) as f64;
let instantaneous = bytes_delta / dt.as_secs_f64();
self.rate = Some(match self.rate {
None => instantaneous,
Some(r) => RATE_ALPHA * instantaneous + (1.0 - RATE_ALPHA) * r,
});
self.rate_sample = Some((now, self.bytes_done));
}
}
}
if let Some(total) = self.bytes_total {
if total > 0 {
self.progress = Some(self.bytes_done as f64 / total as f64);
}
}
}
pub(crate) fn enable_elapsed_time(&mut self) {
self.with_elapsed_time = true;
}
pub(crate) fn elapsed(&mut self) -> Duration {
self.start.get_or_insert_with(Instant::now).elapsed()
}
}
impl Progressive<'_> for State {
fn label(&self) -> Option<&str> {
self.label.as_deref()
}
fn message(&self) -> Option<&str> {
self.message.as_deref()
}
fn progress(&self) -> Option<f64> {
self.progress
}
fn bytes_done(&self) -> u64 {
self.bytes_done
}
fn bytes_total(&self) -> Option<u64> {
self.bytes_total
}
fn rate(&self) -> Option<f64> {
self.rate
}
}