pub mod factory;
pub mod indicatif;
pub mod silent;
pub mod simple;
pub mod status;
pub mod types;
pub use factory::ProgressFactory;
pub use indicatif::IndicatifProgress;
pub use silent::SilentProgress;
pub use simple::SimpleProgress;
pub use status::StatusReporter;
pub use types::{ProgressConfig, ProgressStyle};
pub trait ProgressIndicator: Send + Sync {
fn start(&mut self, config: ProgressConfig);
fn update(&mut self, current: u64, total: Option<u64>);
fn set_message(&mut self, message: String);
fn complete(&mut self, message: Option<String>);
fn success(&self, message: &str) -> std::io::Result<()>;
fn error(&mut self, message: String);
fn create_child(&mut self) -> Box<dyn ProgressIndicator>;
fn suspend(&self, f: &mut dyn FnMut());
fn println(&self, message: &str) -> std::io::Result<()>;
}
#[cfg(test)]
mod tests {
use super::*;
struct MockProgress {
started: bool,
current: u64,
total: Option<u64>,
message: String,
completed: bool,
errored: bool,
}
impl MockProgress {
fn new() -> Self {
Self {
started: false,
current: 0,
total: None,
message: String::new(),
completed: false,
errored: false,
}
}
}
impl ProgressIndicator for MockProgress {
fn start(&mut self, config: ProgressConfig) {
self.started = true;
self.total = config.total;
}
fn update(&mut self, current: u64, total: Option<u64>) {
self.current = current;
if total.is_some() {
self.total = total;
}
}
fn set_message(&mut self, message: String) {
self.message = message;
}
fn complete(&mut self, message: Option<String>) {
self.completed = true;
if let Some(msg) = message {
self.message = msg;
}
}
fn success(&self, message: &str) -> std::io::Result<()> {
println!("✓ {message}");
Ok(())
}
fn error(&mut self, message: String) {
self.errored = true;
self.message = message;
}
fn create_child(&mut self) -> Box<dyn ProgressIndicator> {
Box::new(MockProgress::new())
}
fn suspend(&self, f: &mut dyn FnMut()) {
f();
}
fn println(&self, message: &str) -> std::io::Result<()> {
println!("{message}");
Ok(())
}
}
#[test]
fn test_trait_implementation() {
let mut progress = MockProgress::new();
let config = ProgressConfig::new(ProgressStyle::Count).with_total(100);
progress.start(config);
assert!(progress.started);
assert_eq!(progress.total, Some(100));
progress.update(50, None);
assert_eq!(progress.current, 50);
assert_eq!(progress.total, Some(100));
progress.update(60, Some(200));
assert_eq!(progress.current, 60);
assert_eq!(progress.total, Some(200));
progress.set_message("Processing item".to_string());
assert_eq!(progress.message, "Processing item");
progress.complete(Some("Done!".to_string()));
assert!(progress.completed);
assert_eq!(progress.message, "Done!");
}
#[test]
fn test_error_handling() {
let mut progress = MockProgress::new();
let config = ProgressConfig::new(ProgressStyle::Count);
progress.start(config);
progress.error("Something went wrong".to_string());
assert!(progress.errored);
assert_eq!(progress.message, "Something went wrong");
}
#[test]
fn test_indeterminate_progress() {
let mut progress = MockProgress::new();
let config = ProgressConfig::new(ProgressStyle::Bytes);
progress.start(config);
assert!(progress.started);
assert_eq!(progress.total, None);
progress.update(1000, None);
assert_eq!(progress.current, 1000);
assert_eq!(progress.total, None);
}
#[test]
fn test_trait_object() {
let progress: Box<dyn ProgressIndicator> = Box::new(MockProgress::new());
fn accept_progress(_p: Box<dyn ProgressIndicator>) {}
accept_progress(progress);
}
}