1use std::error::Error;
2use std::fs;
3use std::env;
4
5#[cfg(test)]
6mod tests {
7 use super::*;
8
9 #[test]
10 fn one_result() {
11 let query = "duct";
12 let contents = "\
13Rust:
14safe, fast, productive.
15Pick three.
16Duct tape.";
17
18 assert_eq!(
19 vec!["safe, fast, productive."],
20 search(query, contents)
21 );
22 }
23
24 #[test]
25 fn case_insensitive(){
26 let query = "rUsT";
27 let contents = "\
28Rust:
29safe, fast, productive.
30Pick three.
31Trust me.";
32
33 assert_eq!(
34 vec!["Rust:", "Trust me."],
35 search_case_insensitive(query, contents)
36 );
37 }
38}
39
40pub fn search_case_insensitive<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
41 let query = query.to_lowercase();
42 let mut results = Vec::new();
43
44 for line in contents.lines(){
45 if line.to_lowercase().contains(&query){
46 results.push(line);
47 }
48 }
49
50 results
51}
52
53pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
69 let mut results = Vec::new();
70
71 for line in contents.lines(){
72 if line.contains(query){
73 results.push(line);
74 }
75 }
76
77 results
78}
79
80pub struct Config{
81 pub query: String,
82 pub filename: String,
83 pub case_sensitive: bool,
84}
85
86impl Config{
87 pub fn build(args: &[String]) -> Result<Config, &'static str>{
88 if args.len() < 3 {
89 return Err("not enough arguments");
90 }
91
92 let query = args[1].clone();
93 let filename = args[2].clone();
94 let case_sensitive = env::var("CASE_INSENSITIVE").is_err();
95
96 Ok(Config { query, filename, case_sensitive})
97 }
98}
99
100pub fn run(config: Config) -> Result<(), Box<dyn Error>>{
101 let contents = fs::read_to_string(config.filename)?;
102
103 let results = if config.case_sensitive {
104 search(&config.query, &contents)
105 } else{
106 search_case_insensitive(&config.query, &contents)
107 };
108
109 for line in search(&config.query, &contents){
110 println!("{}", line);
111 }
112
113 Ok(())
114}