1use std::fs;
2use std::env;
3use std::error::Error;
4
5pub struct Config {
7 pub query: String,
8 pub filename: String,
9 pub case_sensitive: bool,
10}
11
12impl Config {
13 pub fn new(mut args: env::Args) -> Result<Config, &'static str> {
15 args.next();
16 let query = match args.next(){
17 Some(s) => s,
18 None => return Err("Didn't get a query string"),
19 };
20
21 let filename = match args.next(){
22 Some(s) => s,
23 None => return Err("Didn't get a filename string"),
24 };
25
26 let case_sensitive = env::var("CASE_INSENSITIVE").is_err();
27
28 Ok(Config { query, filename, case_sensitive})
29 }
30}
31
32pub fn run(config: Config) -> Result<(), Box<dyn Error>>{
34 let contents = fs::read_to_string(config.filename)?;
35
36 let result = if config.case_sensitive {
37 search_case_sensitive(&config.query, &contents)
38 } else {
39 search_case_insensitive(&config.query, &contents)
40 };
41
42 for line in result {
43 println!("{}", line);
44 }
45 Ok(())
46}
47
48fn search_case_sensitive<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
49 contents
50 .lines()
51 .filter(|line| line.contains(query))
52 .collect()
53}
54
55fn search_case_insensitive<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
56 contents
57 .lines()
58 .filter(|line| line.to_lowercase().contains(&query.to_lowercase()))
59 .collect()
60}
61
62#[cfg(test)]
63mod tests {
64 use super::*;
65
66 #[test]
67 fn case_sensitive() {
68 let query = "duct";
69 let contents = "\
70Rust:
71safe, fast, productive.
72Pick three.
73Duct tape.";
74 assert_eq!(vec!["safe, fast, productive."], search_case_sensitive(query, contents));
75 }
76
77 #[test]
78 fn case_insensitive() {
79 let query = "rUst";
80 let contents = "\
81Rust:
82safe, fast, productive.
83Pick three.
84Trust me.";
85 assert_eq!(vec!["Rust:", "Trust me."], search_case_insensitive(query, contents));
86 }
87}