diff options
author | Robin Krahl <robin.krahl@ireas.org> | 2020-09-10 12:39:07 +0200 |
---|---|---|
committer | Daniel Mueller <deso@posteo.net> | 2020-09-12 14:31:46 -0700 |
commit | 57a177e2f946390559a1f17787c5a15d23ac3393 (patch) | |
tree | a8069b927bdda75fbceb9e7a289d8338bb19fd8b /src | |
parent | 33918c32b8de4250c450f8d0b007019913c440a1 (diff) | |
download | nitrocli-57a177e2f946390559a1f17787c5a15d23ac3393.tar.gz nitrocli-57a177e2f946390559a1f17787c5a15d23ac3393.tar.bz2 |
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.
Diffstat (limited to 'src')
-rw-r--r-- | src/commands.rs | 19 | ||||
-rw-r--r-- | src/main.rs | 1 | ||||
-rw-r--r-- | src/output.rs | 110 | ||||
-rw-r--r-- | src/redefine.rs | 6 |
4 files changed, 127 insertions, 9 deletions
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, "") |