minigrep_vielenkz/
lib.rs

1// documentation for the src/lib.rs instead of anything which goes after the comment
2//! # minigrep
3//!
4//! `minigrep-vielenkz` is a lightweight analogue of `grep` to find a match in a file
5
6use std::{env, fs};
7use std::error::Error;
8
9pub struct Config {
10    pub query: String,
11    pub file_path: String,
12    pub ignore_case: bool,
13}
14
15impl Config {
16    // if we return Result<Something, str> - str is always static
17    /// Build [Config] from arguments provided.
18    /// The function is expected arguments:
19    /// 1. Name of the program
20    /// 2. Query to find in file
21    /// 3. Filename to search in
22    ///
23    /// If IGNORE_CASE flag is present in environment the search ignore case will be applied
24    ///
25    /// # Exception
26    /// In case the arguments are not provided the [Err] with string is returned
27    pub fn build(mut args: impl Iterator<Item=String>) -> Result<Config, &'static str> {
28        args.next();
29
30        let query = match args.next() {
31            Some(arg) => arg,
32            None => return Err("Didn't get a query string"),
33        };
34
35        let file_path = match args.next() {
36            Some(arg) => arg,
37            None => return Err("Didn't get a file path"),
38        };
39
40        let ignore_case = env::var("IGNORE_CASE").is_ok();
41
42        Ok(Config {
43            query,
44            file_path,
45            ignore_case,
46        })
47    }
48}
49
50// dyn - dynamic, all types which implemented trait Error
51pub fn run(config: Config) -> Result<(), Box<dyn Error>> {
52    let contents = fs::read_to_string(config.file_path)?;
53
54    let results = if config.ignore_case {
55        search_case_insensitive(&config.query, &contents)
56    } else {
57        search(&config.query, &contents)
58    };
59
60    for line in results {
61        println!("{line}");
62    }
63
64    Ok(())
65}
66
67/// Search for all lines in contents string containing query.
68/// Case sensitive.
69///
70/// # Examples
71///
72/// ```rust
73/// use minigrep_vielenkz::search;
74///
75/// let query = "duct";
76/// let contents = "\
77/// Rust:
78/// safe, fast, productive.
79/// Pick three.
80/// Duct tape.";
81///
82/// assert_eq!(vec!["safe, fast, productive."], search(query, contents));
83/// ```
84pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
85    contents
86        .lines()
87        .filter(|l| l.contains(query))
88        .collect()
89}
90
91/// Search for all lines in contents string containing query.
92/// Case insensitive.
93///
94/// # Examples
95///
96/// ```rust
97/// use minigrep_vielenkz::search_case_insensitive;
98///
99/// let query = "rUsT";
100/// let contents = "\
101/// Rust:
102/// safe, fast, productive.
103/// Pick three.
104/// Trust me.";
105///
106/// assert_eq!(vec!["Rust:", "Trust me."], search_case_insensitive(query, contents));
107/// ```
108pub fn search_case_insensitive<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
109    let query = query.to_lowercase();
110    contents
111        .lines()
112        .filter(|l| l.to_lowercase().contains(&query.to_string()))
113        .collect()
114}
115
116#[cfg(test)]
117mod tests {
118    use super::*;
119
120    #[test]
121    fn case_sensitive() {
122        let query = "duct";
123        let contents = "\
124Rust:
125safe, fast, productive.
126Pick three.
127Duct tape.";
128
129        assert_eq!(vec!["safe, fast, productive."], search(query, contents));
130    }
131
132    #[test]
133    fn case_insensitive() {
134        let query = "rUsT";
135        let contents = "\
136Rust:
137safe, fast, productive.
138Pick three.
139Trust me.";
140
141        assert_eq!(vec!["Rust:", "Trust me."], search_case_insensitive(query, contents));
142    }
143}