Skip to content

Commit

Permalink
Implement insert method for MultiProgress (console-rs#178)
Browse files Browse the repository at this point in the history
  • Loading branch information
nlinker committed Jun 10, 2020
1 parent 0529656 commit b160d94
Show file tree
Hide file tree
Showing 2 changed files with 194 additions and 3 deletions.
157 changes: 157 additions & 0 deletions examples/multi-tree.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
use lazy_static::lazy_static;
use std::fmt::Debug;
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::{Duration, SystemTime, UNIX_EPOCH};

#[derive(Clone, Debug)]
struct Rng(u64);

impl Rng {
fn new() -> Self {
let start = SystemTime::now();
let since_the_epoch = start
.duration_since(UNIX_EPOCH)
.expect("Time went backwards")
.as_secs();
Self(since_the_epoch)
}

fn gen_range(&mut self, n: u64) -> u64 {
let mut state = self.0;
state ^= state >> 12;
state ^= state << 25;
state ^= state >> 27;
state = state.wrapping_mul(2685821657736338717u64);
self.0 = state;
state % n
}
}

#[derive(Debug, Clone)]
enum Action {
AddProgressBar(usize),
IncProgressBar(usize),
}

#[derive(Clone, Debug)]
struct Elem {
key: String,
index: usize,
indent: usize,
progress_bar: ProgressBar,
}

lazy_static! {
static ref ELEMENTS: [Elem; 9] = [
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() },
];
}

fn main() {
let mp = Arc::new(MultiProgress::new());
let sty_main = ProgressStyle::default_bar().template("{bar:40.green/yellow} {pos:>4}/{len:4}");
let sty_aux = ProgressStyle::default_bar().template("{spinner:.green} {msg} {pos:>4}/{len:4}");

let pb_main = mp.add(ProgressBar::new(
ELEMENTS.iter().map(|e| e.progress_bar.length()).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 = Rng::new();
pb_main.tick();
loop {
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();
let len = elem.progress_bar.length();
if pos >= len {
elem.progress_bar.finish_with_message(&format!(
"{}{} {}",
" ".repeat(elem.indent),
"✔",
elem.key
));
}
pb_main.inc(1);
}
}
thread::sleep(Duration::from_millis(15));
}
});

mp.join().unwrap();

println!("===============================");
println!("the tree should be the same as:");
for elem in tree2.lock().unwrap().iter() {
println!("{} {}", " ".repeat(elem.indent), elem.key);
}
}

fn get_action<'a>(rng: &'a mut Rng, 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();
len - pos
})
.sum::<u64>();
if sum_free == 0 && list_len == elem_len {
// nothing to do more
return None;
} else if sum_free == 0 && list_len < elem_len {
// there is no place to make an increment
return Some(Action::AddProgressBar(tree.lock().unwrap().len()));
} else {
loop {
let list = tree.lock().unwrap();
let k = rng.gen_range(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 {
return Some(Action::IncProgressBar(l));
}
}
}
}
}
40 changes: 37 additions & 3 deletions src/progress.rs
Original file line number Diff line number Diff line change
Expand Up @@ -779,6 +779,10 @@ impl ProgressBar {
pub fn position(&self) -> u64 {
self.state.read().unwrap().pos
}

pub fn length(&self) -> u64 {
self.state.read().unwrap().len
}
}

fn draw_state(state: &mut ProgressState) -> io::Result<()> {
Expand Down Expand Up @@ -852,6 +856,7 @@ struct MultiObject {

struct MultiProgressState {
objects: Vec<MultiObject>,
ordering: Vec<usize>,
draw_target: ProgressDrawTarget,
move_cursor: bool,
}
Expand Down Expand Up @@ -894,6 +899,7 @@ impl MultiProgress {
MultiProgress {
state: RwLock::new(MultiProgressState {
objects: vec![],
ordering: vec![],
draw_target,
move_cursor: false,
}),
Expand Down Expand Up @@ -925,13 +931,40 @@ impl MultiProgress {
/// object overriding custom `ProgressDrawTarget` settings.
pub fn add(&self, pb: ProgressBar) -> ProgressBar {
let mut state = self.state.write().unwrap();
let idx = state.objects.len();
let object_idx = state.objects.len();
state.objects.push(MultiObject {
done: false,
draw_state: None,
});
state.ordering.push(object_idx);
pb.set_draw_target(ProgressDrawTarget {
kind: ProgressDrawTargetKind::Remote(object_idx, Mutex::new(self.tx.clone())),
});
pb
}

/// Inserts a progress bar.
///
/// The progress bar inserted at position `index` will have the draw
/// target changed to a remote draw target that is intercepted by the
/// multi progress object overriding custom `ProgressDrawTarget` settings.
///
/// If `index >= MultiProgressState::objects.len()`, the progress bar
/// is added to the end of the list.
pub fn insert(&self, index: usize, pb: ProgressBar) -> ProgressBar {
let mut state = self.state.write().unwrap();
let object_idx = state.objects.len();
state.objects.push(MultiObject {
done: false,
draw_state: None,
});
if index > state.ordering.len() {
state.ordering.push(object_idx);
} else {
state.ordering.insert(index, object_idx);
}
pb.set_draw_target(ProgressDrawTarget {
kind: ProgressDrawTargetKind::Remote(idx, Mutex::new(self.tx.clone())),
kind: ProgressDrawTargetKind::Remote(object_idx, Mutex::new(self.tx.clone())),
});
pb
}
Expand Down Expand Up @@ -1008,7 +1041,8 @@ impl MultiProgress {
let orphan_lines_count = orphan_lines.len();
lines.extend(orphan_lines);

for obj in state.objects.iter() {
for index in state.ordering.iter() {
let obj = &state.objects[*index];
if let Some(ref draw_state) = obj.draw_state {
lines.extend_from_slice(&draw_state.lines[..]);
}
Expand Down

0 comments on commit b160d94

Please sign in to comment.