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}