1use std::error::Error;
2use std::{env, fs};
3
4pub struct Config<'a> {
5 pub query: &'a str,
6 pub file_path: &'a str,
7 pub ignore_case: bool,
8}
9
10impl<'a> Config<'a> {
11 pub fn build(args: &'a [String]) -> Result<Config<'a>, &'static str> {
12 if args.len() < 3 {
13 return Err("not enough arguments");
14 }
15
16 let ignore_case = env::var("IGNORE_CASE").is_ok();
17
18 Ok(Config {
19 query: &args[1],
20 file_path: &args[2],
21 ignore_case,
22 })
23 }
24}
25
26pub fn minigrep(config: &Config) -> Result<String, Box<dyn Error>> {
27 let query = config.query;
28 let file_path = config.file_path;
29 let ignore_case = config.ignore_case;
30
31 let content = fs::read_to_string(file_path)?;
32 let lines = if ignore_case {
33 search_case_insensitive(query, &content)
34 } else {
35 search(query, &content)
36 };
37
38 Ok(lines.iter().map(|x| format!("{x}\n")).collect())
39}
40
41fn search<'a>(query: &str, content: &'a str) -> Vec<&'a str> {
42 content
43 .split('\n')
44 .filter(|line| line.contains(query))
45 .collect()
46}
47
48fn search_case_insensitive<'a>(query: &str, content: &'a str) -> Vec<&'a str> {
49 let query = &query.to_lowercase();
50 content
51 .split('\n')
52 .filter(|line| line.to_lowercase().contains(query))
53 .collect()
54}
55
56#[cfg(test)]
57mod tests {
58 use super::*;
59
60 #[test]
61 fn case_sensitive() {
62 let query = "duct";
63 let contents = "\
64Rust:
65safe, fast, productive.
66Pick three.";
67
68 assert_eq!(vec!["safe, fast, productive."], search(query, contents));
69 }
70
71 #[test]
72 fn case_insensitive() {
73 let query = "rUsT";
74 let contents = "\
75Rust:
76safe, fast, productive.
77Pick three.
78Trust me.";
79
80 assert_eq!(
81 vec!["Rust:", "Trust me."],
82 search_case_insensitive(query, contents)
83 );
84 }
85}