1use std::fs;
2use std::error::Error;
3use std::env;
4
5pub struct Config {
6 pub query: String,
7 pub file_path: String,
8 pub ignore_case:bool,
9}
10impl Config {
11 pub fn build(mut args: impl Iterator<Item=String>)->Result<Config, &'static str> {
12 args.next();
13
14 let query=match args.next(){
19 Some(arg)=>arg,
20 None=>return Err("Didn't get a query string"),
21 };
22 let file_path=match args.next(){
23 Some(arg)=>arg,
24 None=>return Err("Didn't get a file path"),
25 };
26
27 let ignore_case=env::var("IGNORE_CASE").is_ok();
28
29 Ok(Config { query, file_path, ignore_case })
30 }
31}
32
33pub fn run(config: Config) -> Result<(), Box<dyn Error>> {
34 let contents=fs::read_to_string(config.file_path)?;
35 let results = if config.ignore_case {
38 search_case_insensitive(&config.query, &contents)
39 } else {
40 search(&config.query, &contents)
41 };
42
43 for line in results {
44 println!("{line}");
45 }
46
47 Ok(())
48}
49
50
51pub fn search<'a>(query: &str, contents: &'a str)-> Vec<&'a str> {
52 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()
72}
73
74
75
76#[cfg(test)]
77mod tests {
78 use super::*;
79
80 #[test]
81 fn case_sensitive() {
82 let query="duct";
83 let contents="\
84Rust:
85safe, fast, productive.
86Pick three.
87Duct tape.";
88 assert_eq!(vec!["safe, fast, productive."], search(query, contents));
89 }
90
91 #[test]
92 fn case_insensitive() {
93 let query="rUsT";
94 let contents="\
95Rust:
96safe, fast, productive.
97Pick three.
98Trust me.";
99 assert_eq!(vec!["Rust:","Trust me."], search_case_insensitive(query, contents));
100 }
101
102
103}