aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobin Krahl <robin.krahl@ireas.org>2020-09-10 12:39:07 +0200
committerDaniel Mueller <deso@posteo.net>2020-09-12 14:31:46 -0700
commit57a177e2f946390559a1f17787c5a15d23ac3393 (patch)
treea8069b927bdda75fbceb9e7a289d8338bb19fd8b
parent33918c32b8de4250c450f8d0b007019913c440a1 (diff)
downloadnitrocli-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.
-rw-r--r--Cargo.lock19
-rw-r--r--Cargo.toml3
-rw-r--r--src/commands.rs19
-rw-r--r--src/main.rs1
-rw-r--r--src/output.rs110
-rw-r--r--src/redefine.rs6
6 files changed, 149 insertions, 9 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 5e359d1..94126a2 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -164,6 +164,15 @@ 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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -204,6 +213,7 @@ dependencies = [
"nitrokey",
"nitrokey-test",
"nitrokey-test-state",
+ "progressing",
"regex",
"serde",
"structopt",
@@ -300,6 +310,15 @@ dependencies = [
]
[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"
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, "")