eidetic-engine 0.15.2

Durable, local-first, explainable memory for coding agents.
use super::tag_bitmaps::{TagBitmapIndex, TagBitmapQuery};

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct SearchHotPathDiagnostics {
    pub tags: &'static str,
    pub negation_prefilter: &'static str,
    pub scoring: &'static str,
    pub candidate_count: usize,
}

impl SearchHotPathDiagnostics {
    #[must_use]
    pub const fn scalar(candidate_count: usize) -> Self {
        Self {
            tags: "bitmap",
            negation_prefilter: "none",
            scoring: "fixed_point_scalar",
            candidate_count,
        }
    }
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct SearchHotPathResult {
    pub candidate_ids: Vec<u64>,
    pub diagnostics: SearchHotPathDiagnostics,
}

#[must_use]
pub fn filter_candidates(
    tag_index: &TagBitmapIndex,
    query: &TagBitmapQuery,
) -> SearchHotPathResult {
    let candidate_ids = tag_index.matching(query);

    SearchHotPathResult {
        diagnostics: SearchHotPathDiagnostics::scalar(candidate_ids.len()),
        candidate_ids,
    }
}

#[cfg(test)]
mod tests {
    use super::filter_candidates;
    use crate::search::tag_bitmaps::{TagBitmapIndex, TagBitmapQuery};

    #[test]
    fn hot_path_filters_with_bitmap_and_reports_diagnostics() {
        let index = TagBitmapIndex::from_documents([
            (1, vec!["rust", "cli"]),
            (2, vec!["rust", "archived"]),
            (3, vec!["rust", "search"]),
        ]);
        let query = TagBitmapQuery::new(["rust"], ["archived"]);
        let result = filter_candidates(&index, &query);
        assert_eq!(result.candidate_ids, vec![1, 3]);
        assert_eq!(result.diagnostics.tags, "bitmap");
        assert_eq!(result.diagnostics.negation_prefilter, "none");
    }
}