use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
use std::time::Duration;
use thunderstore_engine::progress::{ProgressHandle, ProgressReporter};
pub struct IndicatifProgress {
multi: MultiProgress,
}
impl IndicatifProgress {
pub fn new() -> Self {
Self {
multi: MultiProgress::new(),
}
}
}
impl Default for IndicatifProgress {
fn default() -> Self {
Self::new()
}
}
impl ProgressReporter for IndicatifProgress {
fn begin(&self, label: &str, total: Option<u64>) -> Box<dyn ProgressHandle> {
let bar = match total {
None => {
let style = ProgressStyle::with_template("{spinner} {msg}")
.unwrap()
.tick_strings(&["-", "\\", "|", "/", ""]);
let pb = self.multi.add(ProgressBar::new_spinner());
pb.set_style(style);
pb.enable_steady_tick(Duration::from_millis(130));
pb
}
Some(total) => {
let style = ProgressStyle::with_template("{msg} [{bar:40}] {bytes}/{total_bytes}")
.unwrap()
.progress_chars("#>-");
let pb = self.multi.add(ProgressBar::new(total));
pb.set_style(style);
pb
}
};
bar.set_message(label.to_string());
Box::new(IndicatifHandle { bar })
}
}
struct IndicatifHandle {
bar: ProgressBar,
}
impl ProgressHandle for IndicatifHandle {
fn inc(&self, delta: u64) {
self.bar.inc(delta);
}
fn set_message(&self, msg: &str) {
self.bar.set_message(msg.to_string());
}
fn finish(&self, msg: Option<&str>) {
match msg {
Some(m) => self.bar.finish_with_message(m.to_string()),
None => self.bar.finish(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn both_styles_are_valid_templates() {
let reporter = IndicatifProgress::new();
let spinner = reporter.begin("fetching", None);
let bar = reporter.begin("downloading", Some(1024));
spinner.finish(Some("fetched"));
bar.inc(1024);
bar.finish(None);
}
#[test]
fn the_default_reporter_reports() {
let reporter = IndicatifProgress::default();
let bar = reporter.begin("downloading", Some(1024));
bar.set_message("downloading ModA");
bar.inc(512);
bar.finish(Some("downloaded"));
}
}