Added fully-functional (default) feature `progress-reactive`.

Fortune for leanify-many's current commit: Blessing − 吉
safe-cancel-interrupt
Avril 5 days ago
parent ad03ce86e8
commit aaaddd66ec
Signed by: flanchan
GPG Key ID: 284488987C31F630

2
Cargo.lock generated

@ -178,7 +178,7 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "leanify-many"
version = "1.2.1+1"
version = "1.2.1+2"
dependencies = [
"cfg-if",
"futures",

@ -1,6 +1,6 @@
[package]
name = "leanify-many"
version = "1.2.1+1"
version = "1.2.1+2"
description = "spawn leanify subprocesses"
authors = ["Avril <flanchan@cumallover.me>"]
edition = "2018"
@ -48,7 +48,7 @@ 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<TerminalWidth>` notify_all() call.)
# Capture `SIGWINCH` events and re-size + re-render the progress bar to the new terminal width when appropriate.
progress-reactive = ["progress", "tokio/signal", "signal-hook", "terminal_size"]
[dependencies]

@ -28,6 +28,7 @@ mod extra {
#[inline] fn extra_args<W: Write + ?Sized>(#[allow(unused_variables)] output: &mut W) -> fmt::Result
{
#[cfg(feature="progress")] writeln!(output, " --no-progress Do not display progress bar")?;
#[cfg(feature="progress-reactive")] writeln!(output, " --static-progress Do not dynamically resize the progress bar")?;
#[cfg(feature="colour")] writeln!(output, " --no-colour Do not display terminal colours")?;
#[cfg(feature="colour")] writeln!(output, " --colour Always display terminal colour, even if env flags tell us not to")?;
#[cfg(feature="shutdown")] writeln!(output, " -n, --no-cancel Do not capture `SIGINT`s for graceful shutdown.")?;
@ -163,6 +164,9 @@ fn comp_flags()
check!(on "splash", "Show splash-screen");
check!(on "colour", "Enable coloured output");
check!(on "progress", "Enable progress bar");
if cfg!(feature="progress") {
check!(on "progress-reactive", "Enable progress bar to reactively-resize to terminal width when changed.");
}
check!(on "collect_err", "Collect the output of children's stderr instead of printing immediately");
check!(off "threads", "Enable threaded scheduler (usually not needed)");
check!(off "checked_pass", "Check the arguments passed with `--passthrough` to leanify. By default they are passed as is");
@ -258,6 +262,8 @@ pub struct Flags
{
/// Display the progress bar
#[cfg(feature="progress")] pub progress: bool,
/// Allow the dynamic reactive resizing of the progress bar
#[cfg(feature="progress-reactive")] pub progress_reactive: bool,
/// Force use of colour
#[cfg(feature="colour")] pub coloured: Option<bool>,
/// Limit max children to this number
@ -268,6 +274,20 @@ pub struct Flags
#[cfg(feature="shutdown")] pub graceful_shutdown: bool,
}
impl Flags
{
/// Should we watch for `SIGWINCH` to dynamically resize the progress bar?
///
/// This will return false if there is either: not an output window that can be resized, no rendered progress bar (specified by user,) no dynamic-resize of progress bar (specified by user,) or the `progress-reactive` feature was not enabled at build-time.
#[inline]
pub fn watch_sigwinch(&self) -> bool
{
#![allow(unreachable_code)]
#[cfg(feature="progress-reactive")] return self.progress && self.progress_reactive && (util::is_terminal(&std::io::stdout()) || util::is_terminal(&std::io::stderr()));
false
}
}
impl Default for Flags
{
#[inline]
@ -279,6 +299,7 @@ impl Default for Flags
hard_limit: None,
leanify_flags: Default::default(),
#[cfg(feature="shutdown")] graceful_shutdown: true,
#[cfg(feature="progress-reactive")] progress_reactive: true,
}
}
}
@ -364,6 +385,11 @@ where I: IntoIterator<Item=T>,
continue;
}
},
#[cfg(feature="progress-reactive")]
"--static-progress" => {
cfg.flags.progress_reactive = false;
continue;
},
#[cfg(feature="progress")]
"--no-progress" => {
cfg.flags.progress = false;

@ -24,3 +24,20 @@ where I: Iterator<Item=T>,
output
}
}
/// Explicit utilities
pub mod util {
use crate::*;
use std::os::fd::*;
/// Check if `stream` is open to a tty.
pub fn is_terminal<T: ?Sized + AsFd>(stream: &T) -> bool
{
use std::ffi::c_int;
unsafe extern "C" {
safe fn isatty(fd: c_int) -> c_int;
}
isatty(stream.as_fd().as_raw_fd()) == 1
}
}

@ -9,6 +9,7 @@ use cfg_if::cfg_if;
mod defer;
mod ext;
pub use ext::JoinStrsExt as _;
pub use ext::util;
#[cfg(feature="splash")]
mod splash;

@ -144,12 +144,43 @@ where I: IntoIterator<Item=T>,
}
};
// To close the backing handle and un-hook the signals: `close_resize_handle().await.expect("Failed to close reactive handle")`
/// Coerce a potentially-`None` thunk's return type into an `Option`, so that `Option<Fn() -> T>` becomes `Fn() -> Option<T>`.
///
/// # `Option` mapping
/// Example usage would be `maybe_func!(if flag { Some(some_thunk_expr) } else { None })` will map the return type to `Option`, creating a thunk with the same signature as `some_thunk_expr` but an `Option`-wrapped return type.
///
/// ## Internal type-coercion
/// The return-type can also be coerced itself via `maybe_func!(U: option_thunk_expression)` (where `U: From<Option<T>>`,) creating `for<U: From<Option<T>>> Option<Fn() -> T> -> Fn() -> Option<U>`.
macro_rules! maybe_func {
($type:ty: $func:expr) => {{
let func = $func;
move || -> $type {
if let Some(func) = func {
Some(func()).into()
} else {
None.into()
}
}
}};
($func:expr) => {{
let func = $func;
move || {
if let Some(func) = func {
Some(func())
} else {
None
}
}
}}
}
// To close the backing handle and un-hook the signals: `close_resize_handle().await[.expect("Failed to close reactive handle")]`
// NOTE: This must be called *before* shutting down the progress-bar.
// The return type is coerced to `Option<io::Result<()>>` (`None` is for if `flags.sigwinch()` is false.)
#[cfg(feature="progress-reactive")]
let close_resize_handle = {
let close_resize_handle = maybe_func!(futures::future::OptionFuture<_>: if flags.watch_sigwinch() {
let mut progress = progress.clone();
let (rx, handle) = progress::reactive::spawn_signal_watcher([signal_hook::consts::signal::SIGWINCH])?;
let raw_handle = handle.signal_handle().clone();
@ -157,7 +188,7 @@ where I: IntoIterator<Item=T>,
use futures::stream::StreamExt;
let _on_exit = defer::Defer::new(move || raw_handle.close());
let rx = rx.into_stream();
futures::pin_mut!(rx);
@ -177,14 +208,16 @@ where I: IntoIterator<Item=T>,
}
});
use std::io;
async move || -> io::Result<()> {
Some(async move || -> io::Result<()> {
if handle.close()? == false {
return Err(io::Error::new(io::ErrorKind::BrokenPipe, "The `resize_handle` task has already exited before it was requested to (BUG: This is not a problem, but means the resize-handle has not been closed in the right order in the bringdown code.)"));
}
rx.await?;
Ok(())
}
};
})
} else {
None
});
let display = {
#[cfg(feature="progress")] let mut progress = progress.clone();
@ -336,7 +369,7 @@ where I: IntoIterator<Item=T>,
#[cfg(not(feature="progress"))] eprintln!("[{}] Child panic {:?}", colour::style(colour!(Color::BrightRed), "e"), failed);
}
#[cfg(feature="progress-reactive")] {
if let Err(e) = close_resize_handle().await {
if let Some(Err(e)) = close_resize_handle().await {
let _ = progress.eprintln(format!("[{}] Warning! Failed to close progress-reactive resize handle: {:?}", colour::style(colour!(Color::Yellow),"!"), e)).await;
}
};

Loading…
Cancel
Save