use regex::Regex;
use std::collections::{HashMap, HashSet};
use std::fs::File;
use std::io::Write;
use tempfile::tempdir;
fn search_file_with_combined_pattern(
file_path: &std::path::Path,
combined_regex: ®ex::Regex,
pattern_to_terms: &[HashSet<usize>],
) -> anyhow::Result<HashMap<usize, HashSet<usize>>> {
let mut term_map = HashMap::new();
let content = std::fs::read_to_string(file_path)?;
for (line_number, line) in content.lines().enumerate() {
if line.len() > 2000 {
continue;
}
for cap in combined_regex.captures_iter(line) {
for i in 1..=pattern_to_terms.len() {
if cap.get(i).is_some() {
let pattern_idx = i - 1;
for &term_idx in &pattern_to_terms[pattern_idx] {
term_map
.entry(term_idx)
.or_insert_with(HashSet::new)
.insert(line_number + 1); }
}
}
}
}
Ok(term_map)
}
#[test]
fn test_multiple_capture_groups_in_line() {
let temp_dir = tempdir().unwrap();
let file_path = temp_dir.path().join("test_file.txt");
let mut file = File::create(&file_path).unwrap();
writeln!(file, "This line contains pattern1 and also pattern2").unwrap();
writeln!(file, "This line only has pattern1").unwrap();
writeln!(file, "This line only has pattern2").unwrap();
writeln!(file, "This line has neither pattern").unwrap();
writeln!(file, "pattern1 pattern2 pattern1 pattern2").unwrap();
let pattern1 = "pattern1";
let pattern2 = "pattern2";
let combined_pattern = format!("({pattern1})|({pattern2}))").replace("))", ")");
let combined_regex = Regex::new(&format!("(?i){combined_pattern}")).unwrap();
let mut pattern1_terms = HashSet::new();
pattern1_terms.insert(0);
let mut pattern2_terms = HashSet::new();
pattern2_terms.insert(1);
let pattern_to_terms = vec![pattern1_terms, pattern2_terms];
let result =
search_file_with_combined_pattern(&file_path, &combined_regex, &pattern_to_terms).unwrap();
let term0_lines = result.get(&0).unwrap();
assert!(term0_lines.contains(&1));
assert!(term0_lines.contains(&2));
assert!(!term0_lines.contains(&3));
assert!(!term0_lines.contains(&4));
assert!(term0_lines.contains(&5));
let term1_lines = result.get(&1).unwrap();
assert!(term1_lines.contains(&1));
assert!(!term1_lines.contains(&2));
assert!(term1_lines.contains(&3));
assert!(!term1_lines.contains(&4));
assert!(term1_lines.contains(&5));
assert!(term0_lines.contains(&1) && term1_lines.contains(&1));
assert!(term0_lines.contains(&5) && term1_lines.contains(&5));
}
#[test]
fn test_overlapping_patterns() {
let temp_dir = tempdir().unwrap();
let file_path = temp_dir.path().join("test_file.txt");
let mut file = File::create(&file_path).unwrap();
writeln!(file, "This patternXYZ has overlapping patterns").unwrap();
writeln!(file, "No overlap here").unwrap();
writeln!(file, "patternX and patternY separate").unwrap();
let pattern1 = "patternX";
let pattern2 = "patternY";
let combined_pattern = format!("({pattern1})|({pattern2}))").replace("))", ")");
let combined_regex = Regex::new(&format!("(?i){combined_pattern}")).unwrap();
let mut pattern1_terms = HashSet::new();
pattern1_terms.insert(0);
let mut pattern2_terms = HashSet::new();
pattern2_terms.insert(1);
let pattern_to_terms = vec![pattern1_terms, pattern2_terms];
let result =
search_file_with_combined_pattern(&file_path, &combined_regex, &pattern_to_terms).unwrap();
if let Some(term0_lines) = result.get(&0) {
assert!(term0_lines.contains(&1)); assert!(!term0_lines.contains(&2));
assert!(term0_lines.contains(&3));
} else {
panic!("Term 0 (patternX) not found in results");
}
if let Some(term1_lines) = result.get(&1) {
assert!(!term1_lines.contains(&1));
assert!(!term1_lines.contains(&2));
assert!(term1_lines.contains(&3));
} else {
panic!("Term 1 (patternY) not found in results");
}
}