Skip to main content

sift_core/grep/
mod.rs

1//! Grep pipeline orchestration.
2//!
3//! Bridges the logical query, index planner, and candidate filter.
4//! This is the primary API the CLI calls.
5
6use crate::Candidate;
7use crate::CandidateFilter;
8use crate::SearchOutput;
9use crate::SearchQuery;
10use crate::SearchSeparators;
11use crate::SearchStats;
12use crate::index::Indexes;
13use crate::query::QueryPlanner;
14use crate::search::SearchError;
15use crate::search::SearchOutcome;
16use crate::search::candidates::walk;
17use crate::search::request::SearchExecution;
18use rayon::prelude::*;
19
20/// User-facing request to the grep pipeline.
21pub struct GrepRequest<'a> {
22    pub indexes: &'a Indexes,
23    pub filter: &'a CandidateFilter,
24    pub output: SearchOutput,
25    pub separators: &'a SearchSeparators,
26    pub collect_stats: bool,
27}
28
29impl GrepRequest<'_> {
30    /// Run the full grep pipeline: resolve candidates, execute search, return outcome.
31    ///
32    /// # Errors
33    ///
34    /// Returns an error if candidate resolution, regex compilation, or search execution fails.
35    pub fn run(&self, query: &SearchQuery) -> crate::Result<SearchOutcome> {
36        if query.opts.max_results == Some(0) {
37            return Err(crate::Error::Search(SearchError::InvalidMaxCount));
38        }
39
40        let spec = query.spec();
41        let output = self.output;
42
43        let raw = QueryPlanner::new(spec).candidates(
44            self.indexes,
45            output.candidate_requirement(),
46            || walk::collect_candidates(self.filter),
47        )?;
48
49        let candidates: Vec<Candidate> = raw
50            .into_par_iter()
51            .filter(|c| c.matches(self.filter))
52            .collect();
53
54        if candidates.is_empty() {
55            return Ok(SearchOutcome {
56                matched: false,
57                stats: self.collect_stats.then_some(SearchStats::default()),
58            });
59        }
60
61        query.search(&SearchExecution {
62            candidates,
63            output,
64            separators: self.separators,
65            collect_stats: self.collect_stats,
66        })
67    }
68}