1use std::path::{Path, PathBuf};
2
3use serde::{Deserialize, Serialize};
4
5use super::config::{CorpusKind, IndexConfig};
6use super::trigram;
7use super::{IndexDestination, IndexSource};
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
11pub enum PlanMode {
12 #[default]
14 IndexedCandidates,
15 FullScan,
17}
18
19#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
20pub struct QueryPlanOutput {
21 pub pattern: String,
22 pub mode: PlanMode,
23}
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
34#[serde(rename_all = "lowercase")]
35pub enum IndexKind {
36 Trigram,
38}
39
40impl IndexKind {
41 pub const ALL: &[Self] = &[Self::Trigram];
42
43 #[must_use]
44 pub const fn as_str(self) -> &'static str {
45 match self {
46 Self::Trigram => "trigram",
47 }
48 }
49
50 #[must_use]
52 pub(crate) const fn artifact_names(self) -> &'static [&'static str] {
53 match self {
54 Self::Trigram => &[
55 crate::FILES_BIN,
56 crate::LEXICON_BIN,
57 crate::POSTINGS_BIN,
58 crate::TRIGRAMS_BIN,
59 ],
60 }
61 }
62
63 pub(crate) fn build(
65 self,
66 config: &IndexConfig<'_>,
67 dest: IndexDestination,
68 paths: &[PathBuf],
69 ) -> crate::Result<()> {
70 match self {
71 Self::Trigram => {
72 let tables = trigram::builder::IndexTables::build(config, paths)?;
73 let root = config.corpus.root.canonicalize()?;
74 trigram::TrigramIndex::persist_tables(&tables, &root, config.corpus.kind, dest)?;
75 Ok(())
76 }
77 }
78 }
79
80 pub(crate) fn open(
82 self,
83 source: IndexSource,
84 root: &Path,
85 corpus_kind: CorpusKind,
86 ) -> crate::Result<Index> {
87 match self {
88 Self::Trigram => Ok(Index::Trigram(trigram::TrigramIndex::open_tables(
89 source,
90 root,
91 corpus_kind,
92 )?)),
93 }
94 }
95
96 pub(crate) fn update(
101 self,
102 current: IndexSource,
103 config: &IndexConfig<'_>,
104 dest: IndexDestination,
105 paths: &[PathBuf],
106 ) -> crate::Result<bool> {
107 let is_present = match ¤t {
108 IndexSource::Directory(dir) => dir.exists(),
109 IndexSource::Snapshot { reader, namespace } => {
110 reader.manifest().indexes.iter().any(|n| n == *namespace)
111 }
112 };
113 if !is_present {
114 self.build(config, dest, paths)?;
115 return Ok(true);
116 }
117
118 let root = config
119 .corpus
120 .root
121 .canonicalize()
122 .unwrap_or_else(|_| config.corpus.root.to_path_buf());
123 match self {
124 Self::Trigram => {
125 let existing =
126 trigram::TrigramIndex::open_tables(current, &root, config.corpus.kind)?;
127 let output = existing.rebuild(config, dest, paths)?;
128 Ok(output.is_some())
129 }
130 }
131 }
132}
133
134impl std::fmt::Display for IndexKind {
135 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
136 f.write_str(self.as_str())
137 }
138}
139
140impl std::str::FromStr for IndexKind {
141 type Err = String;
142
143 fn from_str(s: &str) -> Result<Self, Self::Err> {
144 match s {
145 "trigram" => Ok(Self::Trigram),
146 other => Err(format!("unknown index kind: {other}")),
147 }
148 }
149}
150
151pub enum Index {
157 Trigram(trigram::TrigramIndex),
159}
160
161impl Index {
162 #[must_use]
163 pub fn root(&self) -> &Path {
164 match self {
165 Self::Trigram(idx) => idx.root(),
166 }
167 }
168
169 #[must_use]
170 pub const fn corpus_kind(&self) -> CorpusKind {
171 match self {
172 Self::Trigram(idx) => idx.corpus_kind(),
173 }
174 }
175
176 #[must_use]
177 pub fn candidates(&self, query: &crate::query::QuerySpec<'_>) -> Option<Vec<crate::Candidate>> {
178 match self {
179 Self::Trigram(idx) => idx.candidates(query),
180 }
181 }
182
183 #[must_use]
184 pub(crate) fn all_files(&self) -> Vec<crate::Candidate> {
185 match self {
186 Self::Trigram(idx) => idx.all_files(),
187 }
188 }
189}
190
191#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
192pub struct FileId(usize);
193
194impl FileId {
195 #[must_use]
196 pub const fn new(value: usize) -> Self {
197 Self(value)
198 }
199
200 #[must_use]
201 pub const fn get(self) -> usize {
202 self.0
203 }
204}
205
206#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
207pub struct IndexId(usize);
208
209impl IndexId {
210 #[must_use]
211 pub const fn new(value: usize) -> Self {
212 Self(value)
213 }
214
215 #[must_use]
216 pub const fn get(self) -> usize {
217 self.0
218 }
219}