minigrep_lswarss/
lib.rs

1//! # minigrep_lswarss
2//!
3//! `minigrep_lswarss` is a very small part of Unix/Linux tool `grep` made with Rust for
4//! learning purpose while reading and studying the [Rust Book](https://doc.rust-lang.org/stable/book/)
5
6use std::env;
7use std::error::Error;
8use std::fs;
9
10pub struct Config {
11    pub query: String,
12    pub file_path: String,
13    pub ignore_case: bool,
14}
15
16impl Config {
17    pub fn build(mut args: impl Iterator<Item = String>) -> Result<Config, &'static str> {
18        args.next(); // first value of args is always name of the program and we do not care about it
19
20        let query = match args.next() {
21            Some(arg) => arg,
22            None => return Err("Didn't get a query string"),
23        };
24
25        let file_path = match args.next() {
26            Some(arg) => arg,
27            None => return Err("Didn't get a file path"),
28        };
29
30        let ignore_case = env::var("IGNORE_CASE").is_ok();
31
32        Ok(Config {
33            query,
34            file_path,
35            ignore_case,
36        })
37    }
38}
39
40pub fn run(config: Config) -> Result<(), Box<dyn Error>> {
41    let contents = fs::read_to_string(config.file_path)?;
42
43    let results = if config.ignore_case {
44        search_case_insensitive(&config.query, &contents)
45    } else {
46        search(&config.query, &contents)
47    };
48
49    for line in results {
50        println!("{line}")
51    }
52
53    Ok(())
54}
55
56/// Searches through the contents for given query (with case sensitivity) and returns the lines containing it
57/// # Examples
58///
59/// ```
60///  let content = String::from(
61///"this is text
62///this text is amazing,
63///this is amazing,
64///not good at all",
65///   );
66///
67/// let query = String::from("amazing");
68/// let amazing_lines = minigrep_lswarss::search(&query, &content);
69///
70/// assert_eq!(vec!["this text is amazing,", "this is amazing,"], amazing_lines)
71/// ```
72pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
73    contents
74        .lines()
75        .filter(|line| line.contains(query))
76        .collect()
77}
78
79pub fn search_case_insensitive<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
80    let query = query.to_lowercase();
81    let mut results = Vec::new();
82
83    for line in contents.lines() {
84        if line.to_lowercase().contains(&query) {
85            results.push(line);
86        }
87    }
88
89    results
90}
91
92#[cfg(test)]
93mod tests {
94    use super::*;
95
96    #[test]
97    fn case_sensitive() {
98        let query = "duct";
99        let content = "\
100Rust:
101safe, fast, productive.
102Pick three.
103Duck tape.";
104        assert_eq!(vec!["safe, fast, productive."], search(query, content))
105    }
106
107    #[test]
108    fn case_insensitive() {
109        let query = "rUsT";
110        let content = "\
111Rust:
112safe, fast, productive.
113Pick three.
114Trust me.";
115        assert_eq!(
116            vec!["Rust:", "Trust me."],
117            search_case_insensitive(query, content)
118        )
119    }
120}