1use std::error::Error;
2use std::fs;
3use std::env;
4
5pub fn run(config: Config) -> Result<(), Box<dyn Error>> {
9 let contents = fs::read_to_string(config.filename)?;
10
11 let results = if config.case_sensitive {
12 search(&config.query, &contents)
13 } else {
14 search_case_insensitive(&config.query, &contents)
15 };
16
17 for line in results {
18 println!("{}", line);
19 }
20 Ok(())
21}
22
23pub struct Config {
33 pub query: String,
34 pub filename: String,
35 pub case_sensitive: bool,
36}
37
38impl Config {
40 pub fn new(mut args: std::env::Args) -> Result<Config, &'static str> {
41 args.next();
42
43 let query = match args.next() {
44 Some(arg) => arg,
45 None => return Err("Didn't get a query string"),
46 };
47
48 let filename = match args.next() {
49 Some(arg) => arg,
50 None => return Err("Didn't get a file name"),
51 };
52
53 let case_sensitive = env::var("CASE_INSENSITIVE").is_err();
54
55 Ok(Config{query, filename, case_sensitive})
56 }
57}
58
59#[cfg(test)]
60mod tests {
61 use super::*;
62
63 #[test]
64 fn one_result() {
65 let query = "duct";
66 let contents = "\
67Rust:
68safe, fast, productive.
69Pick three.";
70
71 assert_eq!(
72 vec!["safe, fast, productive."],
73 search(query, contents)
74 );
75 }
76
77 #[test]
78 fn case_insensitive() {
79 let query = "rUst";
80 let contents = "\
81Rust:
82safe, fast, productive.
83Trust me.";
84
85 assert_eq!(
86 vec!["Rust:", "Trust me."],
87 search_case_insensitive(query, contents)
88 );
89 }
90}
91
92pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
93 contents.lines()
102 .filter(|line| line.contains(query))
103 .collect()
104}
105
106pub fn search_case_insensitive<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
107 let query = query.to_lowercase();
108 contents.lines()
109 .filter(|line| line.to_lowercase().contains(&query))
110 .collect()
111
112 }
121