use std::fmt::Debug; use std::sync::{Arc, Mutex}; use std::thread; use std::time::Duration; use indicatif::{MultiProgress, ProgressBar, ProgressStyle}; use once_cell::sync::Lazy; use rand::rngs::ThreadRng; use rand::{Rng, RngCore}; #[derive(Debug, Clone)] enum Action { AddProgressBar(usize), IncProgressBar(usize), } #[derive(Clone, Debug)] struct Elem { key: String, index: usize, indent: usize, progress_bar: ProgressBar, } static ELEMENTS: Lazy<[Elem; 9]> = Lazy::new(|| { [ Elem { indent: 1, index: 0, progress_bar: ProgressBar::new(32), key: "jumps".to_string(), }, Elem { indent: 2, index: 1, progress_bar: ProgressBar::new(32), key: "lazy".to_string(), }, Elem { indent: 0, index: 0, progress_bar: ProgressBar::new(32), key: "the".to_string(), }, Elem { indent: 3, index: 3, progress_bar: ProgressBar::new(32), key: "dog".to_string(), }, Elem { indent: 2, index: 2, progress_bar: ProgressBar::new(32), key: "over".to_string(), }, Elem { indent: 2, index: 1, progress_bar: ProgressBar::new(32), key: "brown".to_string(), }, Elem { indent: 1, index: 1, progress_bar: ProgressBar::new(32), key: "quick".to_string(), }, Elem { indent: 3, index: 5, progress_bar: ProgressBar::new(32), key: "a".to_string(), }, Elem { indent: 3, index: 3, progress_bar: ProgressBar::new(32), key: "fox".to_string(), }, ] }); /// The example implements the tree-like collection of progress bars, where elements are /// added on the fly and progress bars get incremented until all elements is added and /// all progress bars finished. /// On each iteration `get_action` function returns some action, and when the tree gets /// complete, the function returns `None`, which finishes the loop. fn main() { let mp = Arc::new(MultiProgress::new()); let sty_main = ProgressStyle::with_template("{bar:40.green/yellow} {pos:>4}/{len:4}").unwrap(); let sty_aux = ProgressStyle::with_template("{spinner:.green} {msg} {pos:>4}/{len:4}").unwrap(); let pb_main = mp.add(ProgressBar::new( ELEMENTS .iter() .map(|e| e.progress_bar.length().unwrap()) .sum(), )); pb_main.set_style(sty_main); for elem in ELEMENTS.iter() { elem.progress_bar.set_style(sty_aux.clone()); } let tree: Arc>> = Arc::new(Mutex::new(Vec::with_capacity(ELEMENTS.len()))); let tree2 = Arc::clone(&tree); let mp2 = Arc::clone(&mp); let _ = thread::spawn(move || { let mut rng = ThreadRng::default(); pb_main.tick(); loop { thread::sleep(Duration::from_millis(15)); match get_action(&mut rng, &tree) { None => { // all elements were exhausted pb_main.finish(); return; } Some(Action::AddProgressBar(el_idx)) => { let elem = &ELEMENTS[el_idx]; let pb = mp2.insert(elem.index + 1, elem.progress_bar.clone()); pb.set_message(format!("{} {}", " ".repeat(elem.indent), elem.key)); tree.lock().unwrap().insert(elem.index, elem); } Some(Action::IncProgressBar(el_idx)) => { let elem = &tree.lock().unwrap()[el_idx]; elem.progress_bar.inc(1); let pos = elem.progress_bar.position(); if pos >= elem.progress_bar.length().unwrap() { elem.progress_bar.finish_with_message(format!( "{}{} {}", " ".repeat(elem.indent), "✔", elem.key )); } pb_main.inc(1); } } } }) .join(); println!("==============================="); println!("the tree should be the same as:"); for elem in tree2.lock().unwrap().iter() { println!("{} {}", " ".repeat(elem.indent), elem.key); } } /// The function guarantees to return the action, that is valid for the current tree. fn get_action(rng: &mut dyn RngCore, tree: &Mutex>) -> Option { let elem_len = ELEMENTS.len() as u64; let list_len = tree.lock().unwrap().len() as u64; let sum_free = tree .lock() .unwrap() .iter() .map(|e| { let pos = e.progress_bar.position(); let len = e.progress_bar.length().unwrap(); len - pos }) .sum::(); if sum_free == 0 && list_len == elem_len { // nothing to do more None } else if sum_free == 0 && list_len < elem_len { // there is no place to make an increment Some(Action::AddProgressBar(tree.lock().unwrap().len())) } else { loop { let list = tree.lock().unwrap(); let k = rng.gen_range(0..17); if k == 0 && list_len < elem_len { return Some(Action::AddProgressBar(list.len())); } else { let l = (k % list_len) as usize; let pos = list[l].progress_bar.position(); let len = list[l].progress_bar.length(); if pos < len.unwrap() { return Some(Action::IncProgressBar(l)); } } } } }