From 8d8f932081be08ba9ca7abd8a6221296259588d2 Mon Sep 17 00:00:00 2001 From: Avril Date: Mon, 24 Mar 2025 17:38:34 +0000 Subject: [PATCH] Started impl of feature `progress-reactive`: Addded `.resize_bar()` (`CommandKind::Resize`) to update progress bar max size to fit recalculated terminal width. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fortune for leanify-many's current commit: Blessing − 吉 --- Cargo.lock | 25 +++++++++++++++---------- Cargo.toml | 10 +++++++--- src/main.rs | 11 +++++++---- src/progress.rs | 38 +++++++++++++++++++++++++++++++++++++- src/work.rs | 10 ++++++++-- 5 files changed, 74 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d9114d5..818588a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,12 +2,6 @@ # It is not intended for manual editing. version = 4 -[[package]] -name = "arc-swap" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d25d88fd6b8041580a654f9d0c581a047baee2b3efee13275f2fc392fc75034" - [[package]] name = "atty" version = "0.2.14" @@ -184,7 +178,7 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "leanify-many" -version = "1.2.0+1" +version = "1.2.1+1" dependencies = [ "cfg-if", "futures", @@ -194,6 +188,8 @@ dependencies = [ "pin-project", "recolored", "rustc_version", + "signal-hook", + "terminal_size", "termprogress", "tokio", ] @@ -401,13 +397,22 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +[[package]] +name = "signal-hook" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" +dependencies = [ + "libc", + "signal-hook-registry", +] + [[package]] name = "signal-hook-registry" -version = "1.2.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e12110bc539e657a646068aaf5eb5b63af9d0c1f7b29c97113fad80e15f035" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" dependencies = [ - "arc-swap", "libc", ] diff --git a/Cargo.toml b/Cargo.toml index 72d58e0..0603e57 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "leanify-many" -version = "1.2.0+1" +version = "1.2.1+1" description = "spawn leanify subprocesses" authors = ["Avril "] edition = "2018" @@ -16,10 +16,10 @@ lto = "fat" codegen-units = 1 [features] -default = ["splash", "progress", "colour", "collect_err", "shutdown"] +default = ["splash", "progress-reactive", "colour", "collect_err", "shutdown"] # Enable progress bar -progress = ["termprogress", "pin-project"] +progress = ["termprogress", "pin-project", "terminal_size"] # Enable threaded scheduler # @@ -48,6 +48,8 @@ collect_err = [] # Without this feature enabled, `leanify` subprocesses will receive terminating `SIGINT`s as normal. shutdown = ["libc"] +# TODO: Implement this to: Capture `SIGWINCH` events and re-size + re-render the progress bar to the new terminal width. (XXX: Use a background thread (outside the thread-pool, as it's blocking) listening on `signal_hooks::Signals.forever()` for this that sends events through a shared Tokio `CondVar` notify_all() call.) +progress-reactive = ["progress", "tokio/signal", "signal-hook"] [dependencies] lazy_static = "1.4" @@ -59,6 +61,8 @@ recolored = { version = "1.9", optional = true } num_cpus = "1.13" pin-project = {version = "0.4", optional = true} libc = { version = "0.2.171", features = ["align"], optional = true } +terminal_size = { version = "^0.1.13", optional = true } +signal-hook = { version = "0.3.17", features = ["iterator"], optional = true } [build-dependencies] rustc_version = "0.2" diff --git a/src/main.rs b/src/main.rs index e09cbd2..093afe8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -31,9 +31,8 @@ mod maybe_single; #[cfg(feature="progress")] mod task_list; #[cfg(feature="progress")] mod progress; -async fn work() -> Result<(), Box> +async fn work() -> Result, Box> { - //println!("umm {}", colour::style(colour!(Color::Blue), "hiii")); let args = arg::parse_args().await.with_prefix("failed to parse args")?; let leanify = leanify::find_binary().with_prefix("Couldn't find leanify binary")?; @@ -85,13 +84,17 @@ async fn work() -> Result<(), Box> } }, _ => args.max_children, - }, get_shutdown_future(&args.flags).fuse()).await + }, get_shutdown_future(&args.flags).fuse()).await + .map(|_| None) //TODO: Can the shutdown future set an `is_interrupted` var we can use to return a non-zero exit code from a SIGINT graceful shutdown here (e.g. `is_interrupted.then_some(1)`)? Do something like this... } #[tokio::main] async fn main() { - prettify_expect(work().await.map_err(|e| e.to_string()), "exited with error"); + if let Some(code) = prettify_expect(work().await.map_err(|e| e.to_string()), "exited with error") { + // Exiting with error (non-zero) code + std::process::exit(code.get()); + } } #[inline] fn prettify_expect(res: Result, msg: S) -> T diff --git a/src/progress.rs b/src/progress.rs index 55967bc..d88f889 100644 --- a/src/progress.rs +++ b/src/progress.rs @@ -19,6 +19,7 @@ use std::{ self, Once, }, + num::NonZeroUsize, }; use tokio::{ sync::{ @@ -68,6 +69,10 @@ pub enum CommandKind Complete, + Resize { + to: Option, + }, + Many(Vec), } @@ -312,6 +317,15 @@ impl ProgressSender self.send_command(CommandKind::BumpLow(by)).await } + /// Resize the whole bar to either a specific size, or to query the terminal size. + /// + /// Currently, the terminal size is grabbed from `stdout` or `stderr` if possible. + /// If `None` is passed and the output of `stdout` *and* `stderr` are not TTYs, no resize will take place. + pub async fn resize_bar(&mut self, to: Option) -> Result + { + self.send_command(CommandKind::Resize { to }).await + } + /// Add a task to the worker's progress bar title line /// /// This function returns a [TaskWaiter]`TaskWaiter` future, upon successful `await`ing will yield the task's ID. @@ -382,7 +396,6 @@ pub fn create_progress { + use std::os::fd::{ + AsFd, + AsRawFd, + }; + + if let Some((w, _)) = terminal_size::terminal_size() + .or_else(|| terminal_size::terminal_size_using_fd(std::io::stderr().as_fd().as_raw_fd())) + { + // Blank the line, and tell to redraw after the dimension update. + progress.blank(); + has_blanked = true; + + progress.update_dimensions(w.0 as usize); + } + }, + CommandKind::Resize { to: Some(to) } => { + // Blank the line, and tell to redraw after the dimension update. + progress.blank(); + has_blanked = true; + + progress.update_dimensions(to.get()); + }, CommandKind::BumpHigh(high) => { let stat = stat.to_mut(); stat.high+=high; diff --git a/src/work.rs b/src/work.rs index 2a9519d..efb0bcd 100644 --- a/src/work.rs +++ b/src/work.rs @@ -143,6 +143,12 @@ where I: IntoIterator, progress::create_progress::(files.len(), iter::empty()) } }; + + #[cfg(feature="progress-reactive")] + let _size_handle = { + let mut progress = progress.clone(); + todo!("progress-reactive: Spawn the `signal_hooks` thread and set up the Tokio `CondVar` to wait on in a background task, sending `Resize{None} to `progress` when an event comes in."); + }; let display = { #[cfg(feature="progress")] let mut progress = progress.clone(); @@ -191,7 +197,7 @@ where I: IntoIterator, let process = Arc::clone(&process); let mut tx = tx.clone(); - let flags = flags.clone(); + let flags = flags.clone(); // XXX: Can we remove this clone somehow? It's kinda big this structure and we only need to do this because of not being able to exclude 'cli from `+ use<>` in the `async fn do_work()` as it's an `async fn` and not a `fn() -> impl Future`... #[cfg(feature="progress")] let mut progress = progress.clone(); @@ -288,7 +294,7 @@ where I: IntoIterator, Ok(v) => Box::new(v), }; - for failed in results + for failed in results { #[cfg(feature="progress")] progress.eprintln(format!("[{}] Child panic {:?}", colour::style(colour!(Color::BrightRed), "e"), failed)).await?.await?; #[cfg(not(feature="progress"))] eprintln!("[{}] Child panic {:?}", colour::style(colour!(Color::BrightRed), "e"), failed);