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. --- src/output.rs | 110 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 src/output.rs (limited to 'src/output.rs') 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(()) + } +} -- cgit v1.2.1