1#![doc = include_str!("../README.md")]
2
3use std::{collections::BTreeMap, sync::OnceLock};
4
5use brk_computer::Computer;
6use brk_error::{Error, Result};
7use brk_indexer::Indexer;
8use brk_structs::Height;
9use nucleo_matcher::{
10 Config, Matcher,
11 pattern::{AtomKind, CaseMatching, Normalization, Pattern},
12};
13use quick_cache::sync::Cache;
14use tabled::settings::Style;
15use vecdb::{AnyCollectableVec, AnyStoredVec};
16
17mod deser;
18mod format;
19mod ids;
20mod index;
21mod output;
22mod pagination;
23mod params;
24mod table;
25mod vecs;
26
27pub use format::Format;
28pub use index::Index;
29pub use output::{Output, Value};
30pub use pagination::{PaginatedIndexParam, PaginationParam};
31pub use params::{IdParam, Params, ParamsOpt};
32pub use table::Tabled;
33use vecs::Vecs;
34
35use crate::vecs::{IdToVec, IndexToVec};
36
37pub fn cached_errors() -> &'static Cache<String, String> {
38 static CACHE: OnceLock<Cache<String, String>> = OnceLock::new();
39 CACHE.get_or_init(|| Cache::new(1000))
40}
41
42#[allow(dead_code)]
43pub struct Interface<'a> {
44 vecs: Vecs<'a>,
45 indexer: &'a Indexer,
46 computer: &'a Computer,
47}
48
49impl<'a> Interface<'a> {
50 pub fn build(indexer: &Indexer, computer: &Computer) -> Self {
51 let indexer = indexer.static_clone();
52 let computer = computer.static_clone();
53 let vecs = Vecs::build(indexer, computer);
54
55 Self {
56 vecs,
57 indexer,
58 computer,
59 }
60 }
61
62 pub fn get_height(&self) -> Height {
63 Height::from(self.indexer.vecs.height_to_blockhash.stamp())
64 }
65
66 pub fn search(&self, params: &Params) -> Result<Vec<(String, &&dyn AnyCollectableVec)>> {
67 let ids = ¶ms.ids;
68 let index = params.index;
69
70 let ids_to_vec = self
71 .vecs
72 .index_to_id_to_vec
73 .get(&index)
74 .ok_or(Error::String(format!(
75 "Index \"{}\" isn't a valid index",
76 index
77 )))?;
78
79 ids.iter()
80 .map(|id| {
81 let vec = ids_to_vec.get(id.as_str()).ok_or_else(|| {
82 let cached_errors = cached_errors();
83
84 if let Some(message) = cached_errors.get(id) {
85 return Error::String(message)
86 }
87
88 let mut message = format!(
89 "No vec named \"{}\" indexed by \"{}\" found.\n",
90 id,
91 index
92 );
93
94 let mut matcher = Matcher::new(Config::DEFAULT);
95
96 let matches = Pattern::new(
97 id.as_str(),
98 CaseMatching::Ignore,
99 Normalization::Smart,
100 AtomKind::Fuzzy,
101 )
102 .match_list(ids_to_vec.keys(), &mut matcher)
103 .into_iter()
104 .take(10)
105 .map(|(s, _)| s)
106 .collect::<Vec<_>>();
107
108 if !matches.is_empty() {
109 message +=
110 &format!("\nMaybe you meant one of the following: {matches:#?} ?\n");
111 }
112
113 if let Some(index_to_vec) = self.id_to_index_to_vec().get(id.as_str()) {
114 message += &format!("\nBut there is a vec named {id} which supports the following indexes: {:#?}\n", index_to_vec.keys());
115 }
116
117 cached_errors.insert(id.clone(), message.clone());
118
119 Error::String(message)
120 });
121 vec.map(|vec| (id.clone(), vec))
122 })
123 .collect::<Result<Vec<_>>>()
124 }
125
126 pub fn format(
127 &self,
128 vecs: Vec<(String, &&dyn AnyCollectableVec)>,
129 params: &ParamsOpt,
130 ) -> Result<Output> {
131 let from = params.from().map(|from| {
132 vecs.iter()
133 .map(|(_, v)| v.i64_to_usize(from))
134 .min()
135 .unwrap_or_default()
136 });
137
138 let to = params.to().map(|to| {
139 vecs.iter()
140 .map(|(_, v)| v.i64_to_usize(to))
141 .min()
142 .unwrap_or_default()
143 });
144
145 let mut values = vecs
146 .iter()
147 .map(|(_, vec)| -> Result<Vec<serde_json::Value>> {
148 Ok(vec.collect_range_serde_json(from, to)?)
149 })
150 .collect::<Result<Vec<_>>>()?;
151
152 let format = params.format();
153
154 if values.is_empty() {
155 return Ok(Output::default(format));
156 }
157
158 let ids_last_i = vecs.len() - 1;
159
160 Ok(match format {
161 Some(Format::CSV) | Some(Format::TSV) => {
162 let delimiter = if format == Some(Format::CSV) {
163 ','
164 } else {
165 '\t'
166 };
167
168 let mut text = vecs
169 .iter()
170 .map(|(id, _)| id.to_owned())
171 .collect::<Vec<_>>()
172 .join(&delimiter.to_string());
173
174 text.push('\n');
175
176 let values_len = values.first().unwrap().len();
177
178 (0..values_len).for_each(|i| {
179 let mut line = "".to_string();
180 values.iter().enumerate().for_each(|(id_i, v)| {
181 line += &v.get(i).unwrap().to_string();
182 if id_i == ids_last_i {
183 line.push('\n');
184 } else {
185 line.push(delimiter);
186 }
187 });
188 text += &line;
189 });
190
191 if format == Some(Format::CSV) {
192 Output::CSV(text)
193 } else {
194 Output::TSV(text)
195 }
196 }
197 Some(Format::MD) => {
198 let mut table =
199 values.to_table(vecs.iter().map(|(s, _)| s.to_owned()).collect::<Vec<_>>());
200
201 table.with(Style::markdown());
202
203 Output::MD(table.to_string())
204 }
205 Some(Format::JSON) | None => {
206 if values.len() == 1 {
207 let mut values = values.pop().unwrap();
208 if values.len() == 1 {
209 let value = values.pop().unwrap();
210 Output::Json(Value::Single(value))
211 } else {
212 Output::Json(Value::List(values))
213 }
214 } else {
215 Output::Json(Value::Matrix(values))
216 }
217 }
218 })
219 }
220
221 pub fn search_and_format(&self, params: Params) -> Result<Output> {
222 self.format(self.search(¶ms)?, ¶ms.rest)
223 }
224
225 pub fn id_to_index_to_vec(&self) -> &BTreeMap<&str, IndexToVec<'_>> {
226 &self.vecs.id_to_index_to_vec
227 }
228
229 pub fn index_to_id_to_vec(&self) -> &BTreeMap<Index, IdToVec<'_>> {
230 &self.vecs.index_to_id_to_vec
231 }
232
233 pub fn get_vecid_count(&self) -> usize {
234 self.vecs.id_count
235 }
236
237 pub fn get_index_count(&self) -> usize {
238 self.vecs.index_count
239 }
240
241 pub fn get_vec_count(&self) -> usize {
242 self.vecs.vec_count
243 }
244
245 pub fn get_indexes(&self) -> &[&'static str] {
246 &self.vecs.indexes
247 }
248
249 pub fn get_accepted_indexes(&self) -> &BTreeMap<&'static str, &'static [&'static str]> {
250 &self.vecs.accepted_indexes
251 }
252
253 pub fn get_vecids(&self, pagination: PaginationParam) -> &[&str] {
254 self.vecs.ids(pagination)
255 }
256
257 pub fn get_index_to_vecids(&self, paginated_index: PaginatedIndexParam) -> Vec<&str> {
258 self.vecs.index_to_ids(paginated_index)
259 }
260
261 pub fn get_vecid_to_indexes(&self, id: String) -> Option<&Vec<&'static str>> {
262 self.vecs.id_to_indexes(id)
263 }
264}