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