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