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