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