feat: improve match_pattern to surface errors and avoid collecting everything at once

feat: implement Match and Matches to hold more info and display it
This commit is contained in:
Mohammad Kanaan 2026-07-27 12:53:58 +03:00
parent d58ae9ffbe
commit 660b710cac
3 changed files with 62 additions and 6 deletions

View file

@ -1,4 +1,5 @@
use clap::Parser;
mod matches;
mod parser;
#[derive(Parser)]
@ -11,12 +12,12 @@ fn main() -> anyhow::Result<()> {
let cli = Cli::parse();
let lines_iter = parser::read_lines(&cli.file_path)?;
let matches = parser::match_pattern(lines_iter, &cli.pattern);
let matches = parser::match_pattern(lines_iter, &cli.pattern, &cli.file_path);
println!("Pattern: {}", cli.pattern);
println!("File path: {}", cli.file_path);
println!("---");
println!("{:?}", matches);
println!("{}", matches?);
Ok(())
}

33
src/matches.rs Normal file
View file

@ -0,0 +1,33 @@
use std::fmt::Display;
pub struct Match {
pub file_name: String,
pub line_number: usize,
pub text: String,
}
pub struct Matches {
pub items: Vec<Match>,
pub pattern: String,
}
impl Display for Match {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}: line {}: {}", self.file_name, self.line_number, self.text)
}
}
impl Display for Matches {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(f, "Matches for pattern {}:", self.pattern);
for (idx, item) in self.items.iter().enumerate() {
if idx > 0 {
writeln!(f)?;
}
write!(f, "{}", item);
}
write!(f, "{}", "")
}
}

View file

@ -6,6 +6,8 @@ use std::{
use anyhow::Context;
use regex::Regex;
use crate::matches::{self, Match, Matches};
pub fn read_lines(
file_path: &str,
) -> anyhow::Result<impl Iterator<Item = std::io::Result<String>>, anyhow::Error> {
@ -20,14 +22,34 @@ pub fn read_lines(
Ok(reader.lines())
}
fn get_file_name_from_path(file_path: &str) -> String {
match file_path.rsplit('/').next() {
Some(name) => name.into(),
None => file_path.into(),
}
}
pub fn match_pattern(
iter: impl Iterator<Item = std::io::Result<String>>,
pattern: &str,
) -> anyhow::Result<Vec<String>, anyhow::Error> {
file_path: &str,
) -> anyhow::Result<Matches, anyhow::Error> {
let reg = Regex::new(pattern).with_context(|| format!("invalid regex: {:?}", pattern))?;
let lines = iter.collect::<std::io::Result<Vec<_>>>()?;
let mut matches = Vec::new();
let matches = lines.into_iter().filter(|line| reg.is_match(line)).collect::<Vec<_>>();
for (idx, line) in iter.enumerate() {
let line = line?;
Ok(matches)
if reg.is_match(&line) {
let m = Match {
line_number: idx,
text: line,
file_name: get_file_name_from_path(file_path),
};
matches.push(m);
}
}
Ok(Matches { items: matches, pattern: String::from(pattern) })
}