dont_use_this_grep/
lib.rs1use std::{env, error::Error, fs};
7
8#[derive(Debug, PartialEq)]
9pub struct Config {
10 pub query: String,
11 pub file_path: String,
12 pub ignore_case: bool,
13}
14
15impl Config {
16 pub fn build(mut args: impl Iterator<Item = String>) -> Result<Config, &'static str> {
34 args.next();
35
36 let query = match args.next() {
37 Some(arg) => arg,
38 None => return Err("No query string"),
39 };
40
41 let file_path = match args.next() {
42 Some(arg) => arg,
43 None => return Err("No query string"),
44 };
45
46 let ignore_case = env::var("IGNORE_CASE").is_ok();
47
48 Ok(Config { query, file_path, ignore_case })
49 }
50}
51
52pub fn run(config: Config) -> Result<(), Box<dyn Error>> {
53 let contents = fs::read_to_string(config.file_path)?;
54
55 let results = if config.ignore_case {
56 search_case_insensitive(&config.query, &contents)
57 } else {
58 search(&config.query, &contents)
59 };
60
61 for line in results {
62 println!("{line}")
63 }
64
65 Ok(())
66}
67
68pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
69 contents
70 .lines()
71 .filter(|line| line.contains(query))
72 .collect()
73}
74
75pub fn search_case_insensitive<'a>(
76 query: &str,
77 contents: &'a str
78) -> Vec<&'a str> {
79 let query = query.to_lowercase();
80 let mut results = Vec::new();
81
82 for line in contents.lines() {
83 if line.to_lowercase().contains(&query) {
84 results.push(line);
85 }
86 }
87
88 results
89}
90
91#[cfg(test)]
92mod tests {
93 use super::*;
94
95 #[test]
96 fn case_sensitive() {
97 let query = "duct";
98 let contents = "\
99Rust:
100safe, fast, productive.
101Pick three.
102Duct tape.";
103
104 assert_eq!(vec!["safe, fast, productive."], search(query, contents));
105 }
106
107 #[test]
108 fn case_insensitive() {
109 let query = "rUsT";
110 let contents = "\
111Rust:
112safe, fast, productive.
113Trust me.";
114
115 assert_eq!(
116 vec!["Rust:", "Trust me."],
117 search_case_insensitive(query, contents)
118 );
119 }
120}