1use std::{env, error::Error, fs};
2
3const CASE_INSENSITIVE: &str = "CASE_INSENSITIVE";
4
5#[derive(Debug, PartialEq)]
6pub struct Config {
7 query: String,
8 filename: String,
9 case_sensitive: bool,
10}
11
12impl Config {
13 pub fn new(mut args: std::env::Args) -> Result<Config, &'static str> {
14 args.next();
15
16 let query = match args.next() {
17 Some(arg) => arg,
18 None => return Err("Didn't get a Query String"),
19 }; let filename = match args.next() {
21 Some(arg) => arg,
22 None => return Err("Didn't get a file name"),
23 };
24
25 let case_sensitive = env::var(CASE_INSENSITIVE).is_err();
26
27 Ok(Config {
28 query: query,
29 filename: filename,
30 case_sensitive,
31 })
32 }
33}
34
35pub fn run(config: Config) -> Result<(), Box<dyn Error>> {
36 let contents = fs::read_to_string(&config.filename)?;
37
38 let results = if config.case_sensitive {
39 search(&config.query, &contents)
40 } else {
41 search_case_insensitive(&config.query, &contents)
42 };
43
44 results.iter().for_each(|line| println!("{}", line));
45
46 Ok(())
47}
48
49pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
63 contents
64 .lines()
65 .map(|line| line.trim())
66 .filter(|line| line.contains(query))
67 .collect()
68}
69
70pub fn search_case_insensitive<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
71 contents
72 .lines()
73 .map(|line| line.trim())
74 .filter(|line| line.to_lowercase().contains(&query.to_lowercase()))
75 .collect()
76}
77
78#[cfg(test)]
79mod public_api_tests {
80 use super::*;
81
82 #[test]
83 fn it_instantiate_config_passing_args() {
84 let args = env::args();
85
86 let expected_config = Config {
87 query: String::from("search term"),
88 filename: String::from("filename to search"),
89 case_sensitive: true,
90 };
91
92 if let Ok(config) = Config::new(args) {
93 assert_eq!(config, expected_config);
94 }
95 }
96
97 #[test]
98 fn it_searches_by_query() {
99 let query = "duct";
100 let contents = "\
101 Rust:
102 safe, fast, productive.
103 Pick three.
104 Duct tape.";
105
106 assert_eq!(vec!["safe, fast, productive."], search(query, contents));
107 }
108
109 #[test]
110 fn it_searches_with_case_sentitive() {
111 let query = "rUsT";
112 let contents = "\
113 Rust:
114 safe, fast, productive.
115 Pick three.
116 Trust me.";
117
118 assert_eq!(
119 vec!["Rust:", "Trust me."],
120 search_case_insensitive(query, contents)
121 );
122 }
123}