sift_core/index/
registry.rs1use std::collections::HashSet;
2use std::path::{Path, PathBuf};
3
4use super::config::CorpusKind;
5use super::error::IndexError;
6use super::kinds::Index;
7use super::snapshot::Snapshot;
8use super::store;
9
10pub struct Indexes {
15 snapshot: Snapshot,
16}
17
18impl Indexes {
19 #[must_use]
23 pub fn from_single(index: Index, root: PathBuf) -> Self {
24 Self {
25 snapshot: Snapshot::from_indexes(root, vec![index]),
26 }
27 }
28
29 pub fn open(sift_dir: &Path) -> Result<Self, IndexError> {
38 let store = store::IndexStore::open(sift_dir).map_err(|e| match e {
39 crate::Error::Index(ie) => ie,
40 crate::Error::Io(io) => IndexError::Io {
41 path: sift_dir.to_path_buf(),
42 source: io,
43 },
44 _ => IndexError::Io {
45 path: sift_dir.to_path_buf(),
46 source: std::io::Error::other(e.to_string()),
47 },
48 })?;
49
50 let snapshot = store.open_current().map_err(|e| match e {
51 crate::Error::Index(ie) => ie,
52 crate::Error::Io(io) => IndexError::Io {
53 path: sift_dir.to_path_buf(),
54 source: io,
55 },
56 _ => IndexError::Io {
57 path: sift_dir.to_path_buf(),
58 source: std::io::Error::other(e.to_string()),
59 },
60 })?;
61
62 Ok(Self { snapshot })
63 }
64
65 #[must_use]
66 pub const fn is_empty(&self) -> bool {
67 self.snapshot.is_empty()
68 }
69
70 #[must_use]
71 pub fn root(&self) -> &Path {
72 self.snapshot.root()
73 }
74
75 #[must_use]
80 pub fn candidates(&self, query: &crate::query::QuerySpec<'_>) -> Option<Vec<crate::Candidate>> {
81 let indexes = self.snapshot.indexes();
82 match indexes.len() {
83 0 => None,
84 1 => indexes[0].candidates(query),
85 _ => Self::candidates_multi(indexes, query),
86 }
87 }
88
89 #[must_use]
91 pub fn indexed_rel_paths(&self) -> HashSet<PathBuf> {
92 let indexes = self.snapshot.indexes();
93 let Some(first) = indexes.first() else {
94 return HashSet::new();
95 };
96
97 let mut paths: HashSet<PathBuf> = first
98 .all_files()
99 .into_iter()
100 .map(|c| c.rel_path().to_path_buf())
101 .collect();
102
103 for index in indexes.iter().skip(1) {
104 let next: HashSet<PathBuf> = index
105 .all_files()
106 .into_iter()
107 .map(|c| c.rel_path().to_path_buf())
108 .collect();
109 paths.retain(|p| next.contains(p));
110 if paths.is_empty() {
111 break;
112 }
113 }
114
115 paths
116 }
117
118 #[must_use]
120 pub fn unindexed_hits(&self, hits: impl IntoIterator<Item = PathBuf>) -> Vec<PathBuf> {
121 let indexed = self.indexed_rel_paths();
122 hits.into_iter()
123 .filter(|path| !indexed.contains(path))
124 .collect()
125 }
126
127 #[must_use]
129 pub(crate) fn complete_candidates(&self) -> Vec<crate::Candidate> {
130 let indexes = self.snapshot.indexes();
131 let mut iter = indexes.iter();
132 let Some(first) = iter.next() else {
133 return Vec::new();
134 };
135
136 let mut files = first.all_files();
137
138 for index in iter {
139 let next: HashSet<PathBuf> = index
140 .all_files()
141 .into_iter()
142 .map(|c| c.rel_path().to_path_buf())
143 .collect();
144 files.retain(|c| next.contains(c.rel_path()));
145 if files.is_empty() {
146 break;
147 }
148 }
149
150 files
151 }
152
153 fn candidates_multi(
155 indexes: &[Index],
156 query: &crate::query::QuerySpec<'_>,
157 ) -> Option<Vec<crate::Candidate>> {
158 use rayon::prelude::*;
159
160 let sets: Vec<Vec<crate::Candidate>> = indexes
161 .par_iter()
162 .filter_map(|idx| idx.candidates(query))
163 .collect();
164
165 if sets.is_empty() {
166 return None;
167 }
168
169 let mut result = sets.into_iter();
170 let mut current = result.next()?;
171
172 for next in result {
173 let lookup: HashSet<&Path> = next.iter().map(crate::Candidate::rel_path).collect();
174 current.retain(|c| lookup.contains(c.rel_path()));
175 if current.is_empty() {
176 break;
177 }
178 }
179
180 Some(current)
181 }
182
183 #[must_use]
184 pub fn first(&self) -> Option<&Index> {
185 self.snapshot.indexes().first()
186 }
187
188 #[must_use]
189 pub fn corpus_kind(&self) -> Option<CorpusKind> {
190 let indexes = self.snapshot.indexes();
191 let kind = indexes.first()?.corpus_kind();
192 if indexes.iter().any(|idx| idx.corpus_kind() != kind) {
193 return None;
194 }
195 Some(kind)
196 }
197}