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