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.
rematch/src/main.rs

154 lines
5.5 KiB

#![allow(dead_code)]
mod re;
mod text;
mod ext; use ext::*;
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 {
use owo_colors::OwoColorize;
use owo_colors::Stream;
macro_rules! colour {
(in $name:ident: $fmt:expr => $col:ident) => {
$fmt.if_supports_color(Stream::$name, |text| text.$col())
};
($fmt:expr => $col:ident) => {
colour!(in Stdout: $fmt => $col)
}
}
println!("rematch v{}: Regular-expression group matcher", env!("CARGO_PKG_VERSION"));
println!("");
println!("Usage: {} <str> <regex> <group>...", args[0]);
println!("Pass `-' as `<str>' to read lines from stdin");
println!("");
println!("Enabled Features:");
if cfg!(feature="perl") {
println!("{}\t\t\tEnable PCRE2 (extended) regular-expressions.\n\t\t\tNote that PCRE2 regex engine matches on *bytes*, not *characters*; meaning if a match cuts a vlid UTF8 codepoint into an invalid one, the output will replace the invalid characters with U+FFFD REPLACEMENT CHARACTER.", colour!(disjoint!["+", "perl"] => bright_red));
} else {
println!("{}\t\t\tPCRE2 (extended) features are disabled; a faster but less featureful regular expression engine (that matches on UTF8 strings instead of raw bytes) is used instead.", colour!(disjoint!["-", "perl"] => blue));
}
if cfg!(feature="unstable") {
println!("{}\t\tUnstable optimisations evailable & enabled for build.", colour!(disjoint!["+", "unstable"] => red));
} else {
println!("{}\t\tUnstable optimisations disabled / not available for build.", colour!(disjoint!["-", "unstable"] => bright_blue));
}
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(())
}