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
82pub 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}