milligrep/lib.rs
1/*
2Copyright (C) 2021 Jayesh Mann <jayeshmann06@gmail.com>
3
4This program is free software: you can redistribute it and/or modify
5it under the terms of the GNU Affero General Public License as published
6by the Free Software Foundation, either version 3 of the License, or
7(at your option) any later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12GNU Affero General Public License for more details.
13
14You should have received a copy of the GNU Affero General Public License
15along with this program. If not, see <https://www.gnu.org/licenses/>. */
16
17//! Custom simplified implementation of grep
18
19use std::env;
20use std::error::Error;
21use std::fs;
22
23/// The necessary configurations for initializing milligrep.
24///
25pub struct Config {
26 /// The pattern to search for in the contents of the file.
27 pub query: String,
28 /// The file along with the path in which the pattern search will be conducted.
29 pub filename: String,
30 /// CASE_SENSITIVE flag, either true or false
31 pub case_sensitive: bool,
32}
33
34impl Config {
35 /// Initializes a new Config.
36 ///
37 /// Returns error if incorrect option is passed.
38 pub fn new(mut args: env::Args) -> Result<Config, &'static str> {
39 args.next();
40 let query = match args.next() {
41 Some(arg) => arg,
42 None => return Err("Didn't get a query string"),
43 };
44 let filename = match args.next() {
45 Some(arg) => arg,
46 None => return Err("Didn't get the file name"),
47 };
48
49 let case_sensitive = env::var("CASE_INSENSITIVE").is_err();
50
51 Ok(Config {
52 query,
53 filename,
54 case_sensitive,
55 })
56 }
57}
58/// Runs the library logic.
59/// Expects a reference to [`Config`](struct@Config).
60///
61/// # Errors
62///
63/// Returns error on the following situations:
64///
65/// - `file` doesn't exist.
66/// - `file` could not be read (unauthorized read permission).
67pub fn run(config: Config) -> Result<(), Box<dyn Error>> {
68 let contents = fs::read_to_string(config.filename)?;
69
70 let results = if config.case_sensitive {
71 search(&config.query, &contents)
72 } else {
73 search_case_insensitive(&config.query, &contents)
74 };
75
76 for line in results {
77 println!("{}", line);
78 }
79
80 Ok(())
81}
82/// Returns the line(s) where the pattern is matched in the contents of the file.
83/// It is case sensitive.
84pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
85 contents
86 .lines()
87 .filter(|line| line.contains(query))
88 .collect()
89}
90/// Returns the line(s) where the pattern is matched in the contents of the file.
91/// It is case in-sensitive.
92pub fn search_case_insensitive<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
93 contents
94 .lines()
95 .filter(|line| line.to_lowercase().contains(&query.to_lowercase()))
96 .collect()
97}
98
99#[cfg(test)]
100mod tests {
101 use super::*;
102 // #[test]
103 // fn new_config() {
104 // let arg_array: Vec<String>;
105 // let res = Config::new(&arg_array).expect("Failed to init config.");
106 // }
107
108 #[test]
109 fn case_sensitive() {
110 let query = "duct";
111 let contents = "\
112Rust:
113safe, fast, productive.
114Pick three.
115Duct tape.";
116
117 assert_eq!(vec!["safe, fast, productive."], search(query, contents));
118 }
119
120 #[test]
121 fn case_insensitive() {
122 let query = "rUsT";
123 let contents = "\
124Rust:
125safe, fast, productive.
126Pick three.
127Trust me.";
128
129 assert_eq!(
130 vec!["Rust:", "Trust me."],
131 search_case_insensitive(query, contents)
132 );
133 }
134}