//! # Grep CLI Tool //! //! 一个简单的命令行工具,用于从文件中搜索指定的查询字符串。 //! //! # 功能概述 //! //! - 提供命令行接口以执行文本搜索。 //! - 支持通过环境变量 `IGNORE_CASE` 切换大小写不敏感模式。 //! - 在遇到错误时提供友好的提示信息。 //! //! # 使用方法 //! //! 假设程序的可执行文件名为 `grep`,可以通过以下方式运行: //! //! ```bash //! ./grep //! ``` //! //! 例如: //! ```bash //! ./grep rust example.txt //! ``` //! 如果设置了环境变量 `IGNORE_CASE`,查询会忽略大小写。 //! - CMD //! ``` bash //! IGNORE_CASE=1 ./grep rust example.txt //! ``` //! - Powershell //! ``` powershell //! $env:IGNORE_CASE = 1 ./grep rust example.txt //! ``` use std::error::Error; use std::{env, fs}; /// 配置结构体,存储查询字符串、文件路径以及是否忽略大小写的标志。 pub struct Config { /// 查询字符串 pub query: String, // 修改为 pub,便于外部访问 /// 文件路径 pub file_path: String, // 修改为 pub,便于外部访问 /// 是否忽略大小写 pub ignore_case: bool, // 修改为 pub,便于外部访问 } impl Config { /// 从命令行参数构建 `Config` 实例。 /// /// # 参数 /// /// - `args`: 命令行参数的迭代器。 /// /// # 返回值 /// /// 返回 `Config` 实例,或者在参数缺失时返回错误消息。 /// /// # 错误 /// /// 当缺少查询字符串或文件路径时,返回对应的错误消息。 /// /// # 示例 /// /// ``` /// let args = vec!["program".to_string(), "query".to_string(), "file.txt".to_string()]; /// let config = Config::build(args.into_iter()); /// assert!(config.is_ok()); /// ``` pub fn build(mut args: impl Iterator) -> Result { args.next(); let query = match args.next() { Some(args) => args, None => return Err("Please provide a query string"), }; let file_path = match args.next() { Some(args) => args, None => return Err("Please provide a file path"), }; let ignore_case = env::var("IGNORE_CASE").is_ok(); Ok(Config { query, file_path, ignore_case, }) } } /// 运行搜索逻辑。 /// /// # 参数 /// /// - `config`: 包含查询信息的 `Config` 实例。 /// /// # 返回值 /// /// 成功返回 `Ok(())`,失败返回包含错误信息的 `Box`。 /// /// # 错误 /// /// 如果文件读取失败,返回错误信息。 pub fn run(config: Config) -> Result<(), Box> { let contents = fs::read_to_string(config.file_path)?; let result = if config.ignore_case { search_case_insensitive(&config.query, &contents) } else { search(&config.query, &contents) }; for line in result { println!("{line}") } Ok(()) } /// 在内容中进行大小写敏感的查询。 /// /// # 参数 /// /// - `query`: 查询字符串。 /// - `contents`: 文件内容。 /// /// # 返回值 /// /// 包含匹配行的 `Vec<&str>`。 fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> { contents.lines() .filter(|line| line.contains(query)) .collect() } /// 在内容中进行大小写不敏感的查询。 /// /// # 参数 /// /// - `query`: 查询字符串。 /// - `contents`: 文件内容。 /// /// # 返回值 /// /// 包含匹配行的 `Vec<&str>`。 fn search_case_insensitive<'a>(query: &str, contents: &'a str) -> Vec<&'a str> { let query = query.to_lowercase(); contents.lines() .filter(|line| line.to_lowercase().contains(&query)) .collect() } #[cfg(test)] mod tests { use super::*; /// 测试大小写敏感的查询功能。 #[test] fn case_sensitive() { let query = "duct"; let contents = "\ Rust: safe, fast, productive. Pick three. Duct tape."; assert_eq!(vec!["safe, fast, productive."], search(query, contents)); } /// 测试大小写不敏感的查询功能。 #[test] fn case_insensitive() { let query = "rUsT"; let contents = "\ Rust: safe, fast, productive. Pick three. Trust me."; assert_eq!( vec!["Rust:", "Trust me."], search_case_insensitive(query, contents) ); } }