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