From 3b1299c1769ebd5d99277b8fd828cba1dc536eda Mon Sep 17 00:00:00 2001 From: Avril Date: Sun, 6 Apr 2025 18:54:31 +0100 Subject: [PATCH] old interface: Added more efficient flow of output `stdout` object(s) through main branches. Internal buffer is flushed into lock before it is dropped in the `-` case, & only the non-locked handle is flushed in the cli-string case. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TODO: We should move this into `master` too really. No reason not to. Fortune for rematch's current commit: Curse − 凶 --- src/main.rs | 71 +++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 63 insertions(+), 8 deletions(-) diff --git a/src/main.rs b/src/main.rs index 893153d..e378694 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,6 +4,52 @@ mod re; mod text; mod args; +/// Run an expression on an named value with a result type `Result`. +/// Where `T` and `U` have *the same API surface* for the duration of the provided expression. +/// +/// # Example +/// If there is a value `let mut value: Result`, where `T: Write` & `U: BufWrite`; +/// the expression `value.flush()` is valid for both `T` and `U`. +/// Therefore, it can be simplified to be called as so: `unwrap_either(mut value => value.flush())`. +/// +/// # Reference capture vs. `move` capture. +/// Note that by default, the identified value is **moved** *into* the expression. +/// The type of reference can be controlled by appending `ref`, `mut`, or `ref mut` to the ident. +/// +/// Identifier capture table: +/// - **none** ~default~ - Capture by move, value is immutable in expression. +/// - `mut` - Capture by move, value is mutable in expression. +/// - `ref` - Capture by ref, value is immutable (`&value`) in expression. +/// - `ref mut` - Capture by mutable ref, value is mutable (`&mut value`) in expression. (__NOTE__: `value` must be defined as mutable to take a mutable reference of it.) +/// +/// Essentially the same rules as any `match` branch pattern. +macro_rules! unwrap_either { + ($res:ident => $($rest:tt)+) => { + match $res { + Ok(ref mut $res) => $($rest)+, + Err(ref mut $res) => $($rest)+, + } + }; + (ref mut $res:ident => $($rest:tt)+) => { + match $res { + Ok(ref mut $res) => $($rest)+, + Err(ref mut $res) => $($rest)+, + } + }; + (ref $res:ident => $($rest:tt)+) => { + match $res { + Ok(ref $res) => $($rest)+, + Err(ref $res) => $($rest)+, + } + }; + (mut $res:ident => $($rest:tt)+) => { + match $res { + Ok(mut $res) => $($rest)+, + Err(mut $res) => $($rest)+, + } + }; +} + use color_eyre::{ eyre::{ self, @@ -175,15 +221,16 @@ fn main() -> eyre::Result<()> .wrap_err("Invalid group index specified")? }; - //TODO: XXX: How to handle multiple groups in `stdin_lines()` case? - //let group = groups[0]; //args[3].parse().expect("Invalid group number."); - use std::io::Write; let mut stdout = std::io::stdout(); - - if &text[..] == "-" { + + // Take the kind of `stdout` used (locked & buffered, or not locked & buffered) .. + let stdout = if &text[..] == "-" { + // Lock the output for the duration of the read lines. + // Buffer the output in program memory to make processing a bit faster (i.e. the segmented 'write' operations in `print_groups()`, which may be called many times here) & not have to wait on write lines for no reason (since we're already waiting on read lines.) let mut stdout = std::io::BufWriter::new(stdout.lock()); - text::stdin_lines(move |text| -> eyre::Result { + + text::stdin_lines(|text| -> eyre::Result { match re.exec(&text)? { Some(g) /*if g.len() > group*/ => // NOTE: This check branch has now been moved into `print_groups()` print_groups(&mut stdout, &g, &groups, &print_cfg)?, //println!("{}", &g[group]), @@ -191,13 +238,21 @@ fn main() -> eyre::Result<()> } Ok(true) })?; + + // Return the buffer to the main block to be flushed to output (see above & below.) + Some(stdout) } else { match re.exec(&text)? { Some(g) /*if g.len() > group*/ => print_groups(&mut stdout, &g[..], &groups, print_cfg)?,//println!("{}", &g.nth(group).unwrap().map(|x| x.as_ref()).unwrap_or("")), _ => (), } - } - stdout.flush().unwrap(); + + // As there is no internal buffer used, there is no reason to pass it to be flushed by the program when finished like the `-` case above. + None + }.ok_or_else(move || stdout); // NOTE ^: Instead, we have it flush the non-memory-buffered output handle before exiting the program. + + // and .. Ensure the stream (and buffer, if used) is flushed (then dropped.) + unwrap_either!(mut stdout => stdout.flush()).unwrap(); } Ok(()) }