1use std::{env, error::Error, fs};
2
3#[cfg(test)]
4mod tests {
5 use super::*;
6
7 #[test]
8 fn case_sensitive() {
9 let query = "duck";
10 let contents = "\
11From troubles of the world
12I turn to ducks,
13Beautiful comical things
14Duck supremacy";
15
16 assert_eq!(vec!["I turn to ducks,"], search(query, contents));
17 }
18
19 #[test]
20 fn case_insensitive() {
21 let query = "DuCk";
22 let contents = "\
23From troubles of the world
24I turn to ducks,
25Beautiful comical things
26Duck supremacy";
27
28 assert_eq!(
29 vec!["I turn to ducks,", "Duck supremacy"],
30 search_case_insensitive(query, contents)
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 result = if config.case_sensitive {
39 search(&config.query, &contents)
40 } else {
41 search_case_insensitive(&config.query, &contents)
42 };
43
44 for line in result {
45 println!("{}", line);
46 }
47
48 Ok(())
49}
50
51pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
52 contents
53 .lines()
54 .filter(|line| line.contains(query))
55 .collect()
56}
57
58pub fn search_case_insensitive<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
59 let query = &query.to_lowercase();
60 contents
61 .lines()
62 .filter(|line| line.to_lowercase().contains(query))
63 .collect()
64}
65
66pub struct Config {
67 pub query: String,
68 pub filename: String,
69 pub case_sensitive: bool,
70}
71
72impl Config {
73 pub fn new(mut args: env::Args) -> Result<Config, &'static str> {
74 args.next();
75
76 let query = match args.next() {
77 Some(arg) => arg,
78 None => return Err("Didn't get a query string"),
79 };
80
81 let filename = match args.next() {
82 Some(arg) => arg,
83 None => return Err("Didn't gate a file name"),
84 };
85 let case_sensitive = if !env::var("CASE_INSENSITIVE").is_err() {
86 env::var("CASE_INSENSITIVE").is_err()
87 } else {
88 args.next().is_none()
89 };
90
91 Ok(Config {
92 query,
93 filename,
94 case_sensitive,
95 })
96 }
97}