feat: add support for multiple files
refactor: replace String with PathBuf
This commit is contained in:
parent
3f1a5c5ef3
commit
ad6dc24d7e
3 changed files with 48 additions and 18 deletions
13
src/main.rs
13
src/main.rs
|
|
@ -1,15 +1,17 @@
|
||||||
use std::process::ExitCode;
|
use std::{path::PathBuf, process::ExitCode};
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
|
||||||
use crate::matches::Matches;
|
use crate::{matches::Matches, parser::process_files};
|
||||||
mod matches;
|
mod matches;
|
||||||
mod parser;
|
mod parser;
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
struct Cli {
|
struct Cli {
|
||||||
pattern: String,
|
pattern: String,
|
||||||
file_path: String,
|
|
||||||
|
#[arg(value_name = "FILES", num_args = 1..)]
|
||||||
|
file_paths: Vec<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> ExitCode {
|
fn main() -> ExitCode {
|
||||||
|
|
@ -29,8 +31,9 @@ fn main() -> ExitCode {
|
||||||
fn run() -> anyhow::Result<Matches> {
|
fn run() -> anyhow::Result<Matches> {
|
||||||
let cli = Cli::parse();
|
let cli = Cli::parse();
|
||||||
|
|
||||||
let lines_iter = parser::read_lines(&cli.file_path)?;
|
// println!("{:?} {}", cli.file_paths, cli.pattern);
|
||||||
let matches = parser::match_pattern(lines_iter, &cli.pattern, &cli.file_path)?;
|
|
||||||
|
let matches = process_files(&cli.file_paths, &cli.pattern)?;
|
||||||
|
|
||||||
Ok(matches)
|
Ok(matches)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,10 @@ impl Matches {
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.items.is_empty()
|
self.items.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn append(&mut self, matches: &mut Self) -> () {
|
||||||
|
self.items.append(&mut matches.items);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Match {
|
impl Display for Match {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use std::{
|
use std::{
|
||||||
fs::File,
|
fs::File,
|
||||||
io::{BufRead, BufReader},
|
io::{BufRead, BufReader},
|
||||||
path::Path,
|
path::{Path, PathBuf},
|
||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
|
|
@ -9,22 +9,44 @@ use regex::Regex;
|
||||||
|
|
||||||
use crate::matches::{Match, Matches};
|
use crate::matches::{Match, Matches};
|
||||||
|
|
||||||
pub fn read_lines(
|
pub fn process_files(
|
||||||
file_path: &str,
|
file_paths: &Vec<PathBuf>,
|
||||||
) -> anyhow::Result<impl Iterator<Item = std::io::Result<String>>, anyhow::Error> {
|
pattern: &str,
|
||||||
if file_path.is_empty() {
|
) -> anyhow::Result<Matches, anyhow::Error> {
|
||||||
return Err(anyhow::anyhow!("File path cannot be empty"));
|
let mut matches = Matches { items: vec![], pattern: String::from(pattern) };
|
||||||
|
|
||||||
|
for file_path in file_paths {
|
||||||
|
let lines_iter = read_lines(file_path)?;
|
||||||
|
let mut _matches = match_pattern(lines_iter, pattern, file_path)?;
|
||||||
|
matches.append(&mut _matches);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// let lines_iter = read_lines(file_path)?;
|
||||||
|
// let matches = match_pattern(lines_iter, pattern, file_path)?;
|
||||||
|
|
||||||
|
Ok(matches)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_lines(
|
||||||
|
file_path: &Path,
|
||||||
|
) -> anyhow::Result<impl Iterator<Item = std::io::Result<String>>, anyhow::Error> {
|
||||||
|
match file_path.try_exists() {
|
||||||
|
Ok(true) => {}
|
||||||
|
Ok(false) => return Err(anyhow::anyhow!("File path needs to exist")),
|
||||||
|
Err(e) => eprintln!("Error checking path: {e}"),
|
||||||
|
}
|
||||||
|
|
||||||
|
let path_string = file_path.to_string_lossy().to_string();
|
||||||
|
|
||||||
let file =
|
let file =
|
||||||
File::open(file_path).with_context(|| format!("failed to open file: {file_path}"))?;
|
File::open(file_path).with_context(|| format!("failed to open file: {path_string}"))?;
|
||||||
let reader = BufReader::new(file);
|
let reader = BufReader::new(file);
|
||||||
|
|
||||||
Ok(reader.lines())
|
Ok(reader.lines())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_file_name_from_path(file_path: &str) -> String {
|
fn get_file_name_from_path(file_path: &Path) -> String {
|
||||||
match Path::new(file_path).file_name() {
|
match file_path.file_name() {
|
||||||
Some(name) => name.to_string_lossy().to_string(),
|
Some(name) => name.to_string_lossy().to_string(),
|
||||||
None => String::from("unknown"),
|
None => String::from("unknown"),
|
||||||
}
|
}
|
||||||
|
|
@ -33,7 +55,7 @@ fn get_file_name_from_path(file_path: &str) -> String {
|
||||||
pub fn match_pattern(
|
pub fn match_pattern(
|
||||||
iter: impl Iterator<Item = std::io::Result<String>>,
|
iter: impl Iterator<Item = std::io::Result<String>>,
|
||||||
pattern: &str,
|
pattern: &str,
|
||||||
file_path: &str,
|
file_path: &Path,
|
||||||
) -> anyhow::Result<Matches, anyhow::Error> {
|
) -> anyhow::Result<Matches, anyhow::Error> {
|
||||||
let reg = Regex::new(pattern).with_context(|| format!("invalid regex: {:?}", pattern))?;
|
let reg = Regex::new(pattern).with_context(|| format!("invalid regex: {:?}", pattern))?;
|
||||||
let mut matches = Vec::new();
|
let mut matches = Vec::new();
|
||||||
|
|
@ -61,11 +83,11 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_get_file_name_from_path() {
|
fn test_get_file_name_from_path() {
|
||||||
let path = "/path/to/file.txt";
|
let path = Path::new("/path/to/file.txt");
|
||||||
let file_name = get_file_name_from_path(path);
|
let file_name = get_file_name_from_path(path);
|
||||||
assert_eq!(file_name, "file.txt");
|
assert_eq!(file_name, "file.txt");
|
||||||
|
|
||||||
let path = "file.txt";
|
let path = Path::new("file.txt");
|
||||||
let file_name = get_file_name_from_path(path);
|
let file_name = get_file_name_from_path(path);
|
||||||
assert_eq!(file_name, "file.txt");
|
assert_eq!(file_name, "file.txt");
|
||||||
}
|
}
|
||||||
|
|
@ -75,7 +97,8 @@ mod tests {
|
||||||
let input: Vec<std::io::Result<String>> =
|
let input: Vec<std::io::Result<String>> =
|
||||||
vec![Ok("hola".into()), Ok("no".into()), Ok("hola again".into())];
|
vec![Ok("hola".into()), Ok("no".into()), Ok("hola again".into())];
|
||||||
|
|
||||||
let result = match_pattern(input.into_iter(), r"^hola", "/tmp/test.txt").unwrap();
|
let result =
|
||||||
|
match_pattern(input.into_iter(), r"^hola", Path::new("/tmp/test.txt")).unwrap();
|
||||||
|
|
||||||
assert_eq!(result.items.len(), 2);
|
assert_eq!(result.items.len(), 2);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue