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