190 lines
5.8 KiB
Rust
190 lines
5.8 KiB
Rust
|
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<Mutex<Vec<&Elem>>> = 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<Vec<&Elem>>) -> Option<Action> {
|
||
|
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::<u64>();
|
||
|
|
||
|
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));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|