tim_rust_minigrep/
lib.rs

1use std::error::Error;
2use std::{fs};
3
4use std::{env};
5
6pub struct Config {
7    pub query: String,
8    pub file_path: String,
9    pub ignore_case: bool,
10}
11
12impl Config {
13    pub fn build (
14        // args: &[String]
15        // 参数是一个迭代器
16        // 意味着 args 可以是任何实现了 Iterator 的类型并返回 String 项(item)
17        mut args: impl Iterator<Item = String>,
18    ) -> Result<Config, &'static str>{
19        // if args.len() < 3 {
20        //     return Err("not enough arguments");
21        // }
22
23        // let query = args[1].clone();
24        // let file_path = args[2].clone();
25        
26        // 跳过第一个路径名称信息
27        args.next();
28
29        let query = match args.next() {
30            Some(arg) => arg,
31            None => return Err("Didn't get a query string"),
32        };
33
34        let file_path = match args.next() {
35            Some(arg) => arg,
36            None => return Err("Didn't get a file path"),
37        };
38
39        let ignore_case = env::var("IGNORE_CASE").is_ok();
40    
41        Ok(Config { query, file_path, ignore_case })
42    }
43}
44
45
46pub fn run(config: Config) -> Result<(), Box<dyn Error>> {
47    // 读取文件
48    println!("读取了文件 {}\n", config.file_path);
49
50    let contents: String = fs::read_to_string(config.file_path)?;
51
52    // println!("读取文本如下:\n{contents}");
53
54    let res = if config.ignore_case {
55        search_case_insensitive(&config.query, &contents)
56    } else {
57        search(&config.query, &contents)
58    };
59
60    for line in res{
61        println!("{}", line);
62    }
63
64    Ok(())
65}
66
67// 返回包含关键词的那一行文本
68pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
69    // let mut res = Vec::new();
70
71    // for one_line in contents.lines() {
72    //     if one_line.contains(query) {
73    //         res.push(one_line)
74    //     }
75    // }
76
77    // res
78
79
80    // 通过 filter 来重构,让代码变得超级简洁,减少了变量(可变状态)的数量
81    contents
82        .lines()
83        .filter(|line| line.contains(query))
84        .collect()
85}
86
87pub fn search_case_insensitive<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
88    // let query = query.to_lowercase();
89
90    // let mut res = Vec::new();
91
92    // for one_line in contents.lines() {
93    //     if one_line.to_lowercase().contains(&query) {
94    //         res.push(one_line)
95    //     }
96    // }
97
98    // res
99
100
101    let query = query.to_lowercase();
102    contents
103        .lines()
104        .filter(|line| line.to_lowercase().contains(&query))
105        .collect()
106}
107
108#[cfg(test)]
109mod tests {
110    use super::*;
111
112    #[test]
113    fn case_sensitive() {
114        let query = "duct";
115        let contents = "\
116Rust:
117safe, fast, productive.
118Pick three.
119Duct tape.";
120
121        assert_eq!(vec!["safe, fast, productive."], search(query, contents));
122    }
123
124    #[test]
125    fn case_insensitive() {
126        let query = "rUsT";
127        let contents = "\
128Rust:
129safe, fast, productive.
130Pick three.
131Trust me.";
132
133        assert_eq!(
134            vec!["Rust:", "Trust me."],
135            search_case_insensitive(query, contents)
136        );
137    }
138}