massimans_minigrep/
lib.rs1use std::{env, error::Error, fs};
2pub struct Config {
3 pub query: String,
4 pub file_name: String,
5 pub ignore_case: bool,
6}
7#[derive(Debug)]
8pub struct SearchResult<'a> {
9 pub line_number: usize,
10 pub line: &'a str,
11}
12impl Config {
13 pub fn new() -> Result<Config, &'static str> {
14 let args: Vec<String> = env::args().collect();
15 if args.len() < 3 {
16 return Err("minigrep expects exactly 2 arguments");
17 }
18 let ignore_case =
19 env::var("IGNORE_CASE").is_ok() || args.contains(&String::from("--ignore-case"));
20 Ok(Config {
21 query: args[1].clone(),
22 file_name: args[2].clone(),
23 ignore_case,
24 })
25 }
26}
27pub fn run(config: Config) -> Result<(), Box<dyn Error>> {
28 let content = fs::read_to_string(&config.file_name)?;
29 let results = if config.ignore_case {
30 search_case_insensitive(&config.query, &content)
31 } else {
32 search(&config.query, &content)
33 };
34 for line in results {
35 println!("{}:{}", line.line_number, line.line);
36 }
37 Ok(())
38}
39pub fn search_case_insensitive<'a>(query: &str, contents: &'a str) -> Vec<SearchResult<'a>> {
40 let query = query.to_lowercase();
41 contents
42 .lines()
43 .enumerate()
44 .map(|(index, line)| SearchResult {
45 line_number: index + 1,
46 line,
47 })
48 .filter(|result| result.line.to_lowercase().contains(&query))
49 .collect()
50}
51pub fn search<'a>(query: &str, contents: &'a str) -> Vec<SearchResult<'a>> {
52 contents
53 .lines()
54 .enumerate()
55 .map(|(index, line)| SearchResult {
56 line_number: index + 1,
57 line,
58 })
59 .filter(|result| result.line.contains(query))
60 .collect()
61}
62#[cfg(test)]
63mod tests {
64 use super::*;
65
66 #[test]
67 fn one_result() {
68 let query = "duct";
69 let contents = "\
70Rust:
71safe, fast, productive.
72Pick three.";
73 let result = search(query, contents);
74 assert_eq!(result[0].line, "safe, fast, productive.");
75 assert_eq!(result[0].line_number, 2);
76 }
77}