use std::sync::Arc;
use std::sync::atomic::{AtomicU64, Ordering};
use std::thread::sleep;
use std::time::Duration;
use rich_rs::{
BarColumn, Console, ConsoleOptions, Live, LiveOptions, Panel, Progress, ProgressColumn,
Renderable, Segments, SpinnerColumn, Style, Table, TextColumn,
};
fn main() -> std::io::Result<()> {
let job1_completed = Arc::new(AtomicU64::new(0));
let job2_completed = Arc::new(AtomicU64::new(0));
let job3_completed = Arc::new(AtomicU64::new(0));
let job1_total: f64 = 100.0;
let job2_total: f64 = 200.0;
let job3_total: f64 = 400.0;
let overall_total: f64 = job1_total + job2_total + job3_total;
let job_columns: Vec<Box<dyn ProgressColumn>> = vec![
Box::new(TextColumn::new("{task.description}")),
Box::new(SpinnerColumn::new()),
Box::new(BarColumn::new()),
Box::new(TextColumn::new(
"[progress.percentage]{task.percentage:>3.0f}%",
)),
];
let job_live_options = LiveOptions {
auto_refresh: false,
..Default::default()
};
let job_progress = Progress::new(job_columns, job_live_options, false, false);
let job1 = job_progress.add_task("[green]Cooking", true, Some(job1_total), 0.0, true);
let job2 = job_progress.add_task("[magenta]Baking", true, Some(job2_total), 0.0, true);
let job3 = job_progress.add_task("[cyan]Mixing", true, Some(job3_total), 0.0, true);
let overall_live_options = LiveOptions {
auto_refresh: false,
..Default::default()
};
let overall_progress = Progress::new_default(overall_live_options, false, false, false);
let overall_task = overall_progress.add_task("All Jobs", true, Some(overall_total), 0.0, true);
let combined = CombinedProgress {
job_progress: Arc::new(job_progress),
overall_progress: Arc::new(overall_progress),
};
let live_options = LiveOptions {
auto_refresh: true,
refresh_per_second: 10.0,
..Default::default()
};
let mut live = Live::with_options(Box::new(combined.clone()), live_options);
live.start(true)?;
loop {
sleep(Duration::from_millis(100));
let c1 = job1_completed.load(Ordering::SeqCst) as f64;
let c2 = job2_completed.load(Ordering::SeqCst) as f64;
let c3 = job3_completed.load(Ordering::SeqCst) as f64;
if c1 < job1_total {
job1_completed.fetch_add(1, Ordering::SeqCst);
combined.job_progress.advance(job1, 1.0);
}
if c2 < job2_total {
job2_completed.fetch_add(1, Ordering::SeqCst);
combined.job_progress.advance(job2, 1.0);
}
if c3 < job3_total {
job3_completed.fetch_add(1, Ordering::SeqCst);
combined.job_progress.advance(job3, 1.0);
}
let total_completed = job1_completed.load(Ordering::SeqCst) as f64
+ job2_completed.load(Ordering::SeqCst) as f64
+ job3_completed.load(Ordering::SeqCst) as f64;
combined.overall_progress.update(
overall_task,
None,
Some(total_completed),
None,
None,
None,
false,
None,
);
if total_completed >= overall_total {
break;
}
}
live.stop()?;
println!("All jobs completed!");
Ok(())
}
#[derive(Clone)]
struct CombinedProgress {
job_progress: Arc<Progress>,
overall_progress: Arc<Progress>,
}
impl Renderable for CombinedProgress {
fn render(&self, console: &Console, options: &ConsoleOptions) -> Segments {
let mut table = Table::grid().with_expand(true);
table.add_column(rich_rs::Column::new());
table.add_column(rich_rs::Column::new());
let overall_panel = Panel::fit(Box::new(ProgressRef(self.overall_progress.clone())))
.with_title("[bold]Overall Progress")
.with_border_style(Style::parse("green").unwrap_or_default())
.with_padding((2, 2));
let jobs_panel = Panel::fit(Box::new(ProgressRef(self.job_progress.clone())))
.with_title("[bold]Jobs")
.with_border_style(Style::parse("red").unwrap_or_default())
.with_padding((1, 2));
let cells: Vec<Box<dyn Renderable + Send + Sync>> =
vec![Box::new(overall_panel), Box::new(jobs_panel)];
table.add_row(rich_rs::Row::new(cells));
table.render(console, options)
}
}
#[derive(Clone)]
struct ProgressRef(Arc<Progress>);
impl Renderable for ProgressRef {
fn render(&self, console: &Console, options: &ConsoleOptions) -> Segments {
self.0.render(console, options)
}
fn measure(&self, console: &Console, options: &ConsoleOptions) -> rich_rs::Measurement {
self.0.measure(console, options)
}
}