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