You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
173 lines
5.9 KiB
173 lines
5.9 KiB
#![allow(dead_code)]
|
|
|
|
mod re;
|
|
mod text;
|
|
|
|
/// 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::{
|
|
eyre::{
|
|
self,
|
|
eyre,
|
|
WrapErr as _,
|
|
},
|
|
SectionExt as _, Help as _,
|
|
};
|
|
|
|
fn initialise() -> eyre::Result<()>
|
|
{
|
|
color_eyre::install()?;
|
|
Ok(())
|
|
}
|
|
|
|
#[inline]
|
|
fn print_groups<'a, S: ?Sized, G, T: 'a, I>(to: &mut S, g: G, groups: I) -> std::io::Result<()>
|
|
where S: std::io::Write,
|
|
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>>,
|
|
T: std::borrow::Borrow<str>,
|
|
I: IntoIterator<Item: std::borrow::Borrow<usize>/*, IntoIter: ExactSizeIterator*/>,
|
|
{
|
|
use std::borrow::Borrow;
|
|
let mut first = true;
|
|
for group in groups.into_iter() {
|
|
let group = group.borrow();
|
|
// // Moved to into match group (skipping invalid groups.)
|
|
// if !first {
|
|
// write!(to, "\t")?;
|
|
// }
|
|
let print_delim = || first.then_some("").unwrap_or("\t"); // If it's not the first iteration, print `\t`.
|
|
match g.into_iter().nth(*group) {
|
|
Some(None) => write!(to, "{}", print_delim()),
|
|
Some(Some(g)) => write!(to, "{}{}", print_delim(), g.borrow()),
|
|
|
|
//TODO: What should be the behaviour of a non-existent group index here? (NOTE: This now corresponds to the previous `g.len() > group` check in caller.) // (NOTE: The original behaviour is to just ignore groups that are out of range entirely (i.e. no printing, no delimit char, no error,) maybe treat non-existent groups as non-matched groups and *just* print the delim char?)
|
|
// (NOTE: Moved out of branch, see above ^) // None if !first => write!(to, "\t"),
|
|
// XXX: Should this do what it does now...? Or should it `break` to prevent the checking for more groups...? Print a warning maybe...?
|
|
None => {
|
|
eprintln!("Warning: Invalid group index {}!", group);
|
|
continue; // Do not set `first = false` if it was an invalid index.
|
|
//Ok(())
|
|
},
|
|
}?;
|
|
first = false;
|
|
}
|
|
// If `first == true`, no groups were printed, so we do not print the new-line.
|
|
if !first {
|
|
to.write_all(b"\n")
|
|
} else {
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
fn main() -> eyre::Result<()>
|
|
{
|
|
initialise().wrap_err("Fatal: Failed to install panic handle")?;
|
|
|
|
//let cli = args::parse_cli();//.wrap_err("Error parsing command-line arguments")?;
|
|
|
|
//eprintln!("{:#?}", cli);
|
|
// return Ok(());
|
|
|
|
let args: re::FrozenVec<re::FrozenString> = std::env::args().map(String::into_boxed_str).collect();
|
|
|
|
if args.len() < 4 {
|
|
println!("Usage: {} <str> <regex> <group>...", args[0]);
|
|
println!("Pass `-' as `<str>' to read lines from stdin");
|
|
std::process::exit(1)
|
|
} else {
|
|
let re = re::Regex::compile(&args[2])?;
|
|
let text = &args[1];
|
|
|
|
let groups = &args[3..];
|
|
|
|
if groups.len() < 1 {
|
|
eprintln!("Warning: No capture groups requested.");
|
|
// NOTE: Unexpected branch...
|
|
return Ok(());
|
|
}
|
|
|
|
let groups = groups.iter().enumerate()
|
|
.map(|(i, x)| x.parse()
|
|
.with_section(|| format!("{:?}", groups).header("Groups specified were"))
|
|
.with_section(|| x.clone().header("Specified capture group index was"))
|
|
.with_section(move || i.header("Argument index in provided groups")))
|
|
.collect::<Result<Box<[usize]>, _>>()
|
|
.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();
|
|
|
|
let stdout = if &text[..] == "-" {
|
|
let mut stdout = std::io::BufWriter::new(stdout.lock());
|
|
text::stdin_lines(|text| -> eyre::Result<bool> {
|
|
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)?, //println!("{}", &g[group]),
|
|
_ => (),
|
|
}
|
|
Ok(true)
|
|
})?;
|
|
|
|
Some(stdout)
|
|
} else {
|
|
|
|
match re.exec(&text)? {
|
|
Some(g) /*if g.len() > group*/ => print_groups(&mut stdout, &g[..], &groups)?,//println!("{}", &g.nth(group).unwrap().map(|x| x.as_ref()).unwrap_or("")),
|
|
_ => (),
|
|
}
|
|
|
|
None
|
|
}.ok_or_else(move || stdout);
|
|
unwrap_either!(mut stdout => stdout.flush()).unwrap();
|
|
}
|
|
Ok(())
|
|
}
|