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