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 mut args: impl Iterator<Item = String>,
18 ) -> Result<Config, &'static str>{
19 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 println!("读取了文件 {}\n", config.file_path);
49
50 let contents: String = fs::read_to_string(config.file_path)?;
51
52 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
67pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
69 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();
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}