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