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