ripvec_core/searchable.rs
1//! Engine-agnostic searchable-index trait.
2//!
3//! Without it, every engine swap requires touching every LSP module.
4
5use std::any::Any;
6
7use crate::chunk::CodeChunk;
8use crate::hybrid::SearchMode;
9
10/// Engine-agnostic searchable index.
11///
12/// transformer engines,
13/// [`RipvecIndex`](crate::encoder::ripvec::index::RipvecIndex) for
14/// the ripvec engine.
15pub trait SearchableIndex: Send + Sync {
16 /// Borrow the indexed chunks.
17 fn chunks(&self) -> &[CodeChunk];
18
19 /// Search by text query.
20 ///
21 /// Returns `(chunk_idx, score)` pairs ranked descending. Score is
22 /// normalized to `[0, 1]` regardless of mode so callers can apply
23 /// a single threshold consistently.
24 fn search(&self, query_text: &str, top_k: usize, mode: SearchMode) -> Vec<(usize, f32)>;
25
26 /// Search by similarity to an existing chunk's embedding.
27 ///
28 /// Caller passes the chunk index whose embedding should be used
29 /// as the query vector. The canonical `goto_definition` pattern:
30 /// the LSP layer identifies the chunk at the cursor, then asks
31 /// the index for structurally similar chunks elsewhere.
32 ///
33 /// If `chunk_idx` is out of range or the engine cannot provide
34 /// an embedding for it (keyword-only mode, embedding row not
35 /// stored), implementations fall back to text-only search via
36 /// [`Self::search`].
37 fn search_from_chunk(
38 &self,
39 chunk_idx: usize,
40 query_text: &str,
41 top_k: usize,
42 mode: SearchMode,
43 ) -> Vec<(usize, f32)>;
44
45 /// Downcast escape hatch for callers that legitimately need the
46 /// concrete engine-specific index type.
47 ///
48 /// Used by transformer-engine-only code paths (e.g., legacy
49 /// `run_search` with caller-supplied query embedding) that need
50 /// use [`Any::downcast_ref`] to attempt the conversion. Returns
51 /// `None`-equivalent when the concrete type doesn't match.
52 ///
53 /// Engine-neutral code should never need this; the three
54 /// trait methods above are the supported surface.
55 fn as_any(&self) -> &dyn Any;
56}