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