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