diff --git a/Cargo.toml b/Cargo.toml index 98a6636..6c6448d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,5 +11,5 @@ panic = "abort" # 发生崩溃时直接退出,不保留复杂的栈回溯 strip = true # 自动剥离所有的调试信息和符号表(相当于执行了 Linux 的 strip 命令) [dependencies] -walkdir = "2" -crossbeam = "0.8" +walkdir = "2" # 目录递归遍历库 +crossbeam = "0.8" # crossbeam 线程池 diff --git a/src/lib.rs b/src/lib.rs index fdcdfe6..0449f83 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,24 +1,81 @@ use std::fs; use std::path::Path; +use crossbeam::channel; +use walkdir::WalkDir; pub mod searcher; -pub use searcher::{ should_skip_dir, is_valid_extension }; +pub use searcher::{ should_skip_dir, is_valid_extension, FileResult }; + + +pub fn search_lines(root_dir: &str,extensions_str: Option, real_query: &str,ignore_case: bool) { + let (tx, rx) = channel::unbounded::(); + crossbeam::scope(|scope|{ + // 使用WalkDir递归遍历路径 + let it = WalkDir::new(root_dir).into_iter().filter_map(|e| e.ok()); + + for entry in it { + let path = entry.path().to_path_buf(); + if path.is_dir() && should_skip_dir(&path) { + continue; + } + // 只搜索文件 跳过文件夹 + if path.is_file() && is_valid_extension(&path, &extensions_str) { + + let thread_tx = tx.clone(); + + scope.spawn(move |_|{ + if let Some(file_results) = search_in_file(real_query, &path, ignore_case) { + let _ = thread_tx.send(file_results); + } + }); + } + } + + drop(tx); + + while let Ok(file_result) = rx.recv() { + // let normalized_path = Path::new(&file_result.path_name) + // .components() + // .map(|comp| comp.as_os_str().to_string_lossy()) + // .collect::>() + // .join(&MAIN_SEPARATOR.to_string()); + let normalized_path = file_result.path_name; + let max_width = file_result.line_count.to_string().len(); + + println!("\x1b[35m{:?} :\x1b[0m", normalized_path); + for (line_num, line_str) in file_result.matches { + println!("\x1b[32m{:>width$} |\x1b[0m {}", line_num, line_str, width = max_width); + } + println!(); + } + }).unwrap() +} + + // 处理单个文件搜索 -pub fn search_in_file(query: &str, path: &Path, ignore_case: bool) -> Result<(), std::io::Error> { - let contents = fs::read_to_string(path)?; +pub fn search_in_file(query: &str, path: &Path, ignore_case: bool) -> Option { + let contents = match fs::read_to_string(path) { + Ok(text) => text, + Err(_) => return None + }; // 🌟 2. 跨模块调用:通过包名或相对路径调用 searcher 里的函数 let results = searcher::search(query, &contents, ignore_case); if !results.is_empty() { - println!("\x1b[35m{:?} :\x1b[0m", path); - let total_lines = contents.lines().count(); - let max_width = total_lines.to_string().len(); + let mut matches_vec = Vec::new(); - for (line_num, line) in results { - println!("\x1b[32m{:>width$} |\x1b[0m {}", line_num, line, width = max_width); + for (line_num, line_str) in results { + matches_vec.push((line_num, line_str.to_string())); } - println!(); + + let file_line_count = contents.lines().count(); + Some(FileResult { + path_name: path.to_string_lossy().replace("\\","/"), + matches: matches_vec, + line_count: file_line_count + }) + } else { + None } - Ok(()) } diff --git a/src/main.rs b/src/main.rs index ff71599..73f55f2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,7 @@ use std::env; use std::path::Path; -use walkdir::WalkDir; -use rustgrep::{ search_in_file, should_skip_dir, is_valid_extension }; +use rustgrep::{ search_lines }; fn main() { // 1.收集命令行参数到一个 Vector @@ -10,7 +9,7 @@ fn main() { // 2.简单的边界检查,防止索引越界 if args.len() < 3 { - println!("用法提示:minigrep <搜索关键字> <文件路径>"); + println!("用法提示:minigrep <搜索关键字> <文件路径> [文件类型,可传入多个,用\",\"隔开,支持\"!\"反选]"); return; } @@ -36,36 +35,5 @@ fn main() { eprintln!("错误:路径 '{}' 不存在", target_path); std::process::exit(1); } - - let mut it = WalkDir::new(target_path).into_iter(); - - // 使用WalkDir递归遍历路径 - while let Some(entry) = it.next() { - // 通过match安全处理异常 - match entry { - Ok(dir_entry) => { - let path = dir_entry.path(); - - if path.is_dir() && should_skip_dir(path) { - it.skip_current_dir(); - continue; - } - - // 只搜索文件 跳过文件夹 - if path.is_file() { - if !is_valid_extension(path, &extensions_str) { - continue; - } - - if let Err(e) = search_in_file(real_query, path, ignore_case) { - // 只在严重错误时打印警告 - if e.kind() != std::io::ErrorKind::InvalidData { - eprintln!("警告: 无法读取文件 {:?}: {}", path, e); - } - } - } - } - Err(e) => eprintln!("遍历目录时出错: {}", e), - } - } + search_lines(target_path,extensions_str,real_query,ignore_case); } diff --git a/src/searcher/mod.rs b/src/searcher/mod.rs index 50a58ed..5c99f5a 100644 --- a/src/searcher/mod.rs +++ b/src/searcher/mod.rs @@ -3,3 +3,9 @@ mod filter; pub use core::search; pub use filter::{ should_skip_dir, is_valid_extension }; + +pub struct FileResult { + pub path_name: String, + pub matches: Vec<(usize, String)>, + pub line_count: usize, +}