1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
//! # dubugrep
//!
//! This module is used to search for text in files.
//! Very much like `grep`.

pub use self::funcs::run;
pub use self::types::Config;

pub mod types {
    pub struct Config {
        pub query: String,
        pub filename: String,
        pub case_sensitive: bool,
    }

    impl Config {
        /// Creates a new config struct from command line arguments
        ///
        /// # Examples
        /// ```
        /// dubugrep::Config::new(std::env::args());
        /// ```
        pub fn new(mut args: std::env::Args) -> Result<Config, &'static str> {
            args.next();

            let query = match args.next() {
                Some(arg) => arg,
                None => return Err("Didn't get a query string"),
            };

            let filename = match args.next() {
                Some(arg) => arg,
                None => return Err("Didn't get a file name"),
            };

            let case_sensitive = std::env::var("CASE_INSENSITIVE").is_err();

            Ok(Config {
                query,
                filename,
                case_sensitive,
            })
        }
    }
}

pub mod funcs {
    use crate::types::*;

    /// Prints the lines in a file that contain the search string
    ///
    /// The file and search string will be supplied through the `Config`
    pub fn run(config: Config) -> Result<(), Box<dyn std::error::Error>> {
        let content = std::fs::read_to_string(config.filename)?;

        let results = if config.case_sensitive {
            search(&config.query, &content)
        } else {
            search_case_insensitive(&config.query, &content)
        };

        for line in results {
            println!("{}", line);
        }

        Ok(())
    }

    pub fn search<'a>(query: &str, content: &'a str) -> Vec<&'a str> {
        content
            .lines()
            .filter(|line| line.contains(query))
            .collect()
    }

    pub fn search_case_insensitive<'a>(query: &str, content: &'a str) -> Vec<&'a str> {
        let query = query.to_lowercase();
        content
            .lines()
            .filter(|line| line.to_lowercase().contains(&query))
            .collect()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn case_sensitive() {
        let query = "duct";
        let content = "\
Rust:
safe, fast, productive.
Pick three.
Duct tape.";
        assert_eq!(
            vec!["safe, fast, productive."],
            funcs::search(query, content)
        );
    }

    #[test]
    fn case_insensitive() {
        let query = "rUsT";
        let content = "\
Rust:
safe, fast, productive.
Pick three.
Trust me.";
        assert_eq!(
            vec!["Rust:", "Trust me."],
            funcs::search_case_insensitive(query, content)
        );
    }
}