use crate::tty::move_cursor_up;
use crate::ProgressBar;
use crossbeam_channel::{unbounded, Receiver, Sender};
use std::io::{Result, Stdout, Write};
use std::str::from_utf8;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Mutex;
pub struct MultiBar<T: Write> {
state: Mutex<State<T>>,
chan: (Sender<WriteMsg>, Receiver<WriteMsg>),
nbars: AtomicUsize,
}
struct State<T: Write> {
lines: Vec<String>,
nlines: usize,
handle: T,
}
impl MultiBar<Stdout> {
pub fn new() -> MultiBar<Stdout> {
MultiBar::on(::std::io::stdout())
}
}
impl<T: Write> MultiBar<T> {
pub fn on(handle: T) -> MultiBar<T> {
MultiBar {
state: Mutex::new(State {
lines: Vec::new(),
handle,
nlines: 0,
}),
chan: unbounded(),
nbars: AtomicUsize::new(0),
}
}
pub fn println(&self, s: &str) {
let mut state = self.state.lock().unwrap();
state.lines.push(s.to_owned());
state.nlines += 1;
}
pub fn create_bar(&self, total: u64) -> ProgressBar<Pipe> {
let mut state = self.state.lock().unwrap();
state.lines.push(String::new());
state.nlines += 1;
self.nbars.fetch_add(1, Ordering::SeqCst);
let mut p = ProgressBar::on(
Pipe {
level: state.nlines - 1,
chan: self.chan.0.clone(),
},
total,
);
p.is_multibar = true;
p.add(0);
p
}
pub fn listen(&self) {
let mut first = true;
let mut out = String::new();
while self.nbars.load(Ordering::SeqCst) > 0 {
let msg = self.chan.1.recv().unwrap();
if msg.done {
self.nbars.fetch_sub(1, Ordering::SeqCst);
continue;
}
out.clear();
let mut state = self.state.lock().unwrap();
state.lines[msg.level] = msg.string;
if !first {
out += &move_cursor_up(state.nlines);
} else {
first = false;
}
for l in state.lines.iter() {
out.push_str(&format!("\r{}\n", l));
}
printfl!(state.handle, "{}", out);
}
}
}
pub struct Pipe {
level: usize,
chan: Sender<WriteMsg>,
}
impl Write for Pipe {
fn write(&mut self, buf: &[u8]) -> Result<usize> {
let s = from_utf8(buf).unwrap().to_owned();
self.chan
.send(WriteMsg {
done: s.is_empty(),
level: self.level,
string: s,
})
.unwrap();
Ok(1)
}
fn flush(&mut self) -> Result<()> {
Ok(())
}
}
struct WriteMsg {
done: bool,
level: usize,
string: String,
}