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