adhom_minigrep/
lib.rs

1//! # minigrep
2//!
3//! `minigrep` is a toy project to practice the fundamentals of the Rust programming laguage.
4
5use std::env;
6use std::error::Error;
7use std::fs;
8
9pub fn run(config: Config) -> Result<(), Box<dyn Error>> {
10    let contents = fs::read_to_string(config.file_path)?;
11
12    let results = if config.ignore_case {
13        search_case_insensitive(&config.query, &contents)
14    } else {
15        search(&config.query, &contents)
16    };
17    for line in results {
18        println!("{line}");
19    }
20    Ok(())
21}
22
23pub struct Config {
24    pub query: String,
25    pub file_path: String,
26    ignore_case: bool,
27}
28
29impl Config {
30    pub fn build(mut args: impl Iterator<Item = String>) -> Result<Config, &'static str> {
31        args.next();
32
33        let query = match args.next() {
34            Some(s) => s,
35            None => return Err("Didn't get a query string"),
36        };
37        let file_path = match args.next() {
38            Some(s) => s,
39            None => return Err("Didn't get a file path"),
40        };
41        let ignore_case = env::var("IGNORE_CASE").is_ok();
42
43        Ok(Config {
44            query,
45            file_path,
46            ignore_case,
47        })
48    }
49}
50/// Searches for a query in a string.
51///
52/// # Examples
53///
54/// ```
55/// use adhom_minigrep::search;
56/// assert_eq!(vec!["needle.", "A needle in a heystack."], search("needle", "\
57/// I am searching for a
58/// needle.
59/// A needle in a heystack."));
60/// ```
61pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
62    contents
63        .lines()
64        .filter(|line| line.contains(query))
65        .collect()
66}
67
68/// Searches for a query in a string (case insensitive).
69///
70/// # Examples
71///
72/// ```
73/// use adhom_minigrep::search_case_insensitive;
74/// assert_eq!(vec!["needle.", "A needle in a heystack."], search_case_insensitive("nEeDle", "\
75/// I am searching for a
76/// needle.
77/// A needle in a heystack."));
78/// ```
79pub fn search_case_insensitive<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
80    contents
81        .lines()
82        // Have to use & here because to_lowercase() returns a new String, so contains takes ownership of it
83        .filter(|line| line.to_lowercase().contains(&query.to_lowercase()))
84        .collect()
85}
86
87#[cfg(test)]
88mod tests {
89    use super::*;
90
91    #[test]
92    fn case_sensitive() {
93        let query = "duct";
94        let contents = "\
95Rust:
96safe, fast, productive.
97Pick three.
98Duct tape.";
99
100        assert_eq!(vec!["safe, fast, productive."], search(query, contents));
101    }
102
103    #[test]
104    fn case_insensitive() {
105        let query = "rUsT";
106        let contents = "\
107Rust:
108safe, fast, productive.
109Pick three.
110Trust me.";
111
112        assert_eq!(
113            vec!["Rust:", "Trust me."],
114            search_case_insensitive(query, contents)
115        );
116    }
117}