colored_minigrep/
lib.rs

1use std::error::Error;
2use std::fs;
3use std::env;
4use colored::*;
5
6const ROOT_DIR: &str = ".";
7
8pub struct Config {
9    pub query: String,
10    pub filename: String,
11}
12
13#[derive(Debug)]
14pub struct ResultType<'a> {
15    pub pos: usize,
16    pub text: &'a str,
17}
18
19impl Config {
20    pub fn new(mut args: env::Args) -> Result<Config, &'static str> {
21        if args.len() < 3 {
22            return Err("Count of args is less than expected");
23        }
24
25        args.next();
26
27        let query = args.next().unwrap_or_else(|| {
28            panic!("First arg did't passed");
29        });
30
31        let filename = args.next().unwrap_or_else(|| {
32            panic!("Second arg did't passed")
33        });
34
35        Ok(Config { query, filename })
36    }
37}
38
39fn work_with_file(filename: &str, query: &str) {
40    let contents = fs::read_to_string(&filename).unwrap();
41
42    println!("{}", filename.green().bold());
43
44    for line in search(&query, &contents) {
45      let result_line = format!("{}: {}", line.pos, line.text);
46      println!("{}", result_line.cyan());
47    }
48}
49
50fn work_with_dir(dir: &str, query: &str) {
51    let entries: Vec<_> =  fs::read_dir(&dir).unwrap()
52        .map(|res| res.map(|entry| entry.path()))
53        .filter(|path| {
54            match path {
55                Ok(ok_path) => ok_path.is_file(),
56                _ => false
57            }
58        })
59        .collect();
60
61    for entry in entries {
62        match entry {
63            Ok(r) => work_with_file(r.to_str().unwrap(), query),
64            _ => println!("File not found")
65        }
66    }
67}
68
69pub fn run(config: Config) -> Result<(), Box<dyn Error>> {
70  let filename_or_dir = config.filename;
71  let query = config.query;
72
73  if filename_or_dir == ROOT_DIR {
74    work_with_dir(&filename_or_dir, &query);
75  } else {
76    work_with_file(&filename_or_dir, &query);
77  }
78
79  Ok(())
80}
81
82/// Find text line by query inside the contents
83///
84/// # Examples
85///
86/// ```
87/// let text = "lalal
88/// hello world
89/// hi
90/// hello!
91/// "
92///
93/// let answer = my_crate::search("hello", "text");
94///
95/// println!("{}", answer);
96/// // hello world
97/// // hello!
98/// ```
99pub fn search<'a>(query: &str, contents: &'a str) -> Vec<ResultType<'a>> {
100    contents
101        .lines()
102        .filter(|line| line.contains(query))
103        .enumerate()
104        .map(|(idx, text)| ResultType { pos: idx, text })
105        .collect()
106}