Added `GroupConfig` argument to `print_groups()`: Configurable group printing delimiters (default: Field delim (specified groups) <TAB>-literal, Line delim (input strings) <LF>-literal.)

This is to allow for supporting `-0` / `--field-delim=`-like cli flags.

Fortune for rematch's current commit: Half blessing − 半吉
old-interface-extra-help-info
Avril 2 weeks ago
parent e6c0714575
commit 4539f87528
Signed by: flanchan
GPG Key ID: 284488987C31F630

@ -1,6 +1,6 @@
[package]
name = "rematch"
version = "1.1.0"
version = "1.1.0+1"
authors = ["Avril <flanchan@cumallover.me>"]
edition = "2024"

@ -19,15 +19,88 @@ fn initialise() -> eyre::Result<()>
Ok(())
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord/*, Copy*/)]
pub struct GroupConfig<'a>
{
pub line_delimiter: &'a [u8],
pub field_delimiter: &'a [u8],
}
impl<'a> Default for GroupConfig<'a>
{
#[inline]
fn default() -> Self
{
Self::new()
}
}
impl<'a> GroupConfig<'a>
{
pub const fn new() -> Self {
Self {
line_delimiter: b"\n",
field_delimiter: b"\t",
}
}
pub const fn has_line_delimiter(&self) -> bool {
! self.line_delimiter.is_empty()
}
pub const fn has_field_delimiter(&self) -> bool {
! self.field_delimiter.is_empty()
}
#[inline]
pub const fn with_field_delimiter<'b>(self, field_delimiter: &'b [u8]) -> GroupConfig<'b>
where 'a: 'b
{
GroupConfig {
field_delimiter,
..self
}
}
#[inline]
pub const fn with_field_delimiter_str<'b>(self, field: &'b str) -> GroupConfig<'b>
where 'a: 'b
{
GroupConfig {
field_delimiter: field.as_bytes(),
..self
}
}
#[inline]
pub const fn with_line_delimiter<'b>(self, line_delimiter: &'b [u8]) -> GroupConfig<'b>
where 'a: 'b
{
GroupConfig {
line_delimiter,
..self
}
}
#[inline]
pub const fn with_line_delimiter_str<'b>(self, line: &'b str) -> GroupConfig<'b>
where 'a: 'b
{
GroupConfig {
line_delimiter: line.as_bytes(),
..self
}
}
}
#[inline]
fn print_groups<'a, S: ?Sized, G, T: 'a, I>(to: &mut S, g: G, groups: I) -> std::io::Result<()>
fn print_groups<'c, 'a, S: ?Sized, G, T: 'a, I>(to: &mut S, g: G, groups: I, how: GroupConfig<'c>) -> 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;
use std::{
borrow::Borrow,
io::Write,
};
let mut first = true;
for group in groups.into_iter() {
let group = group.borrow();
@ -35,10 +108,13 @@ where S: std::io::Write,
// if !first {
// write!(to, "\t")?;
// }
let print_delim = || first.then_some("").unwrap_or("\t"); // If it's not the first iteration, print `\t`.
let print_delim = move |to: &mut S| to.write_all(first.then_some(&[][..]).unwrap_or(&how.field_delimiter[..]).as_ref()); // 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()),
Some(None) => print_delim(to),
Some(Some(g)) => {
print_delim(to)?;
write!(to, "{}", 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"),
@ -52,8 +128,8 @@ where S: std::io::Write,
first = false;
}
// If `first == true`, no groups were printed, so we do not print the new-line.
if !first {
to.write_all(b"\n")
if !first && !how.line_delimiter.is_empty() {
to.write_all(how.line_delimiter.as_ref())
} else {
Ok(())
}
@ -78,21 +154,25 @@ fn main() -> eyre::Result<()>
let re = re::Regex::compile(&args[2])?;
let text = &args[1];
let groups = &args[3..];
let print_cfg = GroupConfig::new();
let groups = {
let groups = &args[3..];
if groups.len() < 1 {
eprintln!("Warning: No capture groups requested.");
// NOTE: Unexpected branch...
return Ok(());
}
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")?;
// Parse each group index into `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.");
@ -105,15 +185,14 @@ fn main() -> eyre::Result<()>
let mut stdout = stdout.lock();
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]),
print_groups(&mut stdout, &g, &groups, print_cfg.clone())?, //println!("{}", &g[group]),
_ => (),
}
Ok(true)
})?;
} 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("")),
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("")),
_ => (),
}
}

Loading…
Cancel
Save