grep_rezza/
lib.rs

1use std::env;
2use std::error::Error;
3use std::fs;
4use std::process;
5
6pub struct Config {
7    pub query: String,
8    pub file_path: String,
9    pub ignore_case: bool,
10}
11
12impl Config {
13    /// Init config
14    /// # Errors
15    ///  ```
16    /// Empty String get error "Didn't get a query string"
17    /// Args has less than 2 item get error "Didn't get a file path"
18    /// ```
19    pub fn build(mut args: impl Iterator<Item = String>) -> Result<Config, &'static str> {
20        args.next();
21
22        let query = match args.next() {
23            Some(s) => s,
24            None => return Err("Didn't get a query string".into()),
25        };
26        let file_path = match args.next() {
27            Some(s) => s,
28            None => return Err("Didn't get a file path".into()),
29        };
30        let ignore_case = match env::var("IGNORE_CASE") {
31            Ok(x) => match x.parse::<i32>().unwrap_or_else(|err| {
32                println!("[Config][build] Got err: {err}");
33                process::exit(1);
34            }) {
35                0 => false,
36                _ => true,
37            },
38            Err(_) => false,
39        };
40        Ok(Config {
41            query,
42            file_path,
43            ignore_case,
44        })
45    }
46}
47
48pub fn run(config: &Config) -> Result<(), Box<dyn Error>> {
49    let content = fs::read_to_string(&config.file_path)?;
50    let results = if config.ignore_case {
51        search_case_insensitive(&config.query, &content)
52    } else {
53        search(&config.query, &content)
54    };
55    for line in results {
56        println!("{line}");
57    }
58    Ok(())
59}
60
61pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
62    let mut results = Vec::new();
63    for line in contents.lines() {
64        if line.contains(query) {
65            results.push(line.trim());
66        }
67    }
68    results
69}
70/// search string in case insensitive
71/// # Examples
72///
73/// ```
74/// let query = "duct"
75///    let contents = "\
76/// Rust:
77/// safe, fast, productive.
78/// Pick three
79/// Duct tape.";
80/// assert_eq!(vec!["safe, fast, productive."], search(query, contents));
81/// ```
82///
83pub fn search_case_insensitive<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
84    let query = &query.to_lowercase();
85    let mut results = Vec::new();
86    for line in contents.lines() {
87        if line.to_lowercase().contains(query) {
88            results.push(line);
89        }
90    }
91    results
92}
93
94#[cfg(test)]
95mod tests {
96    use super::*;
97
98    #[test]
99    fn one_result() {
100        let query = "duct";
101        let contents = "\
102Rust:
103safe, fast, productive.
104Pick three
105Duct tape.";
106        assert_eq!(vec!["safe, fast, productive."], search(query, contents));
107    }
108
109    #[test]
110    fn case_insensitive() {
111        let query = "rUsT";
112        let contents = "\
113Rust:
114safe, fast, productive.
115Pick three.
116Trust me.";
117
118        assert_eq!(
119            vec!["Rust:", "Trust me."],
120            search_case_insensitive(query, contents)
121        );
122    }
123}