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::search::SearchError;
14use crate::search::SearchOutcome;
15use crate::search::candidates::walk;
16use crate::search::request::SearchExecution;
17use rayon::prelude::*;
18
19/// User-facing request to the grep pipeline.
20pub struct GrepRequest<'a> {
21    pub indexes: &'a Indexes,
22    pub filter: &'a CandidateFilter,
23    pub output: SearchOutput,
24    pub separators: &'a SearchSeparators,
25    pub collect_stats: bool,
26}
27
28/// Run the full grep pipeline: resolve candidates, execute search, return outcome.
29///
30/// # Errors
31///
32/// Returns an error if candidate resolution, regex compilation, or search execution fails.
33pub fn run(query: &SearchQuery, request: &GrepRequest<'_>) -> crate::Result<SearchOutcome> {
34    if query.opts.max_results == Some(0) {
35        return Err(crate::Error::Search(SearchError::InvalidMaxCount));
36    }
37
38    let spec = query.spec();
39    let output = request.output;
40
41    let raw = if request.indexes.is_empty() {
42        walk::collect_candidates(request.filter)?
43    } else {
44        let coverage = output.candidate_coverage();
45        request.indexes.candidates(&spec, coverage)
46    };
47
48    let candidates: Vec<Candidate> = raw
49        .into_par_iter()
50        .filter(|c| c.matches(request.filter))
51        .collect();
52
53    if candidates.is_empty() {
54        return Ok(SearchOutcome {
55            matched: false,
56            stats: request.collect_stats.then_some(SearchStats::default()),
57        });
58    }
59
60    query.search(&SearchExecution {
61        candidates,
62        output,
63        separators: request.separators,
64        collect_stats: request.collect_stats,
65    })
66}