From 57a177e2f946390559a1f17787c5a15d23ac3393 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Thu, 10 Sep 2020 12:39:07 +0200 Subject: Show progress bar in fill output This patch uses the progressing crate to display a progress bar for the fill command if the output is printed to a TTY. --- Cargo.lock | 19 ++++++++++ Cargo.toml | 3 ++ src/commands.rs | 19 +++++----- src/main.rs | 1 + src/output.rs | 110 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/redefine.rs | 6 ++++ 6 files changed, 149 insertions(+), 9 deletions(-) create mode 100644 src/output.rs diff --git a/Cargo.lock b/Cargo.lock index 5e359d1..94126a2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -163,6 +163,15 @@ version = "0.2.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99e85c08494b21a9054e7fe1374a732aeadaff3980b6990b94bfd3a70f690005" +[[package]] +name = "log" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" +dependencies = [ + "cfg-if", +] + [[package]] name = "memchr" version = "2.3.3" @@ -204,6 +213,7 @@ dependencies = [ "nitrokey", "nitrokey-test", "nitrokey-test-state", + "progressing", "regex", "serde", "structopt", @@ -299,6 +309,15 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "progressing" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b7db19a74ba7c34de36558abed080568491d2b8999a34de914b1793b0b4b1b" +dependencies = [ + "log", +] + [[package]] name = "quote" version = "1.0.3" diff --git a/Cargo.toml b/Cargo.toml index 8ffa07d..a24cc81 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -53,6 +53,9 @@ version = "0.1" [dependencies.nitrokey] version = "0.7.1" +[dependencies.progressing] +version = "3.0.2" + [dependencies.serde] version = "1.0" features = ["derive"] diff --git a/src/commands.rs b/src/commands.rs index 9af1853..ced976c 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -23,6 +23,7 @@ use nitrokey::GetPasswordSafe; use crate::args; use crate::config; +use crate::output; use crate::pinentry; use crate::Context; @@ -482,20 +483,20 @@ pub fn fill(ctx: &mut Context<'_>) -> anyhow::Result<()> { device.fill_sd_card(&pin).context("Failed to fill SD card") })?; - let mut last_progress = 0; - loop { + let mut progress_bar = output::ProgressBar::new(); + progress_bar.draw(ctx)?; + + while !progress_bar.is_finished() { + thread::sleep(time::Duration::from_secs(1)); + let status = device .get_operation_status() .context("Failed to query operation status")?; match status { - nitrokey::OperationStatus::Ongoing(progress) => { - if last_progress != progress { - println!(ctx, "{}/100", progress)?; - } - last_progress = progress; - } - nitrokey::OperationStatus::Idle => break, + nitrokey::OperationStatus::Ongoing(progress) => progress_bar.update(progress)?, + nitrokey::OperationStatus::Idle => progress_bar.finish(), }; + progress_bar.draw(ctx)?; } Ok(()) diff --git a/src/main.rs b/src/main.rs index 1a2a3d3..c0c7da5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -56,6 +56,7 @@ mod arg_util; mod args; mod commands; mod config; +mod output; mod pinentry; #[cfg(test)] mod tests; diff --git a/src/output.rs b/src/output.rs new file mode 100644 index 0000000..bd6c03c --- /dev/null +++ b/src/output.rs @@ -0,0 +1,110 @@ +// output.rs + +// Copyright (C) 2020 The Nitrocli Developers +// SPDX-License-Identifier: GPL-3.0-or-later + +use anyhow::Context as _; + +use progressing::Baring as _; + +use termion::cursor::DetectCursorPos as _; +use termion::raw::IntoRawMode as _; + +use crate::Context; + +/// A progress bar that can be printed to an interactive output. +pub struct ProgressBar { + /// Whether to redraw the entire progress bar in the next call to `draw`. + redraw: bool, + /// The current progress of the progress bar (0 <= progress <= 100). + progress: u8, + /// Toggled on every call to `draw` to print a pulsing indicator. + toggle: bool, + /// Whether this progress bar finished. + finished: bool, +} + +impl ProgressBar { + /// Creates a new empty progress bar. + pub fn new() -> ProgressBar { + ProgressBar { + redraw: true, + progress: 0, + toggle: false, + finished: false, + } + } + + /// Whether this progress bar is finished. + pub fn is_finished(&self) -> bool { + self.finished + } + + /// Updates the progress bar with the given progress (0 <= progress <= 100). + pub fn update(&mut self, progress: u8) -> anyhow::Result<()> { + anyhow::ensure!(!self.finished, "Tried to update finished progress bar"); + anyhow::ensure!( + progress <= 100, + "Progress bar value out of range: {}", + progress + ); + if progress != self.progress { + self.redraw = true; + self.progress = progress; + } + self.toggle = !self.toggle; + Ok(()) + } + + /// Finish this progress bar. + /// + /// A finished progress bar may no longer be updated. + pub fn finish(&mut self) { + self.finished = true; + self.redraw = true; + self.progress = 100; + } + + /// Print the progress bar to the stdout set in the given context. + /// + /// On every call of this method (as long as the progress bar is not + /// finished), a pulsing indicator is printed to show that the process + /// is still running. If there was progress since the last call to + /// `draw`, or if this is the first call, this function will also + /// print the progress bar itself. + pub fn draw(&self, ctx: &mut Context<'_>) -> anyhow::Result<()> { + if !ctx.is_tty { + return Ok(()); + } + + let pos = ctx + .stdout + .into_raw_mode() + .context("Failed to activate raw mode")? + .cursor_pos() + .context("Failed to query cursor position")?; + + let progress_char = if self.toggle && !self.finished { + "." + } else { + " " + }; + + if self.redraw { + let mut progress_bar = progressing::mapping::Bar::with_range(0, 100); + progress_bar.set(self.progress); + + print!(ctx, "{}", termion::clear::CurrentLine)?; + print!(ctx, "{}", termion::cursor::Goto(1, pos.1))?; + print!(ctx, " {} {}", progress_char, progress_bar)?; + if self.finished { + println!(ctx)?; + } + } else { + print!(ctx, "{}{}", termion::cursor::Goto(2, pos.1), progress_char)?; + } + + ctx.stdout.flush()?; + Ok(()) + } +} diff --git a/src/redefine.rs b/src/redefine.rs index dad4529..10fb631 100644 --- a/src/redefine.rs +++ b/src/redefine.rs @@ -14,6 +14,12 @@ macro_rules! println { }; } +macro_rules! print { + ($ctx:expr, $($arg:tt)*) => { + write!($ctx.stdout, $($arg)*) + }; +} + macro_rules! eprintln { ($ctx:expr) => { writeln!($ctx.stderr, "") -- cgit v1.2.1