118 lines
4.7 KiB
Rust
118 lines
4.7 KiB
Rust
// SPDX-License-Identifier: GPL-3.0-only
|
|
|
|
use grep::matcher::{Match, Matcher};
|
|
use grep::regex::RegexMatcher;
|
|
use grep::searcher::{Searcher, sinks::UTF8};
|
|
use std::path::PathBuf;
|
|
|
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
pub struct LineSearchResult {
|
|
pub number: usize,
|
|
pub text: String,
|
|
pub first: Match,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
pub struct FileSearchResult {
|
|
pub path: PathBuf,
|
|
pub lines: Vec<LineSearchResult>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
pub struct ProjectSearchResult {
|
|
//TODO: should this be included?
|
|
pub value: String,
|
|
pub in_progress: bool,
|
|
pub files: Vec<FileSearchResult>,
|
|
}
|
|
|
|
impl ProjectSearchResult {
|
|
pub fn search_projects(&mut self, projects: Vec<(String, PathBuf)>) {
|
|
//TODO: support literal search
|
|
//TODO: use ignore::WalkParallel?
|
|
match RegexMatcher::new(&self.value) {
|
|
Ok(matcher) => {
|
|
let mut searcher = Searcher::new();
|
|
let mut walk_builder_opt: Option<ignore::WalkBuilder> = None;
|
|
for (_, project_path) in projects.iter() {
|
|
walk_builder_opt = match walk_builder_opt.take() {
|
|
Some(mut walk_builder) => {
|
|
walk_builder.add(project_path);
|
|
Some(walk_builder)
|
|
}
|
|
None => Some(ignore::WalkBuilder::new(project_path)),
|
|
};
|
|
}
|
|
|
|
if let Some(walk_builder) = walk_builder_opt {
|
|
for entry_res in walk_builder.build() {
|
|
let entry = match entry_res {
|
|
Ok(ok) => ok,
|
|
Err(err) => {
|
|
log::error!("failed to walk projects {:?}: {}", projects, err);
|
|
continue;
|
|
}
|
|
};
|
|
|
|
if let Some(file_type) = entry.file_type() {
|
|
if file_type.is_dir() {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
let entry_path = entry.path();
|
|
|
|
let mut lines = Vec::new();
|
|
match searcher.search_path(
|
|
&matcher,
|
|
entry_path,
|
|
UTF8(|number_u64, text| {
|
|
match usize::try_from(number_u64) {
|
|
Ok(number) => match matcher.find(text.as_bytes()) {
|
|
Ok(Some(first)) => {
|
|
lines.push(LineSearchResult {
|
|
number,
|
|
text: text.trim_end().to_string(),
|
|
first,
|
|
});
|
|
},
|
|
Ok(None) => {
|
|
log::error!("first match in file {:?} line {} not found", entry_path, number);
|
|
}
|
|
Err(err) => {
|
|
log::error!("failed to find first match in file {:?} line {}: {}", entry_path, number, err);
|
|
}
|
|
},
|
|
Err(err) => {
|
|
log::error!("failed to convert file {:?} line {} to usize: {}", entry_path, number_u64, err);
|
|
}
|
|
}
|
|
Ok(true)
|
|
}),
|
|
) {
|
|
Ok(()) => {
|
|
if !lines.is_empty() {
|
|
self.files.push(FileSearchResult {
|
|
path: entry_path.to_path_buf(),
|
|
lines,
|
|
});
|
|
}
|
|
}
|
|
Err(err) => {
|
|
log::error!("failed to search file {:?}: {}", entry_path, err);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Err(err) => {
|
|
log::error!(
|
|
"failed to create regex matcher with value {:?}: {}",
|
|
self.value,
|
|
err
|
|
);
|
|
}
|
|
}
|
|
self.in_progress = false;
|
|
}
|
|
}
|