use std::io::{BufRead, BufReader, Read, Write};
#[cfg(feature = "debug")]
use std::time::Instant;
use crate::grc::GrcatConfigEntry;
use crate::style::Style;
#[allow(dead_code)] pub fn colorize_regex<R, W>(
reader: &mut R,
writer: &mut W,
rules: &[GrcatConfigEntry],
) -> Result<(), Box<dyn std::error::Error>>
where
R: Read,
W: Write,
{
#[cfg(feature = "debug")]
let record_time = std::env::var_os("RGRCTIME").is_some();
#[cfg(feature = "debug")]
let overall_start = if record_time {
Some(Instant::now())
} else {
None
};
#[cfg(feature = "debug")]
let mut lines_processed: usize = 0;
let reader = BufReader::new(reader).lines();
if rules.is_empty() {
for line in reader {
writeln!(writer, "{}", line?)?;
}
return Ok(());
}
let default_style = Style::new();
for line in reader {
let mut line = line?;
#[cfg(feature = "debug")]
if record_time {
lines_processed += 1;
}
if line.is_empty() {
writeln!(writer)?;
continue;
}
let mut style_ranges: Vec<(usize, usize, &Style)> = Vec::new();
let mut stop_line_processing = false;
'outer_loop: for rule in rules {
if rule.skip {
continue;
}
if stop_line_processing {
break;
}
let mut offset = 0;
let mut last_end = 0;
let mut rule_matched_once = false;
while offset < line.len() && !rule_matched_once {
if offset < last_end {
offset = last_end;
continue;
}
if let Some(matches) = rule.regex.captures_from_pos(&line, offset) {
for (i, mmatch) in matches.iter().into_iter().enumerate() {
if let Some(mmatch) = mmatch {
let start = mmatch.start();
let end = mmatch.end();
if i < rule.colors.len() {
let style = &rule.colors[i];
style_ranges.push((start, end, style));
last_end = last_end.max(end);
}
}
let full_match = matches.get(0).unwrap();
if !rule.replace.is_empty() {
let mut replacement = rule.replace.clone();
for (i, capture) in matches.iter().into_iter().enumerate() {
if let Some(capture_match) = capture {
let capture_text =
&line[capture_match.start()..capture_match.end()];
let placeholder = format!("\\{}", i);
replacement = replacement.replace(&placeholder, capture_text);
}
}
let before = &line[..full_match.start()];
let after = &line[full_match.end()..];
line = format!("{}{}{}", before, replacement, after);
break 'outer_loop;
}
match rule.count {
crate::grc::GrcatConfigEntryCount::Once => {
rule_matched_once = true;
}
crate::grc::GrcatConfigEntryCount::More => {
}
crate::grc::GrcatConfigEntryCount::Stop => {
stop_line_processing = true;
rule_matched_once = true;
}
}
}
let full_match = matches.get(0).unwrap();
if full_match.end() > full_match.start() {
offset = full_match.end();
} else {
offset = full_match.end() + 1;
}
} else {
break;
}
}
}
if style_ranges.is_empty() {
writeln!(writer, "{}", line)?;
continue;
}
let mut char_styles: Vec<&Style> = vec![&default_style; line.len()];
for (start, end, style) in style_ranges {
for item in char_styles.iter_mut().take(end.min(line.len())).skip(start) {
*item = style;
}
}
let mut prev_style = &default_style;
let mut offset = 0;
for i in 0..line.len() {
let this_style = char_styles[i];
if this_style != prev_style {
if i > 0 {
write!(writer, "{}", prev_style.apply_to(&line[offset..i]))?;
}
prev_style = this_style;
offset = i;
}
}
if offset < line.len() {
write!(writer, "{}", prev_style.apply_to(&line[offset..]))?;
}
writeln!(writer)?;
}
#[cfg(feature = "debug")]
if let Some(s) = overall_start.filter(|_| record_time) {
eprintln!(
"[rgrc:time] colorizer total processed {} lines in {:?}",
lines_processed,
s.elapsed()
);
}
Ok(())
}