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)]
27#[serde(rename_all = "lowercase")]
28pub enum IndexKind {
29 Trigram,
30}
31
32impl IndexKind {
33 pub const ALL: &[Self] = &[Self::Trigram];
34
35 #[must_use]
36 pub const fn as_str(self) -> &'static str {
37 match self {
38 Self::Trigram => "trigram",
39 }
40 }
41
42 #[must_use]
44 pub(crate) const fn artifact_names(self) -> &'static [&'static str] {
45 match self {
46 Self::Trigram => &[
47 crate::FILES_BIN,
48 crate::LEXICON_BIN,
49 crate::POSTINGS_BIN,
50 crate::TRIGRAMS_BIN,
51 ],
52 }
53 }
54
55 pub(crate) fn build(
57 self,
58 config: &IndexConfig<'_>,
59 dest: IndexDestination,
60 paths: &[PathBuf],
61 ) -> crate::Result<()> {
62 match self {
63 Self::Trigram => {
64 let tables = trigram::builder::IndexTables::build(config, paths)?;
65 let root = config.corpus.root.canonicalize()?;
66 trigram::TrigramIndex::persist_tables(&tables, &root, config.corpus.kind, dest)?;
67 Ok(())
68 }
69 }
70 }
71
72 pub(crate) fn open(
74 self,
75 source: IndexSource,
76 root: &Path,
77 corpus_kind: CorpusKind,
78 ) -> crate::Result<Index> {
79 match self {
80 Self::Trigram => Ok(Index::Trigram(trigram::TrigramIndex::open_tables(
81 source,
82 root,
83 corpus_kind,
84 )?)),
85 }
86 }
87
88 pub(crate) fn update(
93 self,
94 current: IndexSource,
95 config: &IndexConfig<'_>,
96 dest: IndexDestination,
97 paths: &[PathBuf],
98 ) -> crate::Result<bool> {
99 let is_present = match ¤t {
100 IndexSource::Directory(dir) => dir.exists(),
101 IndexSource::Snapshot { reader, namespace } => {
102 reader.manifest().indexes.iter().any(|n| n == *namespace)
103 }
104 };
105 if !is_present {
106 self.build(config, dest, paths)?;
107 return Ok(true);
108 }
109
110 let root = config
111 .corpus
112 .root
113 .canonicalize()
114 .unwrap_or_else(|_| config.corpus.root.to_path_buf());
115 match self {
116 Self::Trigram => {
117 let existing =
118 trigram::TrigramIndex::open_tables(current, &root, config.corpus.kind)?;
119 let output = existing.rebuild(config, dest, paths)?;
120 Ok(output.is_some())
121 }
122 }
123 }
124}
125
126impl std::fmt::Display for IndexKind {
127 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
128 f.write_str(self.as_str())
129 }
130}
131
132impl std::str::FromStr for IndexKind {
133 type Err = String;
134
135 fn from_str(s: &str) -> Result<Self, Self::Err> {
136 match s {
137 "trigram" => Ok(Self::Trigram),
138 other => Err(format!("unknown index kind: {other}")),
139 }
140 }
141}
142
143pub enum Index {
145 Trigram(trigram::TrigramIndex),
146}
147
148impl Index {
149 #[must_use]
150 pub fn root(&self) -> &Path {
151 match self {
152 Self::Trigram(idx) => idx.root(),
153 }
154 }
155
156 #[must_use]
157 pub const fn corpus_kind(&self) -> CorpusKind {
158 match self {
159 Self::Trigram(idx) => idx.corpus_kind(),
160 }
161 }
162
163 #[must_use]
164 pub fn candidates(&self, query: &crate::query::QuerySpec<'_>) -> Option<Vec<crate::Candidate>> {
165 match self {
166 Self::Trigram(idx) => idx.candidates(query),
167 }
168 }
169
170 #[must_use]
171 pub(crate) fn all_files(&self) -> Vec<crate::Candidate> {
172 match self {
173 Self::Trigram(idx) => idx.all_files(),
174 }
175 }
176}
177
178#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
179pub struct FileId(usize);
180
181impl FileId {
182 #[must_use]
183 pub const fn new(value: usize) -> Self {
184 Self(value)
185 }
186
187 #[must_use]
188 pub const fn get(self) -> usize {
189 self.0
190 }
191}
192
193#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
194pub struct IndexId(usize);
195
196impl IndexId {
197 #[must_use]
198 pub const fn new(value: usize) -> Self {
199 Self(value)
200 }
201
202 #[must_use]
203 pub const fn get(self) -> usize {
204 self.0
205 }
206}