aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/commands.rs19
-rw-r--r--src/main.rs1
-rw-r--r--src/output.rs110
-rw-r--r--src/redefine.rs6
4 files changed, 127 insertions, 9 deletions
diff --git a/src/commands.rs b/src/commands.rs
index cf3b94f..7b48bb2 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, "")