use std::sync::Arc;
use std::thread::sleep;
use std::time::Duration;
use rich_rs::{
BarColumn, Console, ConsoleOptions, Group, Live, LiveOptions, Panel, Progress, ProgressColumn,
Renderable, Segments, SpinnerColumn, TextColumn, TimeElapsedColumn,
};
const STEP_ACTIONS: [&str; 4] = ["downloading", "configuring", "building", "installing"];
const APPS: [(&str, [u64; 4]); 3] = [
("one", [2, 1, 4, 2]),
("two", [1, 3, 8, 4]),
("three", [2, 1, 3, 2]),
];
fn main() -> std::io::Result<()> {
let current_app_columns: Vec<Box<dyn ProgressColumn>> = vec![
Box::new(TimeElapsedColumn::new()),
Box::new(TextColumn::new("{task.description}")),
];
let current_app_progress = Arc::new(Progress::new(
current_app_columns,
LiveOptions {
auto_refresh: false,
..Default::default()
},
false,
false,
));
let step_columns: Vec<Box<dyn ProgressColumn>> = vec![
Box::new(TextColumn::new(" ")),
Box::new(TimeElapsedColumn::new()),
Box::new(TextColumn::new("[bold purple]{task.description}")),
Box::new(SpinnerColumn::with_spinner("simpleDots")),
];
let step_progress = Arc::new(Progress::new(
step_columns,
LiveOptions {
auto_refresh: false,
..Default::default()
},
false,
false,
));
let app_steps_columns: Vec<Box<dyn ProgressColumn>> = vec![
Box::new(TextColumn::new(
"[bold blue]Progress for app {task.description}: {task.percentage:.0f}%",
)),
Box::new(BarColumn::new()),
Box::new(TextColumn::new(
"({task.completed:.0f} of {task.total:.0f} steps done)",
)),
];
let app_steps_progress = Arc::new(Progress::new(
app_steps_columns,
LiveOptions {
auto_refresh: false,
..Default::default()
},
false,
false,
));
let overall_columns: Vec<Box<dyn ProgressColumn>> = vec![
Box::new(TimeElapsedColumn::new()),
Box::new(BarColumn::new()),
Box::new(TextColumn::new("{task.description}")),
];
let overall_progress = Arc::new(Progress::new(
overall_columns,
LiveOptions {
auto_refresh: false,
..Default::default()
},
false,
false,
));
let overall_task_id = overall_progress.add_task("", true, Some(APPS.len() as f64), 0.0, true);
let progress_group = DynamicProgressGroup {
current_app_progress: current_app_progress.clone(),
step_progress: step_progress.clone(),
app_steps_progress: app_steps_progress.clone(),
overall_progress: overall_progress.clone(),
};
let live_options = LiveOptions {
auto_refresh: true,
refresh_per_second: 10.0,
..Default::default()
};
let mut live = Live::with_options(Box::new(progress_group), live_options);
live.start(true)?;
for (idx, (name, step_times)) in APPS.iter().enumerate() {
let top_descr = format!(
"[bold #AAAAAA]({} out of {} apps installed)",
idx,
APPS.len()
);
overall_progress.update(
overall_task_id,
None,
None,
None,
Some(top_descr),
None,
false,
None,
);
let current_task_id = current_app_progress.add_task(
&format!("Installing app {}", name),
true,
None,
0.0,
true,
);
let app_steps_task_id = app_steps_progress.add_task(
name, true,
Some(step_times.len() as f64),
0.0,
true,
);
run_steps(
name,
step_times,
app_steps_task_id,
&step_progress,
&app_steps_progress,
);
app_steps_progress.update(
app_steps_task_id,
None,
None,
None,
None,
Some(false),
false,
None,
);
current_app_progress.stop_task(current_task_id);
current_app_progress.update(
current_task_id,
None,
None,
None,
Some(format!("[bold green]App {} installed!", name)),
None,
false,
None,
);
overall_progress.advance(overall_task_id, 1.0);
}
let final_descr = format!("[bold green]{} apps installed, done!", APPS.len());
overall_progress.update(
overall_task_id,
None,
None,
None,
Some(final_descr),
None,
false,
None,
);
sleep(Duration::from_millis(500));
live.stop()?;
Ok(())
}
fn run_steps(
name: &str,
step_times: &[u64; 4],
app_steps_task_id: rich_rs::TaskID,
step_progress: &Arc<Progress>,
app_steps_progress: &Arc<Progress>,
) {
for (idx, &step_time) in step_times.iter().enumerate() {
let action = STEP_ACTIONS[idx];
let step_task_id = step_progress.add_task(
&format!("{} {}...", action, name),
true,
Some(step_time as f64),
0.0,
true,
);
for _ in 0..step_time {
sleep(Duration::from_millis(500));
step_progress.advance(step_task_id, 1.0);
}
step_progress.stop_task(step_task_id);
step_progress.update(
step_task_id,
None,
None,
None,
None,
Some(false),
false,
None,
);
app_steps_progress.advance(app_steps_task_id, 1.0);
}
}
#[derive(Clone)]
struct DynamicProgressGroup {
current_app_progress: Arc<Progress>,
step_progress: Arc<Progress>,
app_steps_progress: Arc<Progress>,
overall_progress: Arc<Progress>,
}
impl Renderable for DynamicProgressGroup {
fn render(&self, console: &Console, options: &ConsoleOptions) -> Segments {
let inner_group = Group::from_arcs([
Arc::new(ProgressRef(self.current_app_progress.clone())) as Arc<dyn Renderable>,
Arc::new(ProgressRef(self.step_progress.clone())) as Arc<dyn Renderable>,
Arc::new(ProgressRef(self.app_steps_progress.clone())) as Arc<dyn Renderable>,
]);
let panel = Panel::new(Box::new(inner_group));
let outer_group = Group::from_arcs([
Arc::new(panel) as Arc<dyn Renderable>,
Arc::new(ProgressRef(self.overall_progress.clone())) as Arc<dyn Renderable>,
]);
outer_group.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)
}
}