From 943692900b7c56f1a5c701070bdd41400b8f715c Mon Sep 17 00:00:00 2001 From: gpteth <1324104531@qq.com> Date: Mon, 1 Sep 2025 00:30:51 +0800 Subject: [PATCH] minigrep minigrep --- minigrep/.gitignore | 1 + minigrep/Cargo.lock | 7 + minigrep/Cargo.toml | 77 ++++ minigrep/README.md | 677 ++++++++++++++++++++++++++++ minigrep/poem.txt | 39 ++ minigrep/src/lib.rs | 585 ++++++++++++++++++++++++ minigrep/src/main.rs | 620 +++++++++++++++++++++++++ minigrep/story.txt | 62 +++ minigrep/tests/integration_tests.rs | 383 ++++++++++++++++ 9 files changed, 2451 insertions(+) create mode 100644 minigrep/.gitignore create mode 100644 minigrep/Cargo.lock create mode 100644 minigrep/Cargo.toml create mode 100644 minigrep/README.md create mode 100644 minigrep/poem.txt create mode 100644 minigrep/src/lib.rs create mode 100644 minigrep/src/main.rs create mode 100644 minigrep/story.txt create mode 100644 minigrep/tests/integration_tests.rs diff --git a/minigrep/.gitignore b/minigrep/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/minigrep/.gitignore @@ -0,0 +1 @@ +/target diff --git a/minigrep/Cargo.lock b/minigrep/Cargo.lock new file mode 100644 index 0000000..1511dd5 --- /dev/null +++ b/minigrep/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "minigrep" +version = "0.1.0" diff --git a/minigrep/Cargo.toml b/minigrep/Cargo.toml new file mode 100644 index 0000000..3fc5060 --- /dev/null +++ b/minigrep/Cargo.toml @@ -0,0 +1,77 @@ +[package] +name = "minigrep" +version = "0.1.0" +edition = "2021" +authors = ["Rust学习者 "] +description = "一个用于学习Rust语法的minigrep项目 - 文本搜索工具" +license = "MIT" +readme = "README.md" +repository = "https://github.com/example/minigrep" +keywords = ["cli", "search", "text", "grep", "rust-learning"] +categories = ["command-line-utilities", "text-processing"] + +# 二进制目标配置 +[[bin]] +name = "minigrep" +path = "src/main.rs" + +# 库目标配置 +[lib] +name = "minigrep" +path = "src/lib.rs" + +# 依赖项 - 演示如何管理外部crate +[dependencies] +# 用于环境变量处理的示例依赖 +# serde = { version = "1.0", features = ["derive"] } +# clap = { version = "4.0", features = ["derive"] } + +# 开发依赖 - 仅在测试和开发时使用 +[dev-dependencies] +# 用于更好的测试断言 +# assert_cmd = "2.0" +# predicates = "3.0" + +# 编译配置 +[profile.dev] +# 开发模式:快速编译,包含调试信息 +opt-level = 0 +debug = true +split-debuginfo = "unpacked" +debug-assertions = true +overflow-checks = true +lto = false +panic = "unwind" +incremental = true +codegen-units = 256 +rpath = false + +[profile.release] +# 发布模式:优化性能 +opt-level = 3 +debug = false +split-debuginfo = "packed" +debug-assertions = false +overflow-checks = false +lto = true +panic = "abort" +incremental = false +codegen-units = 1 +rpath = false + +# 文档配置 +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] + +# 示例配置已移除 - 可以通过 cargo run -- --example 查看示例 + +# 特性标志 - 演示条件编译 +[features] +default = [] +# 启用彩色输出 +color = [] +# 启用正则表达式支持 +regex = [] +# 启用性能分析 +profiling = [] diff --git a/minigrep/README.md b/minigrep/README.md new file mode 100644 index 0000000..e94b0f1 --- /dev/null +++ b/minigrep/README.md @@ -0,0 +1,677 @@ +# Minigrep - Rust 学习项目 + +🦀 一个用于学习 Rust 编程语言的文本搜索工具 + +## 📖 项目简介 + +`minigrep` 是一个简化版的 `grep` 命令行工具,专门设计用于学习和演示 Rust 编程语言的核心概念。这个项目涵盖了 Rust 的所有重要特性,从基础语法到高级概念。 + +## 🎯 学习目标 + +通过这个项目,你将学习到以下 Rust 概念: + +### 1. 变量和数据类型 +- **不可变变量**:Rust 默认变量不可变 +- **可变变量**:使用 `mut` 关键字 +- **基本类型**:`i32`, `f64`, `bool`, `char` +- **复合类型**:元组 `(T1, T2)` 和数组 `[T; N]` +- **字符串类型**:`&str` (字符串切片) 和 `String` (拥有的字符串) + +```rust +// 不可变变量 +let x = 5; + +// 可变变量 +let mut y = 10; +y = 15; + +// 类型注解 +let z: i32 = 42; + +// 字符串类型 +let slice: &str = "Hello"; +let owned: String = String::from("World"); +``` + +### 2. 函数 +- **函数定义**:使用 `fn` 关键字 +- **参数和返回值**:类型注解和返回类型 +- **表达式 vs 语句**:Rust 是基于表达式的语言 + +```rust +fn add(a: i32, b: i32) -> i32 { + a + b // 表达式,自动返回 +} + +fn greet(name: &str) { + println!("Hello, {}!", name); // 语句 +} +``` + +### 3. 流程控制 +- **条件语句**:`if`, `else if`, `else` +- **模式匹配**:`match` 表达式 +- **循环**:`for`, `while`, `loop` + +```rust +// if 表达式 +let number = if condition { 5 } else { 6 }; + +// match 表达式 +match value { + 1 => println!("One"), + 2 | 3 => println!("Two or Three"), + _ => println!("Something else"), +} + +// for 循环 +for i in 0..10 { + println!("{}", i); +} +``` + +### 4. 所有权系统 (Ownership) +- **所有权规则**:每个值都有一个所有者 +- **移动语义**:所有权转移 +- **借用**:引用而不获取所有权 +- **生命周期**:引用的有效范围 + +```rust +// 所有权转移 +let s1 = String::from("hello"); +let s2 = s1; // s1 不再有效 + +// 借用 +let s3 = String::from("world"); +let len = calculate_length(&s3); // 借用 s3 +// s3 仍然有效 + +fn calculate_length(s: &String) -> usize { + s.len() +} +``` + +### 5. 结构体 (Structs) +- **定义结构体**:自定义数据类型 +- **方法**:结构体的关联函数 +- **关联函数**:类似静态方法 + +```rust +#[derive(Debug)] +struct Rectangle { + width: u32, + height: u32, +} + +impl Rectangle { + // 方法 + fn area(&self) -> u32 { + self.width * self.height + } + + // 关联函数 + fn square(size: u32) -> Rectangle { + Rectangle { + width: size, + height: size, + } + } +} +``` + +### 6. 枚举和模式匹配 +- **枚举定义**:多种可能的值 +- **Option 类型**:处理可能为空的值 +- **Result 类型**:错误处理 + +```rust +enum IpAddrKind { + V4, + V6, +} + +enum IpAddr { + V4(u8, u8, u8, u8), + V6(String), +} + +// Option 类型 +let some_number = Some(5); +let absent_number: Option = None; + +// Result 类型 +fn divide(a: f64, b: f64) -> Result { + if b == 0.0 { + Err("Division by zero".to_string()) + } else { + Ok(a / b) + } +} +``` + +### 7. 集合类型 +- **Vector**:动态数组 +- **HashMap**:键值对映射 +- **String**:UTF-8 字符串 + +```rust +use std::collections::HashMap; + +// Vector +let mut v = Vec::new(); +v.push(1); +v.push(2); +v.push(3); + +// HashMap +let mut scores = HashMap::new(); +scores.insert(String::from("Blue"), 10); +scores.insert(String::from("Yellow"), 50); + +// 迭代器 +let doubled: Vec = v.iter().map(|x| x * 2).collect(); +``` + +### 8. 错误处理 +- **panic!**:不可恢复的错误 +- **Result**:可恢复的错误 +- **? 操作符**:错误传播 + +```rust +use std::fs::File; +use std::io::ErrorKind; + +// 使用 match 处理 Result +let f = File::open("hello.txt"); +let f = match f { + Ok(file) => file, + Err(error) => match error.kind() { + ErrorKind::NotFound => { + panic!("File not found!"); + } + other_error => { + panic!("Problem opening file: {:?}", other_error); + } + }, +}; + +// 使用 ? 操作符 +fn read_username_from_file() -> Result { + let mut f = File::open("hello.txt")?; + let mut s = String::new(); + f.read_to_string(&mut s)?; + Ok(s) +} +``` + +### 9. 泛型、Trait 和生命周期 +- **泛型**:代码复用 +- **Trait**:共享行为 +- **生命周期**:引用有效性 + +```rust +// 泛型结构体 +struct Point { + x: T, + y: T, +} + +// Trait 定义 +trait Summary { + fn summarize(&self) -> String; +} + +// Trait 实现 +impl Summary for NewsArticle { + fn summarize(&self) -> String { + format!("{}, by {} ({})", self.headline, self.author, self.location) + } +} + +// 生命周期 +fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { + if x.len() > y.len() { + x + } else { + y + } +} +``` + +### 10. 模块系统 +- **包 (Package)**:Cargo 功能,包含一个或多个 crate +- **Crate**:模块树,产生库或可执行文件 +- **模块 (Module)**:组织代码,控制私有性 +- **路径 (Path)**:命名项的方式 + +```rust +// lib.rs +pub mod front_of_house { + pub mod hosting { + pub fn add_to_waitlist() {} + } +} + +pub fn eat_at_restaurant() { + // 绝对路径 + crate::front_of_house::hosting::add_to_waitlist(); + + // 相对路径 + front_of_house::hosting::add_to_waitlist(); +} +``` + +## 🚀 快速开始 + +### 安装 Rust + +```bash +# 安装 Rust +curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh + +# 重新加载环境 +source ~/.cargo/env + +# 验证安装 +rustc --version +cargo --version +``` + +### 运行项目 + +```bash +# 克隆或下载项目 +cd minigrep + +# 构建项目 +cargo build + +# 运行测试 +cargo test + +# 运行程序 +cargo run -- rust poem.txt + +# 查看帮助 +cargo run -- --help + +# 运行示例 +cargo run -- --example +``` + +## 📋 使用方法 + +### 基本搜索 + +```bash +# 在文件中搜索 "rust" +cargo run -- rust poem.txt + +# 不区分大小写搜索 +IGNORE_CASE=1 cargo run -- RUST poem.txt + +# 显示行号 +cargo run -- --line-numbers rust poem.txt + +# 限制结果数量 +cargo run -- --max=5 rust poem.txt +``` + +### 高级功能 + +```bash +# 精确匹配 +cargo run -- --exact "Rust" poem.txt + +# 查看版本 +cargo run -- --version + +# 查看帮助 +cargo run -- --help + +# 运行语法示例 +cargo run -- --example +``` + +## 🏗️ 项目结构 + +``` +minigrep/ +├── Cargo.toml # 项目配置文件 +├── README.md # 项目文档 +├── src/ +│ ├── lib.rs # 库代码 - 核心功能 +│ └── main.rs # 主程序 - 命令行接口 +├── tests/ +│ └── integration_tests.rs # 集成测试 +├── poem.txt # 示例文本文件 +└── story.txt # 另一个示例文件 +``` + +### 核心模块说明 + +#### `lib.rs` - 核心库 +- **Config 结构体**:配置管理 +- **SearchResult 结构体**:搜索结果 +- **SearchMode 枚举**:搜索模式 +- **SearchStats 结构体**:统计信息 +- **搜索函数**:核心搜索逻辑 +- **错误处理**:自定义错误类型 + +#### `main.rs` - 主程序 +- **命令行参数解析** +- **用户界面** +- **程序流程控制** +- **示例代码演示** +- **性能测试** + +## 🧪 测试 + +项目包含全面的测试套件: + +```bash +# 运行所有测试 +cargo test + +# 运行单元测试 +cargo test --lib + +# 运行集成测试 +cargo test --test integration_tests + +# 运行特定测试 +cargo test test_search_case_sensitive + +# 显示测试输出 +cargo test -- --nocapture + +# 运行文档测试 +cargo test --doc +``` + +### 测试覆盖的功能 +- ✅ 配置解析和验证 +- ✅ 各种搜索模式 +- ✅ 文件操作 +- ✅ 错误处理 +- ✅ 边界条件 +- ✅ 性能测试 +- ✅ Unicode 支持 + +## 📚 Rust 概念详解 + +### 内存安全 + +Rust 通过所有权系统在编译时保证内存安全: + +```rust +// 这会编译错误 - 使用已移动的值 +let s1 = String::from("hello"); +let s2 = s1; +// println!("{}", s1); // 错误!s1 已被移动 + +// 正确的方式 - 借用 +let s1 = String::from("hello"); +let s2 = &s1; // 借用 +println!("{} {}", s1, s2); // 正确! +``` + +### 零成本抽象 + +Rust 的抽象不会带来运行时开销: + +```rust +// 这个迭代器链会被编译器优化为简单的循环 +let sum: i32 = (0..1_000_000) + .filter(|x| x % 2 == 0) + .map(|x| x * x) + .sum(); +``` + +### 并发安全 + +Rust 的类型系统防止数据竞争: + +```rust +use std::thread; +use std::sync::{Arc, Mutex}; + +let counter = Arc::new(Mutex::new(0)); +let mut handles = vec![]; + +for _ in 0..10 { + let counter = Arc::clone(&counter); + let handle = thread::spawn(move || { + let mut num = counter.lock().unwrap(); + *num += 1; + }); + handles.push(handle); +} + +for handle in handles { + handle.join().unwrap(); +} +``` + +## 🔧 高级特性 + +### 自定义 Derive + +```rust +#[derive(Debug, Clone, PartialEq)] +struct Point { + x: i32, + y: i32, +} +``` + +### 宏 (Macros) + +```rust +// 声明式宏 +macro_rules! vec { + ( $( $x:expr ),* ) => { + { + let mut temp_vec = Vec::new(); + $( + temp_vec.push($x); + )* + temp_vec + } + }; +} +``` + +### 异步编程 + +```rust +use tokio; + +#[tokio::main] +async fn main() { + let result = fetch_data().await; + println!("Result: {:?}", result); +} + +async fn fetch_data() -> Result> { + // 异步操作 + Ok("Data".to_string()) +} +``` + +## 🎨 最佳实践 + +### 1. 错误处理 + +```rust +// 使用 Result 类型 +fn parse_number(s: &str) -> Result { + s.parse() +} + +// 使用 ? 操作符传播错误 +fn process_numbers(input: &str) -> Result> { + let num1 = parse_number(input)?; + let num2 = parse_number("42")?; + Ok(num1 + num2) +} +``` + +### 2. 迭代器使用 + +```rust +// 函数式编程风格 +let results: Vec<_> = data + .iter() + .filter(|&&x| x > 0) + .map(|&x| x * 2) + .collect(); + +// 链式调用 +let sum: i32 = numbers + .iter() + .filter(|&&x| x % 2 == 0) + .sum(); +``` + +### 3. 模式匹配 + +```rust +// 解构 +match point { + Point { x: 0, y } => println!("On the y axis at {}", y), + Point { x, y: 0 } => println!("On the x axis at {}", x), + Point { x, y } => println!("On neither axis: ({}, {})", x, y), +} + +// if let 简化 +if let Some(value) = optional_value { + println!("Got: {}", value); +} +``` + +## 🔍 性能优化 + +### 1. 避免不必要的分配 + +```rust +// 好:使用字符串切片 +fn process_text(text: &str) -> &str { + text.trim() +} + +// 避免:不必要的 String 分配 +fn process_text_bad(text: &str) -> String { + text.trim().to_string() // 不必要的分配 +} +``` + +### 2. 使用适当的集合类型 + +```rust +// 已知大小时使用数组 +let fixed_size: [i32; 5] = [1, 2, 3, 4, 5]; + +// 动态大小时使用 Vec +let mut dynamic: Vec = Vec::with_capacity(100); // 预分配容量 + +// 键值对使用 HashMap +use std::collections::HashMap; +let mut map: HashMap = HashMap::new(); +``` + +## 🛠️ 开发工具 + +### Cargo 命令 + +```bash +# 创建新项目 +cargo new my_project +cargo new --lib my_library + +# 构建和运行 +cargo build # 调试构建 +cargo build --release # 发布构建 +cargo run # 运行 +cargo test # 测试 + +# 代码检查 +cargo check # 快速检查 +cargo clippy # 代码质量检查 +cargo fmt # 代码格式化 + +# 文档 +cargo doc # 生成文档 +cargo doc --open # 生成并打开文档 +``` + +### 有用的工具 + +```bash +# 安装额外工具 +rustup component add clippy # 代码检查工具 +rustup component add rustfmt # 代码格式化工具 + +# 性能分析 +cargo install flamegraph +cargo flamegraph --bin minigrep + +# 基准测试 +cargo install criterion +``` + +## 📖 学习资源 + +### 官方资源 +- [Rust 官方网站](https://www.rust-lang.org/) +- [Rust 程序设计语言](https://doc.rust-lang.org/book/) +- [Rust 标准库文档](https://doc.rust-lang.org/std/) +- [Rust 参考手册](https://doc.rust-lang.org/reference/) + +### 社区资源 +- [Rust 用户论坛](https://users.rust-lang.org/) +- [Rust 官方 Discord](https://discord.gg/rust-lang) +- [This Week in Rust](https://this-week-in-rust.org/) +- [Awesome Rust](https://github.com/rust-unofficial/awesome-rust) + +### 练习项目 +- [Rustlings](https://github.com/rust-lang/rustlings) - 小练习 +- [Rust by Example](https://doc.rust-lang.org/rust-by-example/) - 示例代码 +- [Exercism Rust Track](https://exercism.org/tracks/rust) - 编程练习 + +## 🤝 贡献 + +欢迎贡献代码、报告问题或提出改进建议! + +### 开发流程 + +1. Fork 项目 +2. 创建特性分支 (`git checkout -b feature/amazing-feature`) +3. 提交更改 (`git commit -m 'Add some amazing feature'`) +4. 推送到分支 (`git push origin feature/amazing-feature`) +5. 打开 Pull Request + +### 代码规范 + +```bash +# 运行所有检查 +cargo fmt --all -- --check +cargo clippy --all-targets --all-features -- -D warnings +cargo test +``` + +## 📄 许可证 + +本项目采用 MIT 许可证 - 查看 [LICENSE](LICENSE) 文件了解详情。 + +## 🙏 致谢 + +- Rust 团队和社区 +- 《Rust 程序设计语言》一书的作者 +- 所有为 Rust 生态系统做出贡献的开发者 + +--- + +**Happy Coding with Rust! 🦀** + +如果你觉得这个项目有帮助,请给它一个 ⭐️! \ No newline at end of file diff --git a/minigrep/poem.txt b/minigrep/poem.txt new file mode 100644 index 0000000..87ee5de --- /dev/null +++ b/minigrep/poem.txt @@ -0,0 +1,39 @@ +I'm nobody! Who are you? +Are you nobody, too? +Then there's a pair of us - don't tell! +They'd banish us, you know. + +How dreary to be somebody! +How public, like a frog +To tell your name the livelong day +To an admiring bog! + +-- Emily Dickinson + +Rust is a systems programming language +that runs blazingly fast, prevents segfaults, +and guarantees thread safety. + +Rust empowers everyone +to build reliable and efficient software. + +The Rust programming language helps you write faster, more reliable software. +High-level ergonomics and low-level control are often at odds in programming language design; +Rust challenges that tradeoff. + +Through balancing powerful technical capacity and a great developer experience, +Rust gives you the option to control low-level details (such as memory usage) +without all the hassle traditionally associated with such control. + +🦀 Rust 是一门系统编程语言 +专注于安全、速度和并发。 + +Rust 的设计让你能够编写快速且安全的代码, +同时避免了传统系统编程语言中的常见陷阱。 + +无论你是经验丰富的系统程序员, +还是刚刚开始学习编程, +Rust 都能为你提供强大的工具和友好的社区支持。 + +Welcome to the Rust community! +Happy coding with Rust! 🚀 \ No newline at end of file diff --git a/minigrep/src/lib.rs b/minigrep/src/lib.rs new file mode 100644 index 0000000..fd87cab --- /dev/null +++ b/minigrep/src/lib.rs @@ -0,0 +1,585 @@ +//! # Minigrep 库 +//! +//! 这是一个用于学习 Rust 语法的文本搜索工具库。 +//! 本文件演示了 Rust 中的各种核心概念: +//! - 结构体和方法 +//! - 枚举和模式匹配 +//! - 错误处理 +//! - 所有权和借用 +//! - 生命周期 +//! - Trait 和泛型 +//! - 模块系统 +//! - 集合操作 + +use std::error::Error; +use std::fs; +use std::env; +use std::collections::HashMap; + +// ============================================================================ +// 1. 枚举和模式匹配 - 定义搜索模式 +// ============================================================================ + +/// 搜索模式枚举 - 演示枚举的定义和使用 +#[derive(Debug, Clone, PartialEq)] +pub enum SearchMode { + /// 区分大小写搜索 + CaseSensitive, + /// 不区分大小写搜索 + CaseInsensitive, + /// 正则表达式搜索(演示带数据的枚举变体) + Regex(String), + /// 精确匹配 + Exact, +} + +// 为枚举实现方法 +impl SearchMode { + /// 从环境变量创建搜索模式 + pub fn from_env() -> Self { + // 演示环境变量处理和模式匹配 + match env::var("IGNORE_CASE") { + Ok(val) if !val.is_empty() => SearchMode::CaseInsensitive, + _ => SearchMode::CaseSensitive, + } + } + + /// 检查是否为大小写敏感模式 + pub fn is_case_sensitive(&self) -> bool { + matches!(self, SearchMode::CaseSensitive | SearchMode::Exact) + } +} + +// ============================================================================ +// 2. 结构体 - 配置结构体 +// ============================================================================ + +/// 配置结构体 - 演示结构体定义、生命周期和所有权 +#[derive(Debug, Clone)] +pub struct Config { + /// 搜索查询字符串 + pub query: String, + /// 文件路径 + pub file_path: String, + /// 搜索模式 + pub search_mode: SearchMode, + /// 是否显示行号 + pub show_line_numbers: bool, + /// 最大结果数量(演示 Option 类型) + pub max_results: Option, +} + +// ============================================================================ +// 3. 实现块 - 为结构体添加方法 +// ============================================================================ + +impl Config { + /// 构造函数 - 演示错误处理和 Result 类型 + /// + /// # 参数 + /// * `args` - 命令行参数的迭代器 + /// + /// # 返回值 + /// * `Result` - 成功返回配置,失败返回错误信息 + /// + /// # 示例 + /// ``` + /// use minigrep::Config; + /// let args = vec!["program".to_string(), "query".to_string(), "file.txt".to_string()]; + /// let config = Config::build(args.into_iter()).unwrap(); + /// ``` + pub fn build( + mut args: impl Iterator, + ) -> Result { + // 跳过程序名 + args.next(); + + // 获取查询字符串 - 演示 Option 和错误处理 + let query = match args.next() { + Some(arg) => arg, + None => return Err("Not enough arguments: missing query string"), + }; + + // 获取文件路径 + let file_path = match args.next() { + Some(arg) => arg, + None => return Err("Not enough arguments: missing file path"), + }; + + // 从环境变量获取搜索模式 + let search_mode = SearchMode::from_env(); + + // 解析其他选项 + let mut show_line_numbers = false; + let mut max_results = None; + + // 处理剩余参数 + for arg in args { + match arg.as_str() { + "--line-numbers" | "-n" => show_line_numbers = true, + arg if arg.starts_with("--max=") => { + if let Some(num_str) = arg.strip_prefix("--max=") { + max_results = num_str.parse().ok(); + } + } + _ => {} // 忽略未知参数 + } + } + + Ok(Config { + query, + file_path, + search_mode, + show_line_numbers, + max_results, + }) + } + + /// 验证配置 - 演示方法链和错误处理 + pub fn validate(&self) -> Result<(), ConfigError> { + if self.query.is_empty() { + return Err(ConfigError::EmptyQuery); + } + + if self.file_path.is_empty() { + return Err(ConfigError::EmptyFilePath); + } + + // 检查文件是否存在 + if !std::path::Path::new(&self.file_path).exists() { + return Err(ConfigError::FileNotFound(self.file_path.clone())); + } + + Ok(()) + } +} + +// ============================================================================ +// 4. 自定义错误类型 - 演示错误处理和 Trait 实现 +// ============================================================================ + +/// 配置错误枚举 - 演示自定义错误类型 +#[derive(Debug, Clone)] +pub enum ConfigError { + EmptyQuery, + EmptyFilePath, + FileNotFound(String), + InvalidMaxResults, +} + +// 为错误类型实现 Display trait +impl std::fmt::Display for ConfigError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ConfigError::EmptyQuery => write!(f, "查询字符串不能为空"), + ConfigError::EmptyFilePath => write!(f, "文件路径不能为空"), + ConfigError::FileNotFound(path) => write!(f, "文件未找到: {}", path), + ConfigError::InvalidMaxResults => write!(f, "无效的最大结果数量"), + } + } +} + +// 实现 Error trait +impl Error for ConfigError {} + +// ============================================================================ +// 5. 搜索结果结构体 - 演示生命周期 +// ============================================================================ + +/// 搜索结果 - 演示生命周期参数 +#[derive(Debug, Clone)] +pub struct SearchResult { + /// 匹配的行内容 + pub line: String, + /// 行号(从1开始) + pub line_number: usize, + /// 匹配的位置(字符索引) + pub match_positions: Vec<(usize, usize)>, +} + +impl SearchResult { + /// 创建新的搜索结果 + pub fn new(line: String, line_number: usize, match_positions: Vec<(usize, usize)>) -> Self { + Self { + line, + line_number, + match_positions, + } + } + + /// 格式化输出 - 演示字符串操作 + pub fn format_output(&self, show_line_numbers: bool) -> String { + if show_line_numbers { + format!("{}: {}", self.line_number, self.line) + } else { + self.line.clone() + } + } +} + +// ============================================================================ +// 6. 泛型和 Trait - 搜索策略 +// ============================================================================ + +/// 搜索策略 trait - 演示 trait 定义 +pub trait SearchStrategy { + /// 执行搜索 + fn search(&self, query: &str, contents: &str) -> Vec; + + /// 获取策略名称 + fn name(&self) -> &'static str; +} + +/// 区分大小写搜索策略 +pub struct CaseSensitiveSearch; + +impl SearchStrategy for CaseSensitiveSearch { + fn search(&self, query: &str, contents: &str) -> Vec { + search_case_sensitive(query, contents) + } + + fn name(&self) -> &'static str { + "区分大小写搜索" + } +} + +/// 不区分大小写搜索策略 +pub struct CaseInsensitiveSearch; + +impl SearchStrategy for CaseInsensitiveSearch { + fn search(&self, query: &str, contents: &str) -> Vec { + search_case_insensitive(query, contents) + } + + fn name(&self) -> &'static str { + "不区分大小写搜索" + } +} + +// ============================================================================ +// 7. 主要功能函数 - 演示所有权、借用和错误处理 +// ============================================================================ + +/// 运行程序的主要逻辑 +/// +/// # 参数 +/// * `config` - 配置对象的引用 +/// +/// # 返回值 +/// * `Result, Box>` - 搜索结果或错误 +/// +/// # 错误 +/// 当文件读取失败或其他 I/O 错误时返回错误 +pub fn run(config: &Config) -> Result, Box> { + // 验证配置 + config.validate()?; + + // 读取文件内容 - 演示错误传播 + let contents = fs::read_to_string(&config.file_path)?; + + // 根据搜索模式选择搜索策略 - 演示模式匹配和 trait 对象 + let results = match &config.search_mode { + SearchMode::CaseSensitive => { + let strategy = CaseSensitiveSearch; + strategy.search(&config.query, &contents) + } + SearchMode::CaseInsensitive => { + let strategy = CaseInsensitiveSearch; + strategy.search(&config.query, &contents) + } + SearchMode::Exact => search_exact(&config.query, &contents), + SearchMode::Regex(pattern) => search_regex(pattern, &contents)?, + }; + + // 应用最大结果限制 - 演示 Option 和迭代器 + let limited_results = match config.max_results { + Some(max) => results.into_iter().take(max).collect(), + None => results, + }; + + Ok(limited_results) +} + +// ============================================================================ +// 8. 搜索函数实现 - 演示迭代器、闭包和集合操作 +// ============================================================================ + +/// 区分大小写搜索 +/// +/// # 参数 +/// * `query` - 搜索查询 +/// * `contents` - 文件内容 +/// +/// # 返回值 +/// 包含匹配行的向量 +/// +/// # 示例 +/// ``` +/// use minigrep::search_case_sensitive; +/// let results = search_case_sensitive("rust", "Rust is great\nrust is awesome"); +/// assert_eq!(results.len(), 1); +/// ``` +pub fn search_case_sensitive(query: &str, contents: &str) -> Vec { + contents + .lines() // 迭代器:按行分割 + .enumerate() // 添加行号 + .filter_map(|(line_num, line)| { // 过滤和映射 + // 查找所有匹配位置 + let match_positions = find_all_matches(line, query, true); + if !match_positions.is_empty() { + Some(SearchResult::new( + line.to_string(), + line_num + 1, // 行号从1开始 + match_positions, + )) + } else { + None + } + }) + .collect() // 收集结果 +} + +/// 不区分大小写搜索 +pub fn search_case_insensitive(query: &str, contents: &str) -> Vec { + let query = query.to_lowercase(); // 转换为小写 + + contents + .lines() + .enumerate() + .filter_map(|(line_num, line)| { + let line_lower = line.to_lowercase(); + let match_positions = find_all_matches(&line_lower, &query, false); + if !match_positions.is_empty() { + Some(SearchResult::new( + line.to_string(), + line_num + 1, + match_positions, + )) + } else { + None + } + }) + .collect() +} + +/// 精确匹配搜索 +pub fn search_exact(query: &str, contents: &str) -> Vec { + contents + .lines() + .enumerate() + .filter_map(|(line_num, line)| { + if line.trim() == query { + Some(SearchResult::new( + line.to_string(), + line_num + 1, + vec![(0, line.len())], + )) + } else { + None + } + }) + .collect() +} + +/// 正则表达式搜索(简化版) +pub fn search_regex(pattern: &str, contents: &str) -> Result, Box> { + // 这里使用简单的字符串匹配模拟正则表达式 + // 在实际项目中,你会使用 regex crate + if pattern.contains('*') { + // 简单的通配符支持 + let prefix = pattern.trim_end_matches('*'); + Ok(contents + .lines() + .enumerate() + .filter_map(|(line_num, line)| { + if line.starts_with(prefix) { + Some(SearchResult::new( + line.to_string(), + line_num + 1, + vec![(0, prefix.len())], + )) + } else { + None + } + }) + .collect()) + } else { + // 回退到普通搜索 + Ok(search_case_sensitive(pattern, contents)) + } +} + +// ============================================================================ +// 9. 辅助函数 - 演示字符串操作和算法 +// ============================================================================ + +/// 查找字符串中所有匹配的位置 +fn find_all_matches(text: &str, pattern: &str, case_sensitive: bool) -> Vec<(usize, usize)> { + let mut matches = Vec::new(); + + if pattern.is_empty() { + return matches; + } + + let search_text = if case_sensitive { text.to_string() } else { text.to_lowercase() }; + let search_pattern = if case_sensitive { pattern.to_string() } else { pattern.to_lowercase() }; + + let mut start = 0; + while let Some(pos) = search_text[start..].find(&search_pattern) { + let actual_pos = start + pos; + let end_pos = actual_pos + search_pattern.chars().count(); + matches.push((actual_pos, end_pos)); + + // 安全地移动到下一个字符位置 + if let Some((next_start, _)) = search_text[actual_pos..].char_indices().nth(1) { + start = actual_pos + next_start; + } else { + break; + } + } + + matches +} + +// ============================================================================ +// 10. 统计和分析功能 - 演示集合操作和高级特性 +// ============================================================================ + +/// 搜索统计信息 +#[derive(Debug, Default)] +pub struct SearchStats { + pub total_lines: usize, + pub matched_lines: usize, + pub total_matches: usize, + pub word_frequency: HashMap, +} + +impl SearchStats { + /// 分析搜索结果并生成统计信息 + pub fn analyze(contents: &str, results: &[SearchResult]) -> Self { + let total_lines = contents.lines().count(); + let matched_lines = results.len(); + let total_matches = results.iter().map(|r| r.match_positions.len()).sum(); + + // 词频统计 - 演示 HashMap 和迭代器链 + let word_frequency = contents + .split_whitespace() + .map(|word| word.to_lowercase().trim_matches(|c: char| !c.is_alphabetic()).to_string()) + .filter(|word| !word.is_empty()) + .fold(HashMap::new(), |mut acc, word| { + *acc.entry(word).or_insert(0) += 1; + acc + }); + + SearchStats { + total_lines, + matched_lines, + total_matches, + word_frequency, + } + } + + /// 获取最常见的词汇 + pub fn most_common_words(&self, n: usize) -> Vec<(String, usize)> { + let mut words: Vec<_> = self.word_frequency.iter().collect(); + words.sort_by(|a, b| b.1.cmp(a.1)); // 按频率降序排序 + words.into_iter() + .take(n) + .map(|(word, count)| (word.clone(), *count)) + .collect() + } +} + +// ============================================================================ +// 11. 测试模块 - 演示单元测试 +// ============================================================================ + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_search_case_sensitive() { + let query = "duct"; + let contents = "\nRust:\nsafe, fast, productive.\nPick three.\nDuct tape."; + + let results = search_case_sensitive(query, contents); + assert_eq!(results.len(), 1); + assert_eq!(results[0].line, "safe, fast, productive."); + assert_eq!(results[0].line_number, 3); + } + + #[test] + fn test_search_case_insensitive() { + let query = "rUsT"; + let contents = "\nRust:\nsafe, fast, productive.\nPick three.\nTrust me."; + + let results = search_case_insensitive(query, contents); + assert_eq!(results.len(), 2); + assert_eq!(results[0].line, "Rust:"); + assert_eq!(results[1].line, "Trust me."); + } + + #[test] + fn test_config_build() { + let args = vec![ + "program".to_string(), + "query".to_string(), + "filename".to_string(), + ]; + + let config = Config::build(args.into_iter()).unwrap(); + assert_eq!(config.query, "query"); + assert_eq!(config.file_path, "filename"); + } + + #[test] + fn test_search_mode_from_env() { + // 测试环境变量 + env::set_var("IGNORE_CASE", "1"); + let mode = SearchMode::from_env(); + assert_eq!(mode, SearchMode::CaseInsensitive); + + env::remove_var("IGNORE_CASE"); + let mode = SearchMode::from_env(); + assert_eq!(mode, SearchMode::CaseSensitive); + } + + #[test] + fn test_find_all_matches() { + let text = "hello world hello rust"; + let pattern = "hello"; + let matches = find_all_matches(text, pattern, true); + assert_eq!(matches.len(), 2); + assert_eq!(matches[0], (0, 5)); + assert_eq!(matches[1], (12, 17)); + } + + #[test] + fn test_search_stats() { + let contents = "hello world\nhello rust\nworld peace"; + let results = search_case_sensitive("hello", contents); + let stats = SearchStats::analyze(contents, &results); + + assert_eq!(stats.total_lines, 3); + assert_eq!(stats.matched_lines, 2); + assert_eq!(stats.total_matches, 2); + } +} + +// ============================================================================ +// 12. 文档测试示例 +// ============================================================================ + +/// 演示文档测试的函数 +/// +/// # 示例 +/// +/// ``` +/// use minigrep::example_function; +/// let result = example_function("test"); +/// assert_eq!(result, "Hello, test!"); +/// ``` +pub fn example_function(name: &str) -> String { + format!("Hello, {}!", name) +} \ No newline at end of file diff --git a/minigrep/src/main.rs b/minigrep/src/main.rs new file mode 100644 index 0000000..3a618b5 --- /dev/null +++ b/minigrep/src/main.rs @@ -0,0 +1,620 @@ +//! # Minigrep 主程序 +//! +//! 这是 minigrep 项目的主入口点,演示了 Rust 中的各种语法特性: +//! - 变量和常量 +//! - 数据类型 +//! - 函数定义和调用 +//! - 流程控制 +//! - 错误处理 +//! - 模块使用 +//! - 命令行参数处理 + +// 导入标准库模块 - 演示模块系统 +use std::env; // 环境变量和命令行参数 +use std::process; // 进程控制 +use std::io::{self, Write}; // I/O 操作 +use std::time::Instant; // 时间测量 + +// 导入我们的库 - 演示本地模块导入 +use minigrep::{ + Config, SearchResult, SearchStats, SearchMode, + run, example_function +}; + +// ============================================================================ +// 1. 常量定义 - 演示常量和静态变量 +// ============================================================================ + +/// 程序版本 - 编译时常量 +const VERSION: &str = "1.0.0"; + +/// 程序名称 +const PROGRAM_NAME: &str = "minigrep"; + +/// 默认最大结果数 +const DEFAULT_MAX_RESULTS: usize = 100; + +/// 静态变量 - 程序运行时的全局状态 +static mut SEARCH_COUNT: usize = 0; + +// ============================================================================ +// 2. 主函数 - 程序入口点 +// ============================================================================ + +/// 主函数 - 程序的入口点 +/// +/// 演示了: +/// - 命令行参数处理 +/// - 错误处理和传播 +/// - 流程控制 +/// - 变量绑定和可变性 +fn main() { + // ======================================================================== + // 变量声明和数据类型演示 + // ======================================================================== + + // 不可变变量 - Rust 默认不可变 + let program_start = Instant::now(); + + // 可变变量 - 使用 mut 关键字 + let mut exit_code = 0; + + // 基本数据类型 + let search_performed: bool = false; + let _unused_integer: i32 = 42; + let _unused_float: f64 = 3.14159; + let _unused_char: char = '🦀'; + + // 字符串类型 + let greeting = "欢迎使用 Minigrep!"; // &str - 字符串切片 + let _owned_string = String::from("这是一个拥有的字符串"); // String - 拥有的字符串 + + // 数组和元组 + let _numbers: [i32; 5] = [1, 2, 3, 4, 5]; + let _coordinates: (f64, f64) = (10.0, 20.0); + + // 打印欢迎信息 + println!("{}", greeting); + println!("版本: {}", VERSION); + println!("{}", "=".repeat(50)); + + // ======================================================================== + // 命令行参数处理 + // ======================================================================== + + // 收集命令行参数 - 演示迭代器和集合 + let args: Vec = env::args().collect(); + + // 检查参数数量 - 演示流程控制 + if args.len() < 2 { + print_usage(); + process::exit(1); + } + + // 检查帮助选项 - 演示模式匹配 + match args.get(1).map(|s| s.as_str()) { + Some("--help") | Some("-h") => { + print_help(); + return; + } + Some("--version") | Some("-v") => { + println!("{} {}", PROGRAM_NAME, VERSION); + return; + } + Some("--example") => { + run_examples(); + return; + } + _ => {} // 继续正常执行 + } + + // ======================================================================== + // 配置解析和验证 + // ======================================================================== + + // 构建配置 - 演示 Result 类型和错误处理 + let config = match Config::build(env::args()) { + Ok(config) => { + println!("✓ 配置解析成功"); + println!(" 查询: '{}'", config.query); + println!(" 文件: '{}'", config.file_path); + println!(" 模式: {:?}", config.search_mode); + config + } + Err(err) => { + eprintln!("❌ 配置错误: {}", err); + print_usage(); + process::exit(1); + } + }; + + // ======================================================================== + // 执行搜索 + // ======================================================================== + + println!("{}", "-".repeat(50)); + println!("🔍 开始搜索..."); + + // 记录搜索开始时间 + let search_start = Instant::now(); + + // 执行搜索 - 演示错误处理和匹配 + let search_results = match run(&config) { + Ok(results) => { + println!("✓ 搜索完成"); + results + } + Err(err) => { + eprintln!("❌ 搜索失败: {}", err); + exit_code = 1; + process::exit(exit_code); + } + }; + + // 计算搜索耗时 + let search_duration = search_start.elapsed(); + + // 更新搜索计数(演示 unsafe 代码) + unsafe { + SEARCH_COUNT += 1; + } + + // ======================================================================== + // 结果处理和显示 + // ======================================================================== + + // 显示搜索结果 - 演示条件语句和循环 + if search_results.is_empty() { + println!("❌ 没有找到匹配的结果"); + exit_code = 1; + } else { + println!("✓ 找到 {} 个匹配结果:", search_results.len()); + println!("{}", "=".repeat(50)); + + // 遍历并显示结果 - 演示迭代器和方法链 + for (index, result) in search_results.iter().enumerate() { + // 格式化输出 + let formatted = result.format_output(config.show_line_numbers); + + // 高亮显示匹配部分(简化版) + let highlighted = highlight_matches(&formatted, &config.query, &config.search_mode); + + println!("[{}] {}", index + 1, highlighted); + + // 显示匹配位置信息 + if !result.match_positions.is_empty() { + print!(" 匹配位置: "); + for (start, end) in &result.match_positions { + print!("{}:{} ", start, end); + } + println!(); + } + } + } + + // ======================================================================== + // 统计信息和性能分析 + // ======================================================================== + + println!("{}", "=".repeat(50)); + + // 读取文件内容进行统计分析 + if let Ok(contents) = std::fs::read_to_string(&config.file_path) { + let stats = SearchStats::analyze(&contents, &search_results); + display_statistics(&stats, search_duration); + } + + // 显示程序运行信息 + let total_duration = program_start.elapsed(); + println!("⏱️ 总运行时间: {:?}", total_duration); + println!("🔍 搜索耗时: {:?}", search_duration); + + unsafe { + println!("📊 本次会话搜索次数: {}", SEARCH_COUNT); + } + + // 根据结果设置退出码 + process::exit(exit_code); +} + +// ============================================================================ +// 3. 辅助函数 - 演示函数定义、参数和返回值 +// ============================================================================ + +/// 打印程序使用说明 +/// +/// 演示了: +/// - 函数定义 +/// - 字符串格式化 +/// - 标准输出 +fn print_usage() { + println!("使用方法:"); + println!(" {} <查询字符串> <文件路径> [选项]", PROGRAM_NAME); + println!(); + println!("选项:"); + println!(" --line-numbers, -n 显示行号"); + println!(" --max=<数量> 限制最大结果数量"); + println!(" --help, -h 显示帮助信息"); + println!(" --version, -v 显示版本信息"); + println!(" --example 运行示例"); + println!(); + println!("环境变量:"); + println!(" IGNORE_CASE=1 启用不区分大小写搜索"); + println!(); + println!("示例:"); + println!(" {} rust poem.txt", PROGRAM_NAME); + println!(" {} --line-numbers hello story.txt", PROGRAM_NAME); + println!(" IGNORE_CASE=1 {} RUST poem.txt", PROGRAM_NAME); +} + +/// 打印详细帮助信息 +fn print_help() { + println!("{} - Rust 文本搜索工具", PROGRAM_NAME); + println!("版本: {}", VERSION); + println!(); + print_usage(); + println!(); + println!("这个程序演示了以下 Rust 概念:"); + println!("• 变量和数据类型"); + println!("• 函数和闭包"); + println!("• 流程控制 (if/else, match, 循环)"); + println!("• 所有权和借用"); + println!("• 结构体和枚举"); + println!("• 模式匹配"); + println!("• 错误处理 (Result, Option)"); + println!("• 集合操作 (Vec, HashMap)"); + println!("• 迭代器和闭包"); + println!("• Trait 和泛型"); + println!("• 生命周期"); + println!("• 模块系统"); + println!("• 测试"); +} + +/// 运行示例代码 +/// +/// 演示了: +/// - 函数调用 +/// - 字符串操作 +/// - 集合创建和操作 +fn run_examples() { + println!("🚀 运行 Rust 语法示例..."); + println!("{}", "=".repeat(50)); + + // ======================================================================== + // 1. 变量和数据类型示例 + // ======================================================================== + + println!("📝 1. 变量和数据类型:"); + + // 基本类型 + let integer: i32 = 42; + let float: f64 = 3.14159; + let boolean: bool = true; + let character: char = '🦀'; + + println!(" 整数: {}", integer); + println!(" 浮点数: {:.2}", float); + println!(" 布尔值: {}", boolean); + println!(" 字符: {}", character); + + // 复合类型 + let tuple: (i32, f64, char) = (42, 3.14, '🦀'); + let array: [i32; 3] = [1, 2, 3]; + + println!(" 元组: {:?}", tuple); + println!(" 数组: {:?}", array); + + // 字符串类型 + let string_slice: &str = "Hello, Rust!"; + let owned_string: String = String::from("Hello, World!"); + + println!(" 字符串切片: {}", string_slice); + println!(" 拥有的字符串: {}", owned_string); + + println!(); + + // ======================================================================== + // 2. 函数示例 + // ======================================================================== + + println!("🔧 2. 函数示例:"); + + // 调用库中的示例函数 + let result = example_function("Rust"); + println!(" 函数调用结果: {}", result); + + // 调用本地函数 + let sum = add_numbers(10, 20); + println!(" 加法结果: {}", sum); + + // 高阶函数示例 + let numbers = vec![1, 2, 3, 4, 5]; + let doubled: Vec = numbers.iter().map(|x| x * 2).collect(); + println!(" 映射结果: {:?}", doubled); + + println!(); + + // ======================================================================== + // 3. 流程控制示例 + // ======================================================================== + + println!("🔄 3. 流程控制示例:"); + + // if/else 语句 + let number = 42; + if number > 0 { + println!(" {} 是正数", number); + } else if number < 0 { + println!(" {} 是负数", number); + } else { + println!(" {} 是零", number); + } + + // match 表达式 + let grade = 'A'; + let description = match grade { + 'A' => "优秀", + 'B' => "良好", + 'C' => "及格", + 'D' | 'F' => "不及格", + _ => "未知等级", + }; + println!(" 等级 {} 对应: {}", grade, description); + + // 循环示例 + print!(" 计数循环: "); + for i in 1..=5 { + print!("{} ", i); + } + println!(); + + println!(); + + // ======================================================================== + // 4. 所有权和借用示例 + // ======================================================================== + + println!("🔒 4. 所有权和借用示例:"); + + // 所有权转移 + let s1 = String::from("Hello"); + let s2 = s1; // s1 的所有权转移给 s2 + // println!("{}", s1); // 这行会编译错误 + println!(" 所有权转移后: {}", s2); + + // 借用 + let s3 = String::from("World"); + let len = calculate_length(&s3); // 借用 s3 + println!(" 字符串 '{}' 的长度是 {}", s3, len); // s3 仍然有效 + + println!(); + + // ======================================================================== + // 5. 结构体和枚举示例 + // ======================================================================== + + println!("📦 5. 结构体和枚举示例:"); + + // 结构体 + #[derive(Debug)] + struct Point { + x: f64, + y: f64, + } + + let point = Point { x: 3.0, y: 4.0 }; + println!(" 点坐标: {:?}", point); + + // 枚举 + #[derive(Debug)] + enum Color { + Red, + Green, + Blue, + RGB(u8, u8, u8), + } + + let color1 = Color::Red; + let color2 = Color::RGB(255, 128, 0); + println!(" 颜色1: {:?}", color1); + println!(" 颜色2: {:?}", color2); + + println!(); + + // ======================================================================== + // 6. 错误处理示例 + // ======================================================================== + + println!("⚠️ 6. 错误处理示例:"); + + // Result 类型 + let result = divide(10.0, 2.0); + match result { + Ok(value) => println!(" 除法结果: {}", value), + Err(err) => println!(" 除法错误: {}", err), + } + + // Option 类型 + let numbers = vec![1, 2, 3, 4, 5]; + match numbers.get(2) { + Some(value) => println!(" 索引2的值: {}", value), + None => println!(" 索引2不存在"), + } + + println!(); + + println!("✅ 示例运行完成!"); +} + +/// 简单的加法函数 - 演示函数参数和返回值 +fn add_numbers(a: i32, b: i32) -> i32 { + a + b // 表达式作为返回值 +} + +/// 计算字符串长度 - 演示借用 +fn calculate_length(s: &String) -> usize { + s.len() +} + +/// 除法函数 - 演示错误处理 +fn divide(a: f64, b: f64) -> Result { + if b == 0.0 { + Err("除数不能为零".to_string()) + } else { + Ok(a / b) + } +} + +/// 高亮显示匹配的文本 - 演示字符串操作和模式匹配 +/// +/// # 参数 +/// * `text` - 要处理的文本 +/// * `query` - 搜索查询 +/// * `mode` - 搜索模式 +/// +/// # 返回值 +/// 高亮后的文本字符串 +fn highlight_matches(text: &str, query: &str, mode: &SearchMode) -> String { + // 简化的高亮实现 + match mode { + SearchMode::CaseSensitive => { + text.replace(query, &format!("[{}]", query)) + } + SearchMode::CaseInsensitive => { + // 简化处理:直接替换 + let lower_text = text.to_lowercase(); + let lower_query = query.to_lowercase(); + if lower_text.contains(&lower_query) { + format!("[匹配] {}", text) + } else { + text.to_string() + } + } + SearchMode::Exact => { + if text.trim() == query { + format!("[精确匹配] {}", text) + } else { + text.to_string() + } + } + SearchMode::Regex(pattern) => { + format!("[正则: {}] {}", pattern, text) + } + } +} + +/// 显示统计信息 - 演示结构体使用和格式化输出 +/// +/// # 参数 +/// * `stats` - 搜索统计信息 +/// * `duration` - 搜索耗时 +fn display_statistics(stats: &SearchStats, duration: std::time::Duration) { + println!("📊 搜索统计:"); + println!(" 总行数: {}", stats.total_lines); + println!(" 匹配行数: {}", stats.matched_lines); + println!(" 总匹配数: {}", stats.total_matches); + + // 计算匹配率 + let match_rate = if stats.total_lines > 0 { + (stats.matched_lines as f64 / stats.total_lines as f64) * 100.0 + } else { + 0.0 + }; + println!(" 匹配率: {:.1}%", match_rate); + + // 显示最常见的词汇 + let common_words = stats.most_common_words(5); + if !common_words.is_empty() { + println!(" 最常见词汇:"); + for (word, count) in common_words { + println!(" '{}': {} 次", word, count); + } + } + + // 性能信息 + println!(" 搜索速度: {:.2} 行/秒", + stats.total_lines as f64 / duration.as_secs_f64()); +} + +// ============================================================================ +// 4. 条件编译示例 - 演示特性标志 +// ============================================================================ + +#[cfg(feature = "color")] +fn colorize_text(text: &str, _color: &str) -> String { + // 在实际实现中,这里会添加 ANSI 颜色代码 + format!("\x1b[32m{}\x1b[0m", text) // 绿色文本 +} + +#[cfg(not(feature = "color"))] +fn colorize_text(text: &str, _color: &str) -> String { + text.to_string() +} + +// ============================================================================ +// 5. 测试模块 - 演示集成测试 +// ============================================================================ + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_add_numbers() { + assert_eq!(add_numbers(2, 3), 5); + assert_eq!(add_numbers(-1, 1), 0); + } + + #[test] + fn test_divide() { + assert_eq!(divide(10.0, 2.0).unwrap(), 5.0); + assert!(divide(10.0, 0.0).is_err()); + } + + #[test] + fn test_calculate_length() { + let s = String::from("hello"); + assert_eq!(calculate_length(&s), 5); + } + + #[test] + fn test_highlight_matches() { + let result = highlight_matches("hello world", "hello", &SearchMode::CaseSensitive); + assert_eq!(result, "[hello] world"); + } +} + +// ============================================================================ +// 6. 文档注释和示例 +// ============================================================================ + +/// 演示文档测试的函数 +/// +/// # 参数 +/// * `input` - 输入字符串 +/// +/// # 返回值 +/// 处理后的字符串 +/// +/// # 示例 +/// +/// ``` +/// // 这个测试会在 `cargo test` 时运行 +/// let result = minigrep::main(); // 注意:这只是示例,实际不会这样调用 +/// // assert_eq!(result, expected); +/// ``` +/// +/// # 错误 +/// +/// 当输入为空时可能返回错误。 +/// +/// # 安全性 +/// +/// 这个函数是安全的,不会导致内存安全问题。 +pub fn process_input(input: &str) -> Result { + if input.is_empty() { + Err("输入不能为空") + } else { + Ok(format!("处理后的: {}", input)) + } +} diff --git a/minigrep/story.txt b/minigrep/story.txt new file mode 100644 index 0000000..0708b62 --- /dev/null +++ b/minigrep/story.txt @@ -0,0 +1,62 @@ +Once upon a time, in a land far away, +there lived a young programmer named Alex. + +Alex loved to code in many languages: +Python for data science, +JavaScript for web development, +and Rust for systems programming. + +One day, Alex discovered the power of Rust. +The language was fast, safe, and expressive. +Rust helped Alex write better code +with fewer bugs and better performance. + +"This is amazing!" Alex exclaimed. +"Rust makes programming so much more enjoyable!" + +Alex decided to learn more about Rust: +- Memory safety without garbage collection +- Zero-cost abstractions +- Pattern matching +- Trait system +- Ownership and borrowing +- Concurrency without data races + +The more Alex learned, the more excited they became. +Rust wasn't just another programming language; +it was a new way of thinking about software development. + +Alex started contributing to open source Rust projects, +joined the Rust community forums, +and even gave talks about Rust at local meetups. + +Years later, Alex became a Rust expert +and helped many other developers +discover the joy of programming in Rust. + +The end. + +--- + +Moral of the story: +Learning new programming languages +can open up new possibilities +and make you a better developer. + +Rust is particularly special because it combines +the performance of low-level languages +with the safety and expressiveness +of high-level languages. + +Whether you're building: +- Web servers +- Operating systems +- Game engines +- Blockchain applications +- Command-line tools +- Or anything else + +Rust can help you build it +faster, safer, and more reliably. + +Happy coding! 🦀 \ No newline at end of file diff --git a/minigrep/tests/integration_tests.rs b/minigrep/tests/integration_tests.rs new file mode 100644 index 0000000..fe82a1d --- /dev/null +++ b/minigrep/tests/integration_tests.rs @@ -0,0 +1,383 @@ +//! # Minigrep 集成测试 +//! +//! 这个文件演示了 Rust 中的集成测试,包括: +//! - 测试模块组织 +//! - 断言宏的使用 +//! - 错误情况测试 +//! - 性能测试 +//! - 文件 I/O 测试 + +use std::fs; +use std::env; +use std::process::Command; +use minigrep::*; + +// ============================================================================ +// 1. 基本功能测试 +// ============================================================================ + +#[test] +fn test_config_build_success() { + // 测试正确的配置构建 + let args = vec![ + "minigrep".to_string(), + "rust".to_string(), + "poem.txt".to_string(), + ]; + + let config = Config::build(args.into_iter()); + assert!(config.is_ok()); + + let config = config.unwrap(); + assert_eq!(config.query, "rust"); + assert_eq!(config.file_path, "poem.txt"); + assert_eq!(config.search_mode, SearchMode::CaseSensitive); +} + +#[test] +fn test_config_build_insufficient_args() { + // 测试参数不足的情况 + let args = vec!["minigrep".to_string()]; + + let config = Config::build(args.into_iter()); + assert!(config.is_err()); + + if let Err(err) = config { + assert!(err.contains("Not enough arguments") || err.contains("参数不足")); + } +} + +#[test] +fn test_config_with_options() { + // 测试带选项的配置 + let args = vec![ + "minigrep".to_string(), + "rust".to_string(), + "poem.txt".to_string(), + "--line-numbers".to_string(), + "--max=50".to_string(), + ]; + + let config = Config::build(args.into_iter()); + assert!(config.is_ok()); + + let config = config.unwrap(); + assert!(config.show_line_numbers); + assert_eq!(config.max_results, Some(50)); +} + +// ============================================================================ +// 2. 搜索功能测试 +// ============================================================================ + +#[test] +fn test_search_case_sensitive() { + // 测试区分大小写搜索 + let query = "duct"; + let contents = "\nRust:\nsafe, fast, productive.\nPick three.\nDuct tape."; + + let results = search_case_sensitive(query, contents); + assert_eq!(results.len(), 1); + assert_eq!(results[0].line, "safe, fast, productive."); + assert_eq!(results[0].line_number, 3); +} + +#[test] +fn test_search_case_insensitive() { + // 测试不区分大小写搜索 + let query = "rUsT"; + let contents = "\nRust:\nsafe, fast, productive.\nPick three.\nTrust me."; + + let results = search_case_insensitive(query, contents); + assert_eq!(results.len(), 2); + assert_eq!(results[0].line, "Rust:"); + assert_eq!(results[1].line, "Trust me."); +} + +#[test] +fn test_search_exact_match() { + // 测试精确匹配 + let query = "Rust"; + let contents = "\nRust\nRust is great\nrust\nRUST"; + + let results = search_exact(query, contents); + assert_eq!(results.len(), 1); + assert_eq!(results[0].line.trim(), "Rust"); +} + +#[test] +fn test_search_no_results() { + // 测试无匹配结果 + let query = "monomorphization"; + let contents = "\nRust:\nsafe, fast, productive.\nPick three."; + + let results = search_case_sensitive(query, contents); + assert_eq!(results.len(), 0); +} + +#[test] +fn test_search_multiple_matches_same_line() { + // 测试同一行多个匹配 + let query = "the"; + let contents = "the quick brown fox jumps over the lazy dog"; + + let results = search_case_sensitive(query, contents); + assert_eq!(results.len(), 1); + assert_eq!(results[0].match_positions.len(), 2); +} + +// ============================================================================ +// 3. 文件操作测试 +// ============================================================================ + +#[test] +fn test_run_with_valid_file() { + // 创建临时测试文件 + let test_content = "Hello world\nRust is awesome\nProgramming is fun"; + let test_file = "test_poem.txt"; + + fs::write(test_file, test_content).expect("无法创建测试文件"); + + // 测试运行 + let config = Config { + query: "Rust".to_string(), + file_path: test_file.to_string(), + search_mode: SearchMode::CaseSensitive, + show_line_numbers: false, + max_results: None, + }; + + let results = run(&config); + assert!(results.is_ok()); + + let results = results.unwrap(); + assert_eq!(results.len(), 1); + assert_eq!(results[0].line, "Rust is awesome"); + + // 清理测试文件 + fs::remove_file(test_file).expect("无法删除测试文件"); +} + +#[test] +fn test_run_with_nonexistent_file() { + // 测试不存在的文件 + let config = Config { + query: "test".to_string(), + file_path: "nonexistent_file.txt".to_string(), + search_mode: SearchMode::CaseSensitive, + show_line_numbers: false, + max_results: None, + }; + + let result = run(&config); + assert!(result.is_err()); +} + +// ============================================================================ +// 4. 统计功能测试 +// ============================================================================ + +#[test] +fn test_search_stats_analysis() { + // 测试搜索统计分析 + let contents = "Hello world\nRust is great\nProgramming with Rust\nHello again"; + let results = vec![ + SearchResult { + line: "Rust is great".to_string(), + line_number: 2, + match_positions: vec![(0, 4)], + }, + SearchResult { + line: "Programming with Rust".to_string(), + line_number: 1, + match_positions: vec![(17, 21)], + }, + ]; + + let stats = SearchStats::analyze(contents, &results); + + assert_eq!(stats.total_lines, 4); + assert_eq!(stats.matched_lines, 2); + assert_eq!(stats.total_matches, 2); + + // 测试词频统计 + let common_words = stats.most_common_words(3); + assert!(!common_words.is_empty()); +} + +// ============================================================================ +// 5. 错误处理测试 +// ============================================================================ + +#[test] +fn test_search_result_formatting() { + // 测试搜索结果格式化 + let result = SearchResult { + line: "Hello, Rust world!".to_string(), + line_number: 1, + match_positions: vec![(7, 11)], + }; + + // 测试不显示行号 + let formatted = result.format_output(false); + assert_eq!(formatted, "Hello, Rust world!"); + + // 测试显示行号 + let formatted = result.format_output(true); + assert_eq!(formatted, "1: Hello, Rust world!"); +} + +#[test] +fn test_search_mode_debug() { + // 测试搜索模式的调试输出 + let mode1 = SearchMode::CaseSensitive; + let mode2 = SearchMode::CaseInsensitive; + let mode3 = SearchMode::Exact; + let mode4 = SearchMode::Regex("test.*pattern".to_string()); + + // 确保所有模式都能正确格式化 + assert!(!format!("{:?}", mode1).is_empty()); + assert!(!format!("{:?}", mode2).is_empty()); + assert!(!format!("{:?}", mode3).is_empty()); + assert!(!format!("{:?}", mode4).is_empty()); +} + +// ============================================================================ +// 6. 性能测试 +// ============================================================================ + +#[test] +fn test_large_file_performance() { + // 创建大文件进行性能测试 + let mut large_content = String::new(); + for i in 0..1000 { + large_content.push_str(&format!("Line {} with some Rust content\n", i)); + } + + let start = std::time::Instant::now(); + let results = search_case_sensitive("Rust", &large_content); + let duration = start.elapsed(); + + // 验证结果正确性 + assert_eq!(results.len(), 1000); + + // 性能检查:应该在合理时间内完成(这里设为1秒,实际应该更快) + assert!(duration.as_secs() < 1, "搜索耗时过长: {:?}", duration); +} + +// ============================================================================ +// 7. 边界条件测试 +// ============================================================================ + +#[test] +fn test_empty_file() { + // 测试空文件 + let results = search_case_sensitive("anything", ""); + assert_eq!(results.len(), 0); +} + +#[test] +fn test_empty_query() { + // 测试空查询 + let contents = "Some content here"; + let results = search_case_sensitive("", contents); + // 空查询应该返回0个结果 + assert_eq!(results.len(), 0); +} + +#[test] +fn test_single_character_query() { + // 测试单字符查询 + let contents = "a\nb\nc\na"; + let results = search_case_sensitive("a", contents); + assert_eq!(results.len(), 2); +} + +#[test] +fn test_unicode_content() { + // 测试 Unicode 内容 + let contents = "Hello 世界\nRust 编程\n🦀 螃蟹"; + let results = search_case_sensitive("世界", contents); + assert_eq!(results.len(), 1); + assert_eq!(results[0].line, "Hello 世界"); +} + +// ============================================================================ +// 8. 命令行集成测试 +// ============================================================================ + +#[test] +fn test_command_line_integration() { + // 创建测试文件 + let test_content = "Hello world\nRust is great\nProgramming is fun"; + let test_file = "integration_test.txt"; + + fs::write(test_file, test_content).expect("无法创建测试文件"); + + // 测试命令行调用 + let output = Command::new("cargo") + .args(&["run", "--", "Rust", test_file]) + .output(); + + match output { + Ok(output) => { + // 检查程序是否成功运行 + let stdout = String::from_utf8_lossy(&output.stdout); + assert!(stdout.contains("Rust is great") || output.status.success()); + } + Err(_) => { + // 如果无法运行 cargo,跳过此测试 + println!("跳过命令行集成测试:无法运行 cargo"); + } + } + + // 清理测试文件 + let _ = fs::remove_file(test_file); +} + +// ============================================================================ +// 9. 辅助测试函数 +// ============================================================================ + +/// 创建临时测试文件的辅助函数 +fn create_temp_file(name: &str, content: &str) -> String { + let temp_path = format!("temp_{}", name); + fs::write(&temp_path, content).expect("无法创建临时文件"); + temp_path +} + +/// 清理临时文件的辅助函数 +fn cleanup_temp_file(path: &str) { + let _ = fs::remove_file(path); +} + +// ============================================================================ +// 10. 模块级测试设置和清理 +// ============================================================================ + +/// 测试模块的设置函数 +#[cfg(test)] +fn setup() { + // 设置测试环境 + env::set_var("RUST_LOG", "debug"); +} + +/// 测试模块的清理函数 +#[cfg(test)] +fn teardown() { + // 清理测试环境 + // 这里可以添加清理逻辑 +} + +// 使用 std::sync::Once 确保设置只运行一次 +use std::sync::Once; +static INIT: Once = Once::new(); + +/// 确保测试环境只初始化一次 +fn ensure_test_setup() { + INIT.call_once(|| { + setup(); + }); +} + +// 在每个需要设置的测试中调用 ensure_test_setup() \ No newline at end of file