1use std::path::PathBuf;
7
8use crate::Candidate;
9use crate::CandidateFilter;
10use crate::SearchOutput;
11use crate::SearchQuery;
12use crate::SearchSeparators;
13use crate::SearchStats;
14use crate::index::Indexes;
15use crate::query::QueryPlanner;
16use crate::search::SearchError;
17use crate::search::SearchOutcome;
18use crate::search::request::{SearchCollection, SearchExecution};
19use rayon::prelude::*;
20
21pub struct GrepRun {
23 pub outcome: SearchOutcome,
24 pub hits: Vec<PathBuf>,
26}
27
28pub struct GrepRequest<'a> {
30 pub indexes: &'a Indexes,
31 pub filter: &'a CandidateFilter,
32 pub output: SearchOutput,
33 pub separators: &'a SearchSeparators,
34 pub collect: SearchCollection,
35 pub store_meta: Option<&'a crate::StoreMeta>,
36 pub walk_unindexed: bool,
37}
38
39impl GrepRequest<'_> {
40 pub fn run(&self, query: &SearchQuery) -> crate::Result<GrepRun> {
46 if query.opts.max_results == Some(0) {
47 return Err(crate::Error::Search(SearchError::InvalidMaxCount));
48 }
49
50 let spec = query.build_query_spec();
51 let output = self.output;
52 let requirement = output.candidate_requirement();
53
54 let raw = QueryPlanner::new(spec).candidates(
55 self.indexes,
56 requirement,
57 self.filter,
58 self.store_meta,
59 self.walk_unindexed,
60 || self.filter.collect(),
61 )?;
62
63 let candidates: Vec<Candidate> = raw
64 .into_par_iter()
65 .filter(|c| c.matches(self.filter))
66 .collect();
67
68 if candidates.is_empty() {
69 return Ok(GrepRun {
70 outcome: SearchOutcome {
71 matched: false,
72 stats: self.collect.stats.then_some(SearchStats::default()),
73 },
74 hits: Vec::new(),
75 });
76 }
77
78 let (outcome, hits) = query.search(&SearchExecution {
79 candidates,
80 output,
81 separators: self.separators,
82 collect: self.collect.with_hits(true),
83 })?;
84
85 Ok(GrepRun { outcome, hits })
86 }
87}