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