sift-core 0.3.0

Indexed regex search over codebases (library + grep-like CLI)
Documentation
//! Grep pipeline orchestration.
//!
//! Bridges the logical query, index planner, and candidate filter.
//! This is the primary API the CLI calls.

use crate::Candidate;
use crate::CandidateFilter;
use crate::SearchOutput;
use crate::SearchQuery;
use crate::SearchSeparators;
use crate::SearchStats;
use crate::index::Indexes;
use crate::query::QueryPlanner;
use crate::search::SearchError;
use crate::search::SearchOutcome;
use crate::search::candidates::walk;
use crate::search::request::SearchExecution;
use rayon::prelude::*;

/// User-facing request to the grep pipeline.
pub struct GrepRequest<'a> {
    pub indexes: &'a Indexes,
    pub filter: &'a CandidateFilter,
    pub output: SearchOutput,
    pub separators: &'a SearchSeparators,
    pub collect_stats: bool,
}

impl GrepRequest<'_> {
    /// Run the full grep pipeline: resolve candidates, execute search, return outcome.
    ///
    /// # Errors
    ///
    /// Returns an error if candidate resolution, regex compilation, or search execution fails.
    pub fn run(&self, query: &SearchQuery) -> crate::Result<SearchOutcome> {
        if query.opts.max_results == Some(0) {
            return Err(crate::Error::Search(SearchError::InvalidMaxCount));
        }

        let spec = query.spec();
        let output = self.output;

        let raw = QueryPlanner::new(spec).candidates(
            self.indexes,
            output.candidate_requirement(),
            || walk::collect_candidates(self.filter),
        )?;

        let candidates: Vec<Candidate> = raw
            .into_par_iter()
            .filter(|c| c.matches(self.filter))
            .collect();

        if candidates.is_empty() {
            return Ok(SearchOutcome {
                matched: false,
                stats: self.collect_stats.then_some(SearchStats::default()),
            });
        }

        query.search(&SearchExecution {
            candidates,
            output,
            separators: self.separators,
            collect_stats: self.collect_stats,
        })
    }
}