1use std::env;
10use std::error::Error;
11use std::fs;
12
13pub fn run(config: Config) -> Result<(), Box<dyn Error>> {
14 let contents = fs::read_to_string(config.filename)?;
15
16 let result = if config.case_sensitive {
17 search(&config.query, &contents)
18 } else {
19 search_case_insensitive(&config.query, &contents)
20 };
21
22 for line in result {
23 println!("{}", line);
24 }
25
26 return Ok(());
27}
28
29pub struct Config {
30 pub query: String,
31 pub filename: String,
32 pub case_sensitive: bool,
33}
34
35impl Config {
36 pub fn new(mut args: std::env::Args) -> Result<Config, &'static str> {
37 if args.len() < 3 {
38 return Err("not enough arguments");
39 }
40 args.next();
41
42 let query = match args.next() {
43 Some(arg) => arg,
44 None => return Err("Didn't get a query string"),
45 };
46 let filename = match args.next() {
47 Some(arg) => arg,
48 None => return Err("Didn't get a file name"),
49 };
50 let case_sensitive = env::var("CASE_INSENSITIVE").is_err();
51
52 Ok(Config {
53 query,
54 filename,
55 case_sensitive,
56 })
57 }
58}
59
60pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
61 contents
70 .lines()
71 .filter(|line| line.contains(&query))
72 .collect()
73}
74
75pub fn search_case_insensitive<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
76 contents
86 .lines()
87 .filter(|line| line.to_lowercase().contains(&query))
88 .collect()
89}
90
91#[cfg(test)]
92mod tests {
93 use super::*;
94
95 #[test]
96 fn one_result() {
97 let query = "ihoey";
98 let contents = "my name is xixi\n
99ihoey
100IHOEY
101and i am from china";
102
103 assert_eq!(vec!["my name is xixi"], search(query, contents));
104 }
105
106 #[test]
107 fn case_insensitive() {
108 let query = "IHOEY";
109 let contents = "my name is xixi\n
110ihoey
111IHOEY
112and i am from china";
113
114 assert_eq!(
115 vec!["ihoey", "IHOEY"],
116 search_case_insensitive(query, contents)
117 );
118 }
119}