dont_use_this_grep/
lib.rs

1//! # Minigrep
2//! 
3//! minigrep defines a series of functions to find a string
4//! within a specified file and a Config struct designed to handle command line args
5
6use std::{env, error::Error, fs};
7
8#[derive(Debug, PartialEq)]
9pub struct Config {
10    pub query: String,
11    pub file_path: String,
12    pub ignore_case: bool,
13}
14
15impl Config {
16    /// Builds a Config structure off of an Iterable of Strings i.e. env::Args
17    /// Minimum args is 3 and first argument is skipped
18    /// # Examples
19    /// 
20    /// ```
21    /// use std::env;
22    /// use minigrep::Config;
23    /// 
24    /// let args = vec![String::from("PROGRAM_NAME"), String::from("QUERY"), String::from("FILE_PATH")].into_iter();
25    /// let config = Config {
26    ///     query: String::from("QUERY"),
27    ///     file_path: String::from("FILE_PATH"),
28    ///     ignore_case: env::var("IGNORE_CASE").is_ok(),
29    /// };
30    /// 
31    /// assert_eq!(config, Config::build(args).unwrap());
32    /// ```
33    pub fn build(mut args: impl Iterator<Item = String>) -> Result<Config, &'static str> {
34        args.next();
35        
36        let query = match args.next() {
37            Some(arg) => arg,
38            None => return Err("No query string"),
39        };
40
41        let file_path = match args.next() {
42            Some(arg) => arg,
43            None => return Err("No query string"),
44        };
45
46        let ignore_case = env::var("IGNORE_CASE").is_ok();
47
48        Ok(Config { query, file_path, ignore_case })
49    }
50}
51
52pub fn run(config: Config) -> Result<(), Box<dyn Error>> {
53    let contents = fs::read_to_string(config.file_path)?;
54
55    let results = if config.ignore_case {
56        search_case_insensitive(&config.query, &contents)
57    } else {
58        search(&config.query, &contents)
59    };
60
61    for line in results {
62        println!("{line}")
63    }
64
65    Ok(())
66}
67
68pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
69    contents
70        .lines()
71        .filter(|line| line.contains(query))
72        .collect()
73}
74
75pub fn search_case_insensitive<'a>(
76    query: &str,
77    contents: &'a str
78) -> Vec<&'a str> {
79    let query = query.to_lowercase();
80    let mut results = Vec::new();
81
82    for line in contents.lines() {
83        if line.to_lowercase().contains(&query) {
84            results.push(line);
85        }
86    }
87
88    results
89}
90
91#[cfg(test)]
92mod tests {
93    use super::*;
94
95    #[test]
96    fn case_sensitive() {
97        let query = "duct";
98        let contents = "\
99Rust:
100safe, fast, productive.
101Pick three.
102Duct tape.";
103
104        assert_eq!(vec!["safe, fast, productive."], search(query, contents));
105    }
106
107    #[test]
108    fn case_insensitive() {
109        let query = "rUsT";
110        let contents = "\
111Rust:
112safe, fast, productive.
113Trust me.";
114
115        assert_eq!(
116            vec!["Rust:", "Trust me."],
117            search_case_insensitive(query, contents)
118        );
119    }
120}