Compare commits

...

2 Commits

Author SHA1 Message Date
Avril 3b1299c176
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.
1 month ago
Avril 765270feaf
Remove unneccisary multi-`stdout` locking. Added internal buffering.
1 month ago

@ -4,6 +4,52 @@ mod re;
mod text; mod text;
mod args; mod args;
/// Run an expression on an named value with a result type `Result<T, U>`.
/// 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<T, U>`, 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::{ use color_eyre::{
eyre::{ eyre::{
self, self,
@ -90,8 +136,8 @@ impl<'a> GroupConfig<'a>
} }
#[inline] #[inline]
fn print_groups<'c, 'a, S: ?Sized, G, T: 'a, I>(to: &mut S, g: G, groups: I, how: GroupConfig<'c>) -> std::io::Result<()> fn print_groups<'c, 'a, S: ?Sized, G, T: 'a, I>(to: &mut S, g: G, groups: I, how: impl std::borrow::Borrow<GroupConfig<'c>>) -> std::io::Result<()>
where S: std::io::Write, where S: std::io::Write + 'c, // NOTE: This lifetime bound is not yet used, as it is just `Write`, but if we change this to a context wrapper, then we can copy the `how`'s `'c` references into the context object without direct write/format/cloning.
G: IntoIterator<Item = &'a Option<T>> + Clone + Copy, // NOTE: Copy bound to ensure we're not accidentally doing deep clones of `g`. G: IntoIterator<Item = &'a Option<T>> + Clone + Copy, // NOTE: Copy bound to ensure we're not accidentally doing deep clones of `g`.
//G: std::ops::Index<usize>, G::Output: std::borrow::Borrow<Option<T>>, //G: std::ops::Index<usize>, G::Output: std::borrow::Borrow<Option<T>>,
T: std::borrow::Borrow<str>, T: std::borrow::Borrow<str>,
@ -101,6 +147,7 @@ where S: std::io::Write,
borrow::Borrow, borrow::Borrow,
io::Write, io::Write,
}; };
let how = how.borrow(); //std::borrow::ToOwned::clone_into(&self, target);
let mut first = true; let mut first = true;
for group in groups.into_iter() { for group in groups.into_iter() {
let group = group.borrow(); let group = group.borrow();
@ -128,7 +175,7 @@ where S: std::io::Write,
first = false; first = false;
} }
// If `first == true`, no groups were printed, so we do not print the new-line. // If `first == true`, no groups were printed, so we do not print the new-line.
if !first && !how.line_delimiter.is_empty() { if !first && how.has_line_delimiter() {
to.write_all(how.line_delimiter.as_ref()) to.write_all(how.line_delimiter.as_ref())
} else { } else {
Ok(()) Ok(())
@ -174,29 +221,38 @@ fn main() -> eyre::Result<()>
.wrap_err("Invalid group index specified")? .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; use std::io::Write;
let mut stdout = std::io::stdout(); 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(|text| -> eyre::Result<bool> { text::stdin_lines(|text| -> eyre::Result<bool> {
let mut stdout = stdout.lock();
match re.exec(&text)? { match re.exec(&text)? {
Some(g) /*if g.len() > group*/ => // NOTE: This check branch has now been moved into `print_groups()` 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.clone())?, //println!("{}", &g[group]), print_groups(&mut stdout, &g, &groups, &print_cfg)?, //println!("{}", &g[group]),
_ => (), _ => (),
} }
Ok(true) Ok(true)
})?; })?;
// Return the buffer to the main block to be flushed to output (see above & below.)
Some(stdout)
} else { } else {
match re.exec(&text)? { 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("")), 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(()) Ok(())
} }

Loading…
Cancel
Save