@ -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 ]
fn print_groups < ' a , S : ? Sized , G , T : ' a , I > ( to : & mut S , g : G , groups : I ) -> std ::io ::Result < ( ) >
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 < ' 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,6 +154,8 @@ fn main() -> eyre::Result<()>
let re = re ::Regex ::compile ( & args [ 2 ] ) ? ;
let text = & args [ 1 ] ;
let print_cfg = GroupConfig ::new ( ) ;
let groups = {
let groups = & args [ 3 .. ] ;
if groups . len ( ) < 1 {
@ -86,13 +164,15 @@ fn main() -> eyre::Result<()>
return Ok ( ( ) ) ;
}
let groups = groups . iter ( ) . enumerate ( )
// 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" ) ? ;
. 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("")),
_ = > ( ) ,
}
}