diff --git a/src/main.rs b/src/main.rs index 46082ab..1faadd6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,4 @@ -use std::{process::exit, env::Args}; +use std::{process::exit, env::Args, io::{stdin, Read}, sync::mpsc, thread, time::Instant}; static DEFAULT_MAX: usize = 100; static DEFAULT_WIDTH: usize = 80; @@ -9,24 +9,66 @@ fn main() { Ok(v ) => v, Err(e) => err(&e) }; - - // [#####....] ---- 50% - let percent = config.val * 100 / config.max; + if let Some(val) = config.val { + let bar = generate_bar(val, config.max, config.width, &config.chars, None); + println!("{}", bar); + } else { + let (tx, rx) = mpsc::sync_channel(256); + thread::spawn(move || { + let mut stdin = stdin(); + let mut out = [0; 1]; + loop { + if let Ok(count) = stdin.read(&mut out) { + tx.send(count); + } + } + }); + let mut val = 0; + let mut spin = 0; + let now = Instant::now(); + loop { + if let Ok(count) = rx.try_recv() { + val += count; + } + let bar = generate_bar_clamp(val, config.max, config.width, &config.chars, Some(spin)); + print!("\r{}", bar); + if val >= config.max { + break; + } + spin = ((now.elapsed().as_millis() / 100) % 6) as usize; + } + } +} + +fn generate_bar(val: usize, max: usize, width: usize, chars: &Chars, spin: Option) -> String { + let percent = val * 100 / max; let percent_str = percent.to_string(); - let after_bar = format!(" {} {}% ", "-".repeat(6 - percent_str.len()), percent_str); - let bar_len = config.width - after_bar.len(); + let after_bar = if let Some(spin) = spin { + format!(" {} {}% {} ", "-".repeat(6 - percent_str.len()), percent_str, if val == max { chars.spin_done() } else { chars.spin(spin) }) + } else { + format!(" {} {}% ", "-".repeat(6 - percent_str.len()), percent_str) + }; + let bar_len = width - after_bar.len(); let sep = bar_len * percent / 100; let mut bar = String::new(); - bar.push(if sep > 0 { config.first_char.1 } else { config.first_char.0 }); + bar.push(chars.first(sep > 0)); for i in 1..bar_len-1 { - bar.push(if sep > i { config.middle_char.1 } else { config.middle_char.0 }); + bar.push(chars.mid(sep > i)); } - bar.push(if sep > bar_len-1 { config.last_char.1 } else { config.last_char.0 }); + bar.push(chars.last(sep > bar_len - 1)); - println!("{}{}", bar, after_bar); + format!("{}{}", bar, after_bar) +} + +fn generate_bar_clamp(mut val: usize, max: usize, width: usize, chars: &Chars, spin: Option) -> String { + if val > max { + val = max; + } + + generate_bar(val, max, width, chars, spin) } fn err(msg: &str) -> ! { @@ -36,16 +78,48 @@ fn err(msg: &str) -> ! { } fn show_help() { - eprintln!("Usage: progress [--max ] [--width ] [--fira]"); + eprintln!("Usage: progress [] [--max ] [--width ] [--fira]"); +} + +struct Chars { + first: (char, char), + mid: (char, char), + last: (char, char), + spin: [char; 6], + spin_done: char +} + +impl Chars { + fn new(first: (char, char), mid: (char, char), last: (char, char), spin: [char; 6], spin_done: char) -> Self { + Self { first, mid, last, spin, spin_done } + } + + fn first(&self, filled: bool) -> char { + if filled { self.first.1 } else { self.first.0 } + } + + fn mid(&self, filled: bool) -> char { + if filled { self.mid.1 } else { self.mid.0 } + } + + fn last(&self, filled: bool) -> char { + if filled { self.last.1 } else { self.last.0 } + } + + fn spin(&self, n: usize) -> char { + self.spin[n % 6] + } + + fn spin_done(&self) -> char { + self.spin_done + } } struct Config { - val: usize, + val: Option, max: usize, - first_char: (char, char), - middle_char: (char, char), - last_char: (char, char), - width: usize + width: usize, + chars: Chars } impl Config { @@ -53,17 +127,19 @@ impl Config { args.next(); let mut val = None; let mut max = None; - let mut first_char = ('[', '['); - let mut middle_char = ('.', '#'); - let mut last_char = (']', ']'); let mut width = None; + let mut first_char = ('[', '['); + let mut mid_char = ('.', '#'); + let mut last_char = (']', ']'); + let mut spin = ['/', '-', '\\', '/', '-', '\\']; while let Some(next) = args.next() { if next.starts_with("--") { if next == "--fira" { first_char = ('\u{ee00}', '\u{ee03}'); - middle_char = ('\u{ee01}', '\u{ee04}'); + mid_char = ('\u{ee01}', '\u{ee04}'); last_char = ('\u{ee02}', '\u{ee05}'); + spin = ['\u{ee06}', '\u{ee07}', '\u{ee08}', '\u{ee09}', '\u{ee0a}', '\u{ee0b}']; } else if next == "--max" { if let Some(_) = max { return Err("can only provide 1 max".to_string()) } let next = match args.next() { @@ -98,18 +174,9 @@ impl Config { } } } - - let val = match val { - Some(v) => v, - None => return Err("must provide a value".to_string()) - }; let max = max.unwrap_or(DEFAULT_MAX); - if val > max { - return Err("value cannot be > max".to_string()); - } - let width = width.unwrap_or_else(|| termsize::get().map_or(DEFAULT_WIDTH, |v| v.cols.into())); if width < 11 { @@ -117,12 +184,8 @@ impl Config { } Ok(Config { - val, - max, - first_char, - middle_char, - last_char, - width + val, max, width, + chars: Chars::new(first_char, mid_char, last_char, spin, '✓'), }) } } \ No newline at end of file