1use rusqlite::{params, Connection, NO_PARAMS};
2use serde::Serialize;
3use serde_json::json;
4use std::any::type_name;
5use std::collections::HashMap;
6use std::time::{SystemTime, UNIX_EPOCH};
7
8#[derive(Debug)]
9pub struct HotPot {
10 pub conn: Connection,
11 pub collections: HashMap<String, Collection>,
12}
13
14#[derive(Debug, Clone)]
15pub struct Collection {
16 pub name: String,
17}
18
19#[derive(Debug)]
20pub struct NewEntry {
21 time_created: i64,
22 data: String,
23}
24
25#[derive(Debug, Clone)]
26pub struct Entry {
27 pub id: i64,
28 pub time_created: i64,
29 pub data: String,
30}
31
32#[derive(Debug)]
33pub enum Error {
34 General,
35}
36
37#[derive(Debug, Clone)]
38pub enum QueryKind {
39 Contains,
40 Object,
41}
42
43#[derive(Debug, Clone)]
44pub struct Query {
45 pub query_type: QueryKind,
46 pub collection: String,
47 pub comparison: String,
48 pub key: Option<String>,
49 pub string_value: Option<String>,
50 pub bool_value: Option<bool>,
51 pub float_value: Option<f32>,
52 pub int_value: Option<i32>,
53}
54
55#[derive(Debug)]
56pub struct QueryBuilder {
57 pub query_type: Option<QueryKind>,
58 pub collection: Option<String>,
59 pub comparison: Option<String>,
60 pub key: Option<String>,
61 pub string_value: Option<String>,
62 pub bool_value: Option<bool>,
63 pub float_value: Option<f32>,
64 pub int_value: Option<i32>,
65}
66
67#[derive(Debug)]
68pub struct Index {
69 pub collection: String,
70 pub key: String,
71 pub value: String,
72 }
83
84#[derive(Debug)]
85pub struct IndexBuilder {
86 pub collection: Option<String>,
87 pub key: Option<String>,
88 pub value: Option<String>,
89}
90
91fn get_ms_time() -> i64 {
92 let start = SystemTime::now();
93 let since_the_epoch = start
94 .duration_since(UNIX_EPOCH)
95 .expect("Time went backwards");
96 since_the_epoch.as_millis() as i64
97}
98
99fn type_of<T>(_: T) -> &'static str {
100 type_name::<T>()
101}
102
103impl QueryBuilder {
104 pub fn new() -> QueryBuilder {
105 QueryBuilder {
106 query_type: None,
107 collection: None,
108 comparison: None,
109 key: None,
110 string_value: None,
111 bool_value: None,
112 float_value: None,
113 int_value: None,
114 }
115 }
116
117 pub fn kind(mut self, typ: QueryKind) -> QueryBuilder {
118 self.query_type = Some(typ);
119 self
120 }
121
122 pub fn collection(mut self, collection: &str) -> QueryBuilder {
123 self.collection = Some(String::from(collection));
124 self
125 }
126
127 pub fn comparison(mut self, comparison: &str) -> QueryBuilder {
128 self.comparison = Some(String::from(comparison));
129 self
130 }
131
132 pub fn string(mut self, value: &str) -> QueryBuilder {
133 self.string_value = Some(String::from(value));
134 self
135 }
136
137 pub fn bool(mut self, value: bool) -> QueryBuilder {
138 self.bool_value = Some(value.clone());
139 self
140 }
141
142 pub fn float(mut self, value: f32) -> QueryBuilder {
143 self.float_value = Some(value.clone());
144 self
145 }
146
147 pub fn int(mut self, value: i32) -> QueryBuilder {
148 self.int_value = Some(value.clone());
149 self
150 }
151
152 pub fn key(mut self, key: &str) -> QueryBuilder {
153 self.key = Some(String::from(key));
154 self
155 }
156
157 pub fn finish(self) -> Query {
158 Query {
159 query_type: self.query_type.unwrap(),
160 collection: self.collection.unwrap(),
161 comparison: self.comparison.unwrap(),
162 key: self.key,
163 string_value: self.string_value,
164 bool_value: self.bool_value,
165 float_value: self.float_value,
166 int_value: self.int_value,
167 }
168 }
169}
170
171impl HotPot {
172 pub fn new() -> HotPot {
173 let mut hp = HotPot {
174 conn: Connection::open("database.hpdb").unwrap(),
175 collections: HashMap::new(),
176 };
177 let collections = hp.list_collections();
178 match collections {
179 Ok(collection_names) => {
180 for name in collection_names {
181 hp.collections.insert(
182 String::from(name.clone()),
183 Collection {
184 name: String::from(name),
185 },
186 );
187 }
188 }
189 Err(_) => (),
190 }
191 hp
192 }
193
194 pub fn list_collections(&mut self) -> rusqlite::Result<Vec<String>> {
195 let mut stmt = self.conn.prepare(
196 "SELECT name FROM sqlite_master WHERE type ='table' AND name NOT LIKE 'sqlite_%'",
197 )?;
198 let rows = stmt.query_map(NO_PARAMS, |row| Ok(row.get(0)?))?;
199 let mut names = Vec::new();
200 for name_result in rows {
201 names.push(name_result?);
202 }
203 Ok(names)
204 }
205
206 pub fn create_collection(&mut self, name: &str) -> Result<bool, Error> {
207 &self
208 .conn
209 .execute(
210 &format!(
211 "
212 CREATE TABLE {} (
213 id INTEGER PRIMARY KEY,
214 time_created INTEGER NOT NULL,
215 data BLOB
216 )",
217 name
218 ),
219 params![],
220 )
221 .map_err(|_| Error::General);
222 &self.collections.insert(
223 String::from(name),
224 Collection {
225 name: String::from(name),
226 },
227 );
228 Ok(true)
229 }
230
231 pub fn execute(&self, query: Query) -> Result<Vec<Entry>, Error> {
232 let mut results = Vec::new();
234 let c = &self
235 .collections
236 .get(&query.collection)
237 .expect("collection does not exist");
238 match query.query_type {
239 QueryKind::Contains => {
240 if !query.string_value.is_none() {
241 results = c
242 .query_arrays_contains::<String>(
243 &self.conn,
244 &query.collection,
245 &query.comparison,
246 &query.string_value.unwrap(),
247 )
248 .unwrap_or(Vec::new());
249 }
250 if !query.int_value.is_none() {
251 results = c
252 .query_arrays_contains::<i32>(
253 &self.conn,
254 &query.collection,
255 &query.comparison,
256 &query.int_value.unwrap(),
257 )
258 .unwrap_or(Vec::new());
259 }
260 if !query.bool_value.is_none() {
261 results = c
262 .query_arrays_contains::<bool>(
263 &self.conn,
264 &query.collection,
265 &query.comparison,
266 &query.bool_value.unwrap(),
267 )
268 .unwrap_or(Vec::new());
269 }
270 if !query.float_value.is_none() {
271 results = c
272 .query_arrays_contains::<f32>(
273 &self.conn,
274 &query.collection,
275 &query.comparison,
276 &query.float_value.unwrap(),
277 )
278 .unwrap_or(Vec::new());
279 }
280 }
281 QueryKind::Object => {
282 if !query.string_value.is_none() {
283 results = c
284 .query_object_with_key_value::<String>(
285 &self.conn,
286 &query.collection,
287 &query.comparison,
288 &query.key.clone().unwrap(),
289 &query.string_value.unwrap(),
290 )
291 .unwrap_or(Vec::new());
292 }
293 if !query.int_value.is_none() {
294 results = c
295 .query_object_with_key_value::<i32>(
296 &self.conn,
297 &query.collection,
298 &query.comparison,
299 &query.key.clone().unwrap(),
300 &query.int_value.unwrap(),
301 )
302 .unwrap_or(Vec::new());
303 }
304 if !query.bool_value.is_none() {
305 results = c
306 .query_object_with_key_value::<bool>(
307 &self.conn,
308 &query.collection,
309 &query.comparison,
310 &query.key.clone().unwrap(),
311 &query.bool_value.unwrap(),
312 )
313 .unwrap_or(Vec::new());
314 }
315 if !query.float_value.is_none() {
316 results = c
318 .query_object_with_key_value::<f32>(
319 &self.conn,
320 &query.collection,
321 &query.comparison,
322 &query.key.clone().unwrap(),
323 &query.float_value.unwrap(),
324 )
325 .unwrap_or(Vec::new());
326 }
327 }
328 }
329 Ok(results)
330 }
331
332 pub fn add_object_to_collection(&mut self, cname: &str, val: String) -> Result<bool, Error> {
333 let c = &self.collections.get(cname).unwrap();
334 let _did_insert = c.add_object(&self.conn, cname, val);
335 Ok(true)
336 }
337 pub fn add_index_to_collection(
338 &mut self,
339 collection_name: &str,
340 key: &str,
341 index_name: &str,
342 ) -> Result<bool, Error> {
343 let c = &self.collections.get(collection_name).unwrap();
344 let _did_insert = c.add_index(&self.conn, collection_name, key, index_name);
345 Ok(true)
346 }
347
348 pub fn insert<T: Serialize>(&mut self, cname: &str, svalue: &T) -> Result<bool, Error> {
349 let val: String = json!(svalue).to_string();
351
352 let c = &self.collections.get(cname).unwrap();
353 let _did_insert = c.add_object(&self.conn, cname, val);
354 Ok(true)
355 }
356}
357
358impl Collection {
359 pub fn add_object(
360 &self,
361 conn: &Connection,
362 cname: &str,
363 value: String,
364 ) -> rusqlite::Result<()> {
365 let me = NewEntry {
366 time_created: get_ms_time(),
367 data: value,
368 };
369 conn.execute(
370 &format!(
371 "INSERT INTO {} (time_created, data)
372 VALUES (?1, ?2)",
373 cname
374 ),
375 params![me.time_created, me.data.to_string()],
376 )?;
377 Ok(())
378 }
379
380 pub fn add_index(
381 &self,
382 conn: &Connection,
383 collection_name: &str,
384 key: &str,
385 index_name: &str,
386 ) -> rusqlite::Result<()> {
387 let mut stmt = conn.prepare(&format!(
388 "CREATE INDEX {} ON {} (json_extract(data, '$.{}'))",
389 index_name, collection_name, key
390 ))?;
391 let res = stmt.execute(params![])?;
392 Ok(())
393 }
394 pub fn query_arrays_contains<T: std::fmt::Display>(
395 &self,
396 conn: &Connection,
397 cname: &str,
398 comparison: &str,
399 value: &T,
400 ) -> rusqlite::Result<Vec<Entry>> {
401 match type_of(value) {
402 "&alloc::string::String" => {
403 let mut stmt = conn.prepare(&format!(
404 "SELECT * from {}, json_each(data) WHERE json_each.value {} '{}'",
405 cname, comparison, value
406 ))?;
407
408 let person_iter = stmt.query_map(params![], |row| {
409 Ok(Entry {
410 id: row.get(0).unwrap(),
411 time_created: row.get(1).unwrap(),
412 data: row.get(2).unwrap(),
413 })
414 })?;
415 let results: Vec<Entry> = person_iter.map(|data| data.unwrap()).collect();
416 Ok(results)
417 }
418 _ => {
419 let mut stmt = conn.prepare(&format!(
420 "SELECT * from {}, json_each(data) WHERE json_each.value {} {}",
421 cname, comparison, value
422 ))?;
423
424 let person_iter = stmt.query_map(params![], |row| {
425 Ok(Entry {
426 id: row.get(0).unwrap(),
427 time_created: row.get(1).unwrap(),
428 data: row.get(2).unwrap(),
429 })
430 })?;
431 let results: Vec<Entry> = person_iter.map(|data| data.unwrap()).collect();
432 Ok(results)
433 }
434 }
435 }
436
437 pub fn query_object_with_key_value<T: std::fmt::Display>(
438 &self,
439 conn: &Connection,
440 cname: &str,
441 comparison: &str,
442 key: &str,
443 value: &T,
444 ) -> rusqlite::Result<Vec<Entry>> {
445 match type_of(value) {
446 "&alloc::string::String" => {
447 let query = format!(
448 "SELECT * FROM {}, json_tree(data, '$.{}') WHERE json_tree.value {} '{}'",
449 cname, key, comparison, value
450 );
451 let mut stmt = conn.prepare(&query)?;
453 let person_iter = stmt.query_map(params![], |row| {
454 Ok(Entry {
455 id: row.get(0).unwrap(),
456 time_created: row.get(1).unwrap(),
457 data: row.get(2).unwrap(),
458 })
459 })?;
460 let results: Vec<Entry> = person_iter.map(|data| data.unwrap()).collect();
461 Ok(results)
462 }
463 _ => {
464 let query = format!(
465 "SELECT * FROM {}, json_tree(data, '$.{}') WHERE json_tree.value {} {}",
466 cname, key, comparison, value
467 );
468 let mut stmt = conn.prepare(&query)?;
470 let person_iter = stmt.query_map(params![], |row| {
471 Ok(Entry {
472 id: row.get(0).unwrap(),
473 time_created: row.get(1).unwrap(),
474 data: row.get(2).unwrap(),
475 })
476 })?;
477 let results: Vec<Entry> = person_iter.map(|data| data.unwrap()).collect();
478 Ok(results)
479 }
480 }
481 }
482}