From 4539f875280ad6fab8cbaf1cc7a935b0726c8594 Mon Sep 17 00:00:00 2001 From: Avril Date: Sun, 6 Apr 2025 18:18:16 +0100 Subject: [PATCH] Added `GroupConfig` argument to `print_groups()`: Configurable group printing delimiters (default: Field delim (specified groups) -literal, Line delim (input strings) -literal.) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is to allow for supporting `-0` / `--field-delim=`-like cli flags. Fortune for rematch's current commit: Half blessing − 半吉 --- Cargo.toml | 2 +- src/main.rs | 127 ++++++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 104 insertions(+), 25 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 203615c..fdc9ed3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rematch" -version = "1.1.0" +version = "1.1.0+1" authors = ["Avril "] edition = "2024" diff --git a/src/main.rs b/src/main.rs index 9b60b7f..91daf9e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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> + Clone + Copy, // NOTE: Copy bound to ensure we're not accidentally doing deep clones of `g`. //G: std::ops::Index, G::Output: std::borrow::Borrow>, T: std::borrow::Borrow, I: IntoIterator/*, 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(()); - } - - 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::, _>>() - .wrap_err("Invalid group index specified")?; + if groups.len() < 1 { + eprintln!("Warning: No capture groups requested."); + // NOTE: Unexpected branch... + return Ok(()); + } + + // 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::, _>>() + .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("")), _ => (), } }