minigrep666/
lib.rs

1use std::env;
2use std::error::Error;
3use std::fs;
4
5/// Struct to configure command options
6pub struct Config {
7    pub query: String,
8    pub filename: String,
9    pub case_sensitive: bool,
10}
11
12impl Config {
13    pub fn new(mut args: env::Args) -> Result<Config, &'static str> {
14        args.next();
15
16        let query = match args.next() {
17            Some(arg) => arg,
18            None => return Err("Didn't get a query string"),
19        };
20
21        let filename = match args.next() {
22            Some(arg) => arg,
23            None => return Err("Didn't get a filename"),
24        };
25
26        let case_sensitive = env::var("CASE_INSENSITIVE").is_err();
27
28        Ok(Config {
29            query,
30            filename,
31            case_sensitive,
32        })
33    }
34}
35
36pub fn run(config: Config) -> Result<(), Box<dyn Error>> {
37    let contents = fs::read_to_string(config.filename)?;
38
39    let results = if config.case_sensitive {
40        search(&config.query, &contents)
41    } else {
42        search_case_insensitive(&config.query, &contents)
43    };
44
45    println!("Matched lines:");
46    for line in results {
47        println!("{}", line);
48    }
49
50    Ok(())
51}
52
53/// Searches (case-sensitive) text in lines. Prints any line that contains the query string.
54///
55/// # Examples
56///
57/// ```
58/// let query = "abc";
59/// let contents = "\
60/// abc def
61/// sde fdf
62/// def abc";
63///
64/// assert_eq!(minigrep666::search(query, contents), ["abc def", "def abc"]);
65/// ```
66pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
67    contents
68        .lines()
69        .filter(|line| line.contains(query))
70        .collect()
71}
72
73pub fn search_case_insensitive<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
74    let query = query.to_lowercase();
75
76    contents
77        .lines()
78        .filter(|line| line.to_lowercase().contains(&query))
79        .collect()
80}
81
82#[cfg(test)]
83mod tests {
84    use super::*;
85
86    #[test]
87    fn one_result() {
88        let query = "fast";
89        let contents = "\
90Rust:
91safe, fast, productive.
92Pick three.";
93
94        assert_eq!(vec!["safe, fast, productive."], search(query, contents));
95    }
96
97    #[test]
98    fn not_found() {
99        let query = "slow";
100        let contents = "\
101Rust:
102safe, fast, productive.
103Pick three.";
104
105        assert_eq!(Vec::<&str>::new(), search(query, contents));
106    }
107
108    #[test]
109    fn case_sensitive() {
110        let query = "duct";
111        let contents = "\
112Rust:
113safe, fast, productive.
114Pick three.
115Duct tape.";
116
117        assert_eq!(vec!["safe, fast, productive."], search(query, contents));
118    }
119
120    #[test]
121    fn case_insensitive() {
122        let query = "rUsT";
123        let contents = "\
124Rust:
125safe, fast, productive.
126Pick three.
127Trust me.";
128
129        assert_eq!(
130            vec!["Rust:", "Trust me."],
131            search_case_insensitive(query, contents)
132        );
133    }
134}