-
Notifications
You must be signed in to change notification settings - Fork 17
/
Copy pathsearcher.rs
96 lines (85 loc) · 2.88 KB
/
searcher.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
use super::{file_entry::FileEntry, sink::MatchesSink, SearchConfig};
use grep::{
matcher::LineTerminator,
regex::RegexMatcherBuilder,
searcher::{BinaryDetection, SearcherBuilder},
};
use ignore::WalkBuilder;
use std::{path::Path, sync::mpsc};
pub enum Event {
NewEntry(FileEntry),
SearchingFinished,
Error,
}
pub fn search(config: SearchConfig, tx: mpsc::Sender<Event>) {
std::thread::spawn(move || {
let path_searchers = config
.paths
.clone()
.into_iter()
.map(|path| {
let config = config.clone();
let tx = tx.clone();
std::thread::spawn(move || run(&path, config, tx))
})
.collect::<Vec<_>>();
for searcher in path_searchers {
if searcher.join().is_err() {
tx.send(Event::Error).ok();
return;
}
}
tx.send(Event::SearchingFinished).ok();
});
}
fn run(path: &Path, config: SearchConfig, tx: mpsc::Sender<Event>) {
let grep_searcher = SearcherBuilder::new()
.binary_detection(BinaryDetection::quit(b'\x00'))
.line_terminator(LineTerminator::byte(b'\n'))
.line_number(true)
.multi_line(false)
.build();
let matcher = RegexMatcherBuilder::new()
.line_terminator(Some(b'\n'))
.case_insensitive(config.case_insensitive)
.case_smart(config.case_smart)
.word(config.word_regexp)
.build(&config.pattern)
.expect("Cannot build RegexMatcher");
let mut builder = WalkBuilder::new(path);
let walk_parallel = builder
.overrides(config.overrides.clone())
.types(config.types.clone())
.hidden(!config.search_hidden)
.follow_links(config.follow_links)
.build_parallel();
walk_parallel.run(move || {
let tx = tx.clone();
let matcher = matcher.clone();
let mut grep_searcher = grep_searcher.clone();
Box::new(move |result| {
let dir_entry = match result {
Ok(entry) => {
if !entry.file_type().map_or(false, |ft| ft.is_file()) {
return ignore::WalkState::Continue;
}
entry
}
Err(_) => return ignore::WalkState::Continue,
};
let mut matches_in_entry = Vec::new();
let sr = MatchesSink::new(&matcher, &mut matches_in_entry);
grep_searcher
.search_path(&matcher, dir_entry.path(), sr)
.ok();
if !matches_in_entry.is_empty() {
tx.send(Event::NewEntry(FileEntry::new(
dir_entry.path().to_string_lossy().into_owned(),
matches_in_entry,
)))
.ok();
}
ignore::WalkState::Continue
})
});
}