persistent_keystore_rs/lib.rs
1use std::path::{Path, PathBuf};
2use std::fs::File;
3use std::io::SeekFrom;
4use std::time::{SystemTime, Duration};
5use std::collections::HashMap;
6use std::io::prelude::*;
7use std::sync::{Arc, Mutex};
8use std::thread::sleep;
9use std::fs::OpenOptions;
10use lz4_flex::{compress_prepend_size, decompress_size_prepended};
11use tracing::{debug, error, info, trace};
12
13mod structs;
14pub mod errors;
15pub mod prelude;
16pub use structs::*;
17#[cfg(feature = "mocks")]
18pub use prelude::MockDatabaseClient;
19use errors::*;
20use prelude::*;
21use std::thread::JoinHandle;
22
23struct Saver {
24 handle: Option<JoinHandle<()>>,
25 killer: std::sync::mpsc::SyncSender<()>,
26}
27
28impl Drop for Saver {
29 fn drop(&mut self) {
30 self.killer.send(()).unwrap();
31 if let Some(h) = self.handle.take() {
32 h.join().unwrap();
33 }
34 }
35}
36
37/// Thread-safe, optionally persistent client for interacting with a keystore database
38#[derive(Clone)]
39pub struct Client {
40 database: Arc<Mutex<Database>>,
41 raw_file: Arc<Mutex<PathBuf>>,
42 handle: Arc<Option<Saver>>,
43}
44
45fn open_file<P: AsRef<Path> + Clone + std::fmt::Debug>(path: P) -> Result<File, std::io::Error> {
46 debug!("Opening file {:?}", path);
47 OpenOptions::new()
48 .write(true)
49 .read(true)
50 .create(false)
51 .truncate(false)
52 .append(false)
53 .open(path)
54}
55
56impl Client {
57 /// Creates a database at the supplied path
58 /// ```
59 /// # use persistent_keystore_rs::Client;
60 /// use std::path::Path;
61 /// let c = Client::new(Path::new("temp.db"), None);
62 /// # std::fs::remove_file("temp.db").unwrap();
63 /// ```
64 /// This database will not sync on its own and will need to be saved with
65 /// ```
66 /// # use persistent_keystore_rs::Client;
67 /// # let mut c = Client::new(std::path::Path::new("temp2.db"), None).unwrap();
68 /// c.save();
69 /// # std::fs::remove_file("temp2.db").unwrap();
70 /// ```
71 ///
72 /// To create a persistent database that will create a thread attached to the lifetime
73 /// of the client provide a duration
74 /// ```
75 /// # use persistent_keystore_rs::Client;
76 /// use std::path::Path;
77 /// use std::time::Duration;
78 /// let c = Client::new(Path::new("temp3.db"), Some(Duration::from_millis(30)));
79 /// # drop(c);
80 /// # std::fs::remove_file("temp3.db").unwrap();
81 /// ```
82 /// This thread will prune (remove stale entries) and save the database
83 /// every duration
84 pub fn new<P: AsRef<Path> + Clone + std::fmt::Debug>(path: P, sync_interval: Option<Duration>) -> Result<Box<dyn DatabaseClient>, DatabaseError> {
85 info!("Creating Client with database at {:?}", path);
86 if path.as_ref().exists() {
87 error!("Database exists, cannot create: {:?}", path);
88 return Err(DatabaseError::DatabaseExistsError)
89 };
90
91 let mut database = Database::default();
92
93 if let Some(d) = sync_interval {
94 debug!("Setting sync interval to {:?}", d);
95 database.set_sync_duration(d);
96 };
97
98 let mut client = Self{
99 database: Arc::new(Mutex::new(database)),
100 raw_file: Arc::new(Mutex::new(PathBuf::from(path.as_ref()))),
101 handle: Arc::new(None),
102 };
103
104 if let Some(d) = sync_interval {
105 let mut c = client.clone();
106 let (tx, rx) = std::sync::mpsc::sync_channel(0);
107 let h = std::thread::spawn( move || loop {
108 if let Ok(_) = rx.try_recv() {
109 trace!("Breaking");
110 break
111 };
112
113 trace!("Sleeping for {:?}", d);
114 sleep(d);
115
116 trace!("Pruning database");
117 c.prune().unwrap();
118 debug!("Database pruned");
119
120 trace!("Saving database");
121 c.save().unwrap();
122 debug!("Database saved");
123 }
124 );
125 client.handle = Arc::new(Some(Saver{
126 handle: Some(h),
127 killer: tx,
128 }));
129 };
130
131 client.save()?;
132 trace!("Returning Client");
133 Ok(Box::new(client))
134 }
135
136 /// Opens an existing database at the supplied path
137 /// ```
138 /// # use persistent_keystore_rs::Client;
139 /// use std::path::Path;
140 /// # let c = Client::new(Path::new("existing.db"), None);
141 /// # drop(c);
142 /// let c = Client::open(Path::new("existing.db"));
143 /// # std::fs::remove_file("existing.db").unwrap();
144 /// ```
145 /// This database will resume the sync settings that were provided when
146 /// the database was created.
147 pub fn open<P: AsRef<Path> + Clone + std::fmt::Debug>(path: P) -> Result<Box<dyn DatabaseClient>, DatabaseError> {
148 info!("Opening Client with database at {:?}", path);
149 if !path.as_ref().exists() {
150 error!("Database does not exist exists, cannot open: {:?}", path);
151 return Err(DatabaseError::DatabaseDoesNotExist(path.as_ref().to_str().unwrap().to_string()))
152 } ;
153
154 let mut f = open_file(&path)?;
155 let mut compressed: Vec<u8> = Vec::new();
156 f.read_to_end(&mut compressed)?;
157 let uncompressed = decompress_size_prepended(&compressed)?;
158 let database: Database = bincode::deserialize(&uncompressed)?;
159 let sync_interval = database.sync_interval.clone();
160
161 let mut client = Self{
162 database: Arc::new(Mutex::new(database)),
163 raw_file: Arc::new(Mutex::new(PathBuf::from(path.as_ref()))),
164 handle: Arc::new(None),
165 };
166
167 if let Some(duration) = sync_interval {
168 let mut c = client.clone();
169 let (tx, rx) = std::sync::mpsc::sync_channel(0);
170 let h = std::thread::spawn( move || loop {
171 if let Ok(_) = rx.try_recv() {
172 trace!("Breaking");
173 break
174 };
175
176 trace!("Sleeping for {:?}", duration);
177 sleep(duration);
178
179 trace!("Pruning database");
180 c.prune().unwrap();
181 debug!("Database pruned");
182
183 trace!("Saving database");
184 c.save().unwrap();
185 debug!("Database saved");
186 }
187 );
188
189 client.handle = Arc::new(Some(Saver{
190 handle: Some(h),
191 killer: tx,
192 }));
193 };
194
195
196 trace!("Returning Client");
197
198 Ok(Box::new(client))
199 }
200}
201
202impl DatabaseClient for Client {
203 /// Removes stale entries as defined by the expiration value per table
204 /// and saves the database to disk; using lz4 compression
205 /// ```
206 /// # use persistent_keystore_rs::Client;
207 /// use std::path::Path;
208 /// let c = Client::new(Path::new("saved.db"), None);
209 /// # std::fs::remove_file("saved.db").unwrap();
210 /// ```
211 /// This database will not sync on its own and will need to be saved with
212 /// ```
213 /// # use persistent_keystore_rs::Client;
214 /// # let mut c = Client::new(std::path::Path::new("saved2.db"), None).unwrap();
215 /// c.save();
216 /// # std::fs::remove_file("saved2.db").unwrap();
217 fn save(&mut self) -> Result<(), DatabaseError> {
218 trace!("Saving database");
219 if let Ok(database) = self.database.lock() {
220 if let Ok(raw_file) = self.raw_file.lock() {
221 debug!("Saving database {:?}", raw_file);
222 let mut f = OpenOptions::new()
223 .write(true)
224 .read(true)
225 .create(true)
226 .truncate(true)
227 .append(false)
228 .open(raw_file.as_path())?;
229 let output = bincode::serialize(&database.clone())?;
230 let compressed = compress_prepend_size(&output);
231 f.seek(SeekFrom::Start(0))?;
232 f.write_all(&compressed)?;
233 f.flush()?;
234 f.sync_all()?;
235 drop(f);
236 return Ok(())
237
238 } else {
239 error!("Unable to get file mutex");
240 };
241 };
242 error!("Unable to get database lock");
243 Err(DatabaseError::UnableToGetLock)
244 }
245
246 /// Creates a table within the database of the associated client
247 /// ```
248 /// use persistent_keystore_rs::{Client, Table, FieldType};
249 /// use persistent_keystore_rs::prelude::*;
250 /// use std::time::Duration;
251 /// # use std::path::Path;
252 /// let mut c = Client::new(Path::new("createtable.db"), None).unwrap();
253 /// let table = Table::new()
254 /// .name(String::from("MyTable"))
255 /// .primary_field(FieldType::String).unwrap()
256 /// .add_field(String::from("TimeStamp"), FieldType::Date).unwrap()
257 /// .add_expiration(Duration::from_secs(2592000))
258 /// .build().unwrap();
259 /// c.create_table(table).unwrap();
260 /// # std::fs::remove_file("createtable.db").unwrap();
261 /// ```
262 fn create_table(&mut self, table: Table) -> Result<(), DatabaseError> {
263 trace!("Creating table {}", table.name);
264 if let Ok(mut database) = self.database.lock() {
265 match database.get_table(&table.name.clone()) {
266 Ok(_) => {
267 error!("Table {} exists", table.name);
268 return Err(DatabaseError::TableExists(table.name))
269 },
270 Err(_) => {
271 debug!("Creating table {}", table.name);
272 return database.create_table(table)
273 },
274 };
275 };
276 error!("Unable to get database lock");
277 Err(DatabaseError::UnableToGetLock)
278 }
279
280 /// Lists tables within the database of the associated client
281 /// ```
282 /// # use persistent_keystore_rs::{Client, Table, FieldType};
283 /// # use std::time::Duration;
284 /// # use std::path::Path;
285 /// let mut c = Client::new(Path::new("listtable.db"), None).unwrap();
286 /// # let table = Table::new()
287 /// # .name(String::from("MyTable"))
288 /// # .primary_field(FieldType::String).unwrap()
289 /// # .add_field(String::from("TimeStamp"), FieldType::Date).unwrap()
290 /// # .add_expiration(Duration::from_secs(2592000))
291 /// # .build().unwrap();
292 /// # c.create_table(table).unwrap();
293 /// let tables = c.list_tables().unwrap();
294 /// assert_eq!(tables.len(), 1);
295 /// assert_eq!(tables[0], String::from("MyTable"));
296 /// # std::fs::remove_file("listtable.db").unwrap();
297 /// ```
298 fn list_tables(&mut self) -> Result<Vec<String>, DatabaseError> {
299 trace!("Listing Tables");
300 if let Ok(mut database) = self.database.lock() {
301 let tables = database.list_tables();
302 debug!("Listed {} tables", tables.len());
303 return Ok(tables)
304 };
305 error!("Unable to get database lock");
306 Err(DatabaseError::UnableToGetLock)
307 }
308
309 /// Drops the specified table from within the database of the associated client
310 /// ```
311 /// # use persistent_keystore_rs::{Client, Table, FieldType};
312 /// # use std::time::Duration;
313 /// # use std::path::Path;
314 /// let mut c = Client::new(Path::new("droptable.db"), None).unwrap();
315 /// # let table = Table::new()
316 /// # .name(String::from("MyTable"))
317 /// # .primary_field(FieldType::String).unwrap()
318 /// # .add_field(String::from("TimeStamp"), FieldType::Date).unwrap()
319 /// # .add_expiration(Duration::from_secs(2592000))
320 /// # .build().unwrap();
321 /// # c.create_table(table).unwrap();
322 /// let tables = c.list_tables().unwrap();
323 /// assert_eq!(tables.len(), 1);
324 /// assert_eq!(tables[0], String::from("MyTable"));
325 /// c.drop_table(&String::from("MyTable")).unwrap();
326 /// let current_tables = c.list_tables().unwrap();
327 /// assert_eq!(current_tables.len(), 0);
328 /// # std::fs::remove_file("droptable.db").unwrap();
329 /// ```
330 fn drop_table(&mut self, table: &String) -> Result<(), DatabaseError> {
331 trace!("Dropping table {}", table);
332 if let Ok(mut database) = self.database.lock() {
333 debug!("Dropping table {}", table);
334 return database.drop_table(table)
335 };
336 error!("Unable to get database lock");
337 Err(DatabaseError::UnableToGetLock)
338 }
339
340 /// Inserts the provided entry into the specified table within the database of the associated client.
341 /// If an entry with the same primary key exists, an DatabaseError::EntryExists is returned
342 /// ```
343 /// # use persistent_keystore_rs::{Client, Table, FieldType};
344 /// use persistent_keystore_rs::{Entry, Field};
345 /// # use std::time::Duration;
346 /// # use std::path::Path;
347 /// use std::time::SystemTime;
348 /// let mut c = Client::new(Path::new("insertentry.db"), None).unwrap();
349 /// # let table = Table::new()
350 /// # .name(String::from("MyTable"))
351 /// # .primary_field(FieldType::String).unwrap()
352 /// # .add_field(String::from("TimeStamp"), FieldType::Date).unwrap()
353 /// # .add_expiration(Duration::from_secs(2592000))
354 /// # .build().unwrap();
355 /// # c.create_table(table).unwrap();
356 /// let entry = Entry::new()
357 /// .set_primary_field(Field::String("MyFirstEntry".to_string())).unwrap()
358 /// .add_field("TimeStamp".to_string(), Field::Date(SystemTime::now())).unwrap()
359 /// .build().unwrap();
360 /// c.insert("MyTable".to_string(), entry).unwrap();
361 /// # std::fs::remove_file("insertentry.db").unwrap();
362 /// ```
363 fn insert(&mut self, table: String, entry: Entry) -> Result<(), DatabaseError> {
364 trace!("Inserting entry into table {}: {}", table, entry);
365 if let Ok(mut database) = self.database.lock() {
366 match database.get_table(&table) {
367 Ok(t) => {
368 debug!("Inserting entry into table {}", table);
369 return t.insert(entry)
370 },
371 Err(_) => {
372 error!("Table {} does not exist", table);
373 return Err(DatabaseError::TableDoesNotExist(table))
374 }
375 }
376 };
377 error!("Unable to get database lock");
378 Err(DatabaseError::UnableToGetLock)
379 }
380
381 /// Inserts the provided entry into the specified table within the database of the associated client.
382 /// If an entry with the same primary key exists, the entry is updated.
383 /// ```
384 /// # use persistent_keystore_rs::{Client, Table, FieldType};
385 /// use persistent_keystore_rs::{Entry, Field};
386 /// # use std::time::Duration;
387 /// # use std::path::Path;
388 /// use std::time::SystemTime;
389 /// let mut c = Client::new(Path::new("insertorupdateentry.db"), None).unwrap();
390 /// # let table = Table::new()
391 /// # .name(String::from("MyTable"))
392 /// # .primary_field(FieldType::String).unwrap()
393 /// # .add_field(String::from("TimeStamp"), FieldType::Date).unwrap()
394 /// # .add_expiration(Duration::from_secs(2592000))
395 /// # .build().unwrap();
396 /// # c.create_table(table).unwrap();
397 /// let entry = Entry::new()
398 /// .set_primary_field(Field::String("MyFirstEntry".to_string())).unwrap()
399 /// .add_field("TimeStamp".to_string(), Field::Date(SystemTime::now())).unwrap()
400 /// .build().unwrap();
401 /// c.insert("MyTable".to_string(), entry.clone()).unwrap();
402 /// c.insert_or_update("MyTable".to_string(), entry).unwrap();
403 /// # std::fs::remove_file("insertorupdateentry.db").unwrap();
404 /// ```
405 fn insert_or_update(&mut self, table: String, entry: Entry) -> Result<(), DatabaseError> {
406 trace!("Inserting or updating entry into table {}: {}", table, entry);
407 if let Ok(mut database) = self.database.lock() {
408 match database.get_table(&table) {
409 Ok(t) => {
410 debug!("Inserting entry into table {}", table);
411 return t.insert_or_update(entry)
412 },
413 Err(_) => {
414 error!("Table {} does not exist", table);
415 return Err(DatabaseError::TableDoesNotExist(table))
416 },
417 }
418 };
419 error!("Unable to get database lock");
420 Err(DatabaseError::UnableToGetLock)
421 }
422
423 /// Updates an existing entry in the specified table within the database of the associated client.
424 /// If an entry does not exist, DatabaseError::EntryDoesNotExists is returned
425 /// ```
426 /// # use persistent_keystore_rs::{Client, Table, FieldType};
427 /// use persistent_keystore_rs::{Entry, Field};
428 /// # use std::time::Duration;
429 /// # use std::path::Path;
430 /// use std::time::SystemTime;
431 /// let mut c = Client::new(Path::new("updateentry.db"), None).unwrap();
432 /// # let table = Table::new()
433 /// # .name(String::from("MyTable"))
434 /// # .primary_field(FieldType::String).unwrap()
435 /// # .add_field(String::from("TimeStamp"), FieldType::Date).unwrap()
436 /// # .add_expiration(Duration::from_secs(2592000))
437 /// # .build().unwrap();
438 /// # c.create_table(table).unwrap();
439 /// let entry = Entry::new()
440 /// .set_primary_field(Field::String("MyFirstEntry".to_string())).unwrap()
441 /// .add_field("TimeStamp".to_string(), Field::Date(SystemTime::now())).unwrap()
442 /// .build().unwrap();
443 /// # c.insert("MyTable".to_string(), entry.clone()).unwrap();
444 /// c.update("MyTable".to_string(), entry).unwrap();
445 /// # std::fs::remove_file("updateentry.db").unwrap();
446 /// ```
447 fn update(&mut self, table: String, entry: Entry) -> Result<(), DatabaseError> {
448 trace!("Updating entry into table {}: {}", table, entry);
449 if let Ok(mut database) = self.database.lock() {
450 match database.get_table(&table) {
451 Ok(t) => {
452 debug!("Updating entry {} in table {}", entry.primary_field, table);
453 return t.update(entry)
454 },
455 Err(_) => {
456 error!("Table {} does not exist", table);
457 return Err(DatabaseError::TableDoesNotExist(table))
458 }
459 }
460 };
461 error!("Unable to get database lock");
462 Err(DatabaseError::UnableToGetLock)
463 }
464
465 /// Get an existing entry from the specified table within the database of the associated client.
466 /// If an entry does not exist, DatabaseError::EntryDoesNotExists is returned
467 /// ```
468 /// # use persistent_keystore_rs::{Client, Table, FieldType, Entry};
469 /// use persistent_keystore_rs::Field;
470 /// # use std::time::Duration;
471 /// # use std::path::Path;
472 /// # use std::time::SystemTime;
473 /// let mut c = Client::new(Path::new("getentry.db"), None).unwrap();
474 /// # let table = Table::new()
475 /// # .name(String::from("MyTable"))
476 /// # .primary_field(FieldType::String).unwrap()
477 /// # .add_field(String::from("TimeStamp"), FieldType::Date).unwrap()
478 /// # .add_expiration(Duration::from_secs(2592000))
479 /// # .build().unwrap();
480 /// # c.create_table(table).unwrap();
481 /// # let entry = Entry::new()
482 /// # .set_primary_field(Field::String("MyFirstEntry".to_string())).unwrap()
483 /// # .add_field("TimeStamp".to_string(), Field::Date(SystemTime::now())).unwrap()
484 /// # .build().unwrap();
485 /// # c.insert("MyTable".to_string(), entry.clone()).unwrap();
486 /// let e = c.get("MyTable".to_string(), Field::String("MyFirstEntry".to_string())).unwrap();
487 /// # std::fs::remove_file("getentry.db").unwrap();
488 /// ```
489 fn get(&mut self, table: String, primary_field: Field) -> Result<Entry, DatabaseError> {
490 trace!("Getting entry {} from table {}", primary_field, table);
491 if let Ok(mut database) = self.database.lock() {
492 match database.get_table(&table) {
493 Ok(t) => {
494 debug!("Getting entry {} from table {}", primary_field, table);
495 let item = t.get(&primary_field)?;
496 return Ok(item.clone());
497 },
498 Err(_) => {
499 error!("Table {} does not exist", table);
500 return Err(DatabaseError::TableDoesNotExist(table))
501 }
502 }
503 };
504 error!("Unable to get database lock");
505 Err(DatabaseError::UnableToGetLock)
506 }
507
508 /// Delete an existing entry from the specified table within the database of the associated client.
509 /// If an entry does not exist, DatabaseError::EntryDoesNotExists is returned
510 /// ```
511 /// # use persistent_keystore_rs::{Client, Table, FieldType, Entry};
512 /// use persistent_keystore_rs::Field;
513 /// # use std::time::Duration;
514 /// # use std::path::Path;
515 /// # use std::time::SystemTime;
516 /// let mut c = Client::new(Path::new("delete.db"), None).unwrap();
517 /// # let table = Table::new()
518 /// # .name(String::from("MyTable"))
519 /// # .primary_field(FieldType::String).unwrap()
520 /// # .add_field(String::from("TimeStamp"), FieldType::Date).unwrap()
521 /// # .add_expiration(Duration::from_secs(2592000))
522 /// # .build().unwrap();
523 /// # c.create_table(table).unwrap();
524 /// # let entry = Entry::new()
525 /// # .set_primary_field(Field::String("MyFirstEntry".to_string())).unwrap()
526 /// # .add_field("TimeStamp".to_string(), Field::Date(SystemTime::now())).unwrap()
527 /// # .build().unwrap();
528 /// # c.insert("MyTable".to_string(), entry.clone()).unwrap();
529 /// c.delete("MyTable".to_string(), Field::String("MyFirstEntry".to_string())).unwrap();
530 /// # std::fs::remove_file("delete.db").unwrap();
531 /// ```
532 fn delete(&mut self, table: String, primary_field: Field) -> Result<(), DatabaseError> {
533 trace!("Deleting entry {} from table {}", primary_field, table);
534 if let Ok(mut database) = self.database.lock() {
535 match database.get_table(&table) {
536 Ok(t) => {
537 debug!("Deleting entry {} from table {}", primary_field, table);
538 return t.delete(primary_field)
539 },
540 Err(_) => {
541 error!("Table {} does not exist", table);
542 return Err(DatabaseError::TableDoesNotExist(table))
543 },
544 }
545 };
546 error!("Unable to get database lock");
547 Err(DatabaseError::UnableToGetLock)
548 }
549
550 /// Delete all entries matching the supplied criteria.
551 /// ```
552 /// # use persistent_keystore_rs::{Client, Table, FieldType, Entry};
553 /// use persistent_keystore_rs::Field;
554 /// use std::collections::HashMap;
555 /// # use std::time::Duration;
556 /// # use std::path::Path;
557 /// # use std::time::SystemTime;
558 /// let mut c = Client::new(Path::new("deletemany.db"), None).unwrap();
559 /// # let table = Table::new()
560 /// # .name(String::from("MyTable"))
561 /// # .primary_field(FieldType::String).unwrap()
562 /// # .add_field(String::from("TimeStamp"), FieldType::Date).unwrap()
563 /// # .add_field(String::from("OptionalField"), FieldType::String).unwrap()
564 /// # .add_expiration(Duration::from_secs(2592000))
565 /// # .build().unwrap();
566 /// # c.create_table(table).unwrap();
567 /// # let entry = Entry::new()
568 /// # .set_primary_field(Field::String("MyFirstEntry".to_string())).unwrap()
569 /// # .add_field("TimeStamp".to_string(), Field::Date(SystemTime::now())).unwrap()
570 /// # .add_field("OptionalField".to_string(), Field::String("MyTestingField".to_string())).unwrap()
571 /// # .build().unwrap();
572 /// # c.insert("MyTable".to_string(), entry.clone()).unwrap();
573 /// # let entry2 = Entry::new()
574 /// # .set_primary_field(Field::String("MySecondEntry".to_string())).unwrap()
575 /// # .add_field("TimeStamp".to_string(), Field::Date(SystemTime::now())).unwrap()
576 /// # .add_field("OptionalField".to_string(), Field::String("MyTestingField".to_string())).unwrap()
577 /// # .build().unwrap();
578 /// # c.insert("MyTable".to_string(), entry2.clone()).unwrap();
579 /// # let entry3 = Entry::new()
580 /// # .set_primary_field(Field::String("MyThirdEntry".to_string())).unwrap()
581 /// # .add_field("TimeStamp".to_string(), Field::Date(SystemTime::now())).unwrap()
582 /// # .add_field("OptionalField".to_string(), Field::String("MyOtherTestingField".to_string())).unwrap()
583 /// # .build().unwrap();
584 /// # c.insert("MyTable".to_string(), entry3.clone()).unwrap();
585 /// let mut criteria: HashMap<String, Field> = HashMap::new();
586 /// criteria.insert("OptionalField".to_string(), Field::String("MyTestingField".to_string()));
587 /// c.delete_many("MyTable".to_string(), criteria).unwrap();
588 /// # assert_eq!(c.scan("MyTable".to_string()).unwrap().len(), 1);
589 /// # std::fs::remove_file("deletemany.db").unwrap();
590 /// ```
591 fn delete_many(&mut self, table: String, criteria: HashMap<String, Field>) -> Result<u64, DatabaseError> {
592 trace!("Deleting many from table {}", table);
593 if let Ok(mut database) = self.database.lock() {
594 match database.get_table(&table) {
595 Ok(t) => {
596 let items = t.scan()?;
597 let mut deleted = 0;
598 'L:
599 for i in items {
600 for (k, v) in &criteria {
601 match &i.fields.get_key_value(k) {
602 Some((_, value)) => {
603 if v != *value {
604 trace!("{} does not meet criteria", i.primary_field);
605 continue 'L;
606 }
607 },
608 None => {
609 trace!("{} does not meet criteria", i.primary_field);
610 continue 'L
611 },
612 };
613
614 };
615 debug!("Deleting entry {} from table {}", i.primary_field, table);
616 t.delete(i.primary_field)?;
617 deleted+=1;
618 };
619 return Ok(deleted)
620 },
621 Err(_) => {
622 error!("Table {} does not exist", table);
623 return Err(DatabaseError::TableDoesNotExist(table))
624 },
625 };
626 };
627 error!("Unable to get database lock");
628 Err(DatabaseError::UnableToGetLock)
629 }
630
631 /// Returns all entries from the specified table within the database of the associated client.
632 /// If no entries exist, will return an empty vec
633 /// ```
634 /// # use persistent_keystore_rs::{Client, Table, FieldType, Entry, Field};
635 /// # use std::time::Duration;
636 /// # use std::path::Path;
637 /// # use std::time::SystemTime;
638 /// let mut c = Client::new(Path::new("scan.db"), None).unwrap();
639 /// # let table = Table::new()
640 /// # .name(String::from("MyTable"))
641 /// # .primary_field(FieldType::String).unwrap()
642 /// # .add_field(String::from("TimeStamp"), FieldType::Date).unwrap()
643 /// # .add_field(String::from("OptionalField"), FieldType::String).unwrap()
644 /// # .add_expiration(Duration::from_secs(2592000))
645 /// # .build().unwrap();
646 /// # c.create_table(table).unwrap();
647 /// # let entry = Entry::new()
648 /// # .set_primary_field(Field::String("MyFirstEntry".to_string())).unwrap()
649 /// # .add_field("TimeStamp".to_string(), Field::Date(SystemTime::now())).unwrap()
650 /// # .add_field("OptionalField".to_string(), Field::String("MyTestingField".to_string())).unwrap()
651 /// # .build().unwrap();
652 /// # c.insert("MyTable".to_string(), entry.clone()).unwrap();
653 /// # let entry2 = Entry::new()
654 /// # .set_primary_field(Field::String("MySecondEntry".to_string())).unwrap()
655 /// # .add_field("TimeStamp".to_string(), Field::Date(SystemTime::now())).unwrap()
656 /// # .add_field("OptionalField".to_string(), Field::String("MyTestingField".to_string())).unwrap()
657 /// # .build().unwrap();
658 /// # c.insert("MyTable".to_string(), entry2.clone()).unwrap();
659 /// # let entry3 = Entry::new()
660 /// # .set_primary_field(Field::String("MyThirdEntry".to_string())).unwrap()
661 /// # .add_field("TimeStamp".to_string(), Field::Date(SystemTime::now())).unwrap()
662 /// # .add_field("OptionalField".to_string(), Field::String("MyOtherTestingField".to_string())).unwrap()
663 /// # .build().unwrap();
664 /// # c.insert("MyTable".to_string(), entry3.clone()).unwrap();
665 /// let results = c.scan("MyTable".to_string()).unwrap();
666 /// # assert_eq!(c.scan("MyTable".to_string()).unwrap().len(), 3);
667 /// # std::fs::remove_file("scan.db").unwrap();
668 /// ```
669 fn scan(&mut self, table: String) -> Result<Vec<Entry>, DatabaseError> {
670 trace!("Scanning table {}", table);
671 if let Ok(mut database) = self.database.lock() {
672 match database.get_table(&table) {
673 Ok(t) => {
674 debug!("Scanning table {}", table);
675 return t.scan()
676 },
677 Err(_) => {
678 error!("Table {} does not exist", table);
679 return Err(DatabaseError::TableDoesNotExist(table))
680 },
681 };
682 };
683 error!("Unable to get database lock");
684 Err(DatabaseError::UnableToGetLock)
685 }
686
687 /// Query for entries within a specified table meeting the supplied criteria.
688 /// ```
689 /// # use persistent_keystore_rs::{Client, Table, FieldType, Entry};
690 /// use persistent_keystore_rs::Field;
691 /// use std::collections::HashMap;
692 /// # use std::time::Duration;
693 /// # use std::path::Path;
694 /// # use std::time::SystemTime;
695 /// let mut c = Client::new(Path::new("query.db"), None).unwrap();
696 /// # let table = Table::new()
697 /// # .name(String::from("MyTable"))
698 /// # .primary_field(FieldType::String).unwrap()
699 /// # .add_field(String::from("TimeStamp"), FieldType::Date).unwrap()
700 /// # .add_field(String::from("OptionalField"), FieldType::String).unwrap()
701 /// # .add_expiration(Duration::from_secs(2592000))
702 /// # .build().unwrap();
703 /// # c.create_table(table).unwrap();
704 /// # let entry = Entry::new()
705 /// # .set_primary_field(Field::String("MyFirstEntry".to_string())).unwrap()
706 /// # .add_field("TimeStamp".to_string(), Field::Date(SystemTime::now())).unwrap()
707 /// # .add_field("OptionalField".to_string(), Field::String("MyTestingField".to_string())).unwrap()
708 /// # .build().unwrap();
709 /// # c.insert("MyTable".to_string(), entry.clone()).unwrap();
710 /// # let entry2 = Entry::new()
711 /// # .set_primary_field(Field::String("MySecondEntry".to_string())).unwrap()
712 /// # .add_field("TimeStamp".to_string(), Field::Date(SystemTime::now())).unwrap()
713 /// # .add_field("OptionalField".to_string(), Field::String("MyTestingField".to_string())).unwrap()
714 /// # .build().unwrap();
715 /// # c.insert("MyTable".to_string(), entry2.clone()).unwrap();
716 /// # let entry3 = Entry::new()
717 /// # .set_primary_field(Field::String("MyThirdEntry".to_string())).unwrap()
718 /// # .add_field("TimeStamp".to_string(), Field::Date(SystemTime::now())).unwrap()
719 /// # .add_field("OptionalField".to_string(), Field::String("MyOtherTestingField".to_string())).unwrap()
720 /// # .build().unwrap();
721 /// # c.insert("MyTable".to_string(), entry3.clone()).unwrap();
722 /// let mut criteria: HashMap<String, Field> = HashMap::new();
723 /// criteria.insert("OptionalField".to_string(), Field::String("MyTestingField".to_string()));
724 /// let results = c.query("MyTable".to_string(), criteria).unwrap();
725 /// # assert_eq!(results.len(), 2);
726 /// # std::fs::remove_file("query.db").unwrap();
727 /// ```
728 fn query(&mut self, table: String, criteria: HashMap<String, Field>) -> Result<Vec<Entry>, DatabaseError> {
729 trace!("Querying table {}", table);
730 if let Ok(mut database) = self.database.lock() {
731 match database.get_table(&table) {
732 Ok(t) => {
733 let items = t.scan()?;
734 let mut results = Vec::new();
735 'L:
736 for i in items {
737 for (k, v) in &criteria {
738 match &i.fields.get_key_value(k) {
739 Some((_, value)) => {
740 if v != *value {
741 trace!("{} does not meet criteria", i.primary_field);
742 continue 'L;
743 }
744 },
745 None => {
746 trace!("{} does not meet criteria", i.primary_field);
747 continue 'L;
748 },
749 };
750
751 };
752 results.push(i.clone());
753 };
754 return Ok(results)
755 },
756 Err(_) => {
757 error!("Table {} does not exist", table);
758 return Err(DatabaseError::TableDoesNotExist(table))
759 },
760 };
761 };
762 error!("Unable to get database lock");
763 Err(DatabaseError::UnableToGetLock)
764 }
765
766 /// Removes entries that have expired by the specified TTL field in the table.
767 /// This is done automatically before saves if a sync_interval is provided.
768 ///
769 /// ```
770 /// use persistent_keystore_rs::{Client, Table, FieldType, Entry};
771 /// # use std::thread::sleep;
772 /// use persistent_keystore_rs::Field;
773 /// use std::collections::HashMap;
774 /// # use std::time::Duration;
775 /// # use std::path::Path;
776 /// # use std::time::SystemTime;
777 /// let mut c = Client::new(Path::new("prune.db"), None).unwrap();
778 /// let table = Table::new()
779 /// .name(String::from("MyTable"))
780 /// .add_expiration(Duration::from_secs(1))
781 /// .primary_field(FieldType::String).unwrap()
782 /// .add_field("FirstKey".to_string(), FieldType::I64).unwrap()
783 /// .add_optional_field("OptionalKey".to_string(), FieldType::String).unwrap()
784 /// .build().unwrap();
785 ///
786 /// c.create_table(table).unwrap();
787 ///
788 /// let entry_first = Entry::new()
789 /// .set_primary_field(Field::String("This should Succeed".to_string())).unwrap()
790 /// .add_field("FirstKey".to_string(), Field::I64(123123)).unwrap()
791 /// .add_field("OptionalKey".to_string(), Field::String("My first entry".to_string())).unwrap()
792 /// .build().unwrap();
793 ///
794 /// let result = c.insert("MyTable".to_string(), entry_first);
795 ///
796 /// if let Err(e) = result {
797 /// panic!("No error expected, received {}", e)
798 /// }
799 ///
800 /// let output = c.scan("MyTable".to_string()).unwrap();
801 ///
802 /// if output.len() != 1 {
803 /// panic!("Expected scan results to be 1")
804 /// };
805 ///
806 /// sleep(Duration::from_secs(2));
807 /// c.prune().unwrap();
808 ///
809 /// let output = c.scan("MyTable".to_string()).unwrap();
810 /// if output.len() != 0 {
811 /// panic!("Expected scan results to be 0")
812 /// };
813 /// # std::fs::remove_file("prune.db").unwrap();
814 /// ```
815 fn prune(&mut self) -> Result<(), DatabaseError> {
816 trace!("Pruning database");
817 if let Ok(mut database) = self.database.lock() {
818 let current_time = SystemTime::now();
819 for t in database.list_tables() {
820 if let Ok(table) = database.get_table(&t) {
821 if let Some(expire_after) = table.expire_after {
822 let items = table.scan()?;
823 for item in items {
824 if let Some(n) = item.last_timestamp {
825 if let Ok(last_time) = current_time.duration_since(n) {
826 if last_time > expire_after {
827 debug!("Pruning item {}", item.clone());
828 table.delete(item.primary_field)?;
829 } else {
830 trace!("Not yet expired {}", item.clone());
831 }
832 }
833 }
834 }
835 } else {
836 debug!("No expire after setting for table {}", t);
837 }
838 }
839 };
840 return Ok(())
841 };
842 error!("Unable to get database lock");
843 Err(DatabaseError::UnableToGetLock)
844 }
845}
846
847#[cfg(test)]
848mod tests {
849 use super::*;
850 use std::env::temp_dir;
851
852 fn create_client_table(name: String) -> (Box<dyn DatabaseClient>, TableBuilder) {
853 let mut temp_dir_path = temp_dir();
854 temp_dir_path.push(format!("{}.db", name));
855 if temp_dir_path.exists() {
856 std::fs::remove_file(temp_dir_path.clone().to_str().unwrap()).unwrap();
857 };
858
859 let c = Client::new(temp_dir_path, None).unwrap();
860
861 let table = structs::Table::new()
862 .name(name);
863
864 (c, table)
865 }
866
867 #[test]
868 fn entry_missing_fields() {
869 let missing_fields_entry = structs::Entry::new()
870 .set_primary_field(Field::String("This entry should fail".to_string())).unwrap()
871 .build();
872
873 if let Err(e) = missing_fields_entry {
874 match e {
875 DatabaseError::EntryMustContainFields => {},
876 _ => panic!("Expected EntryMustContainFields but not {}", e),
877 };
878 } else {
879 panic!("Expected error and did not receive one");
880 };
881 }
882
883 #[test]
884 fn entry_mismatched_primary_field_type() {
885 let (mut c, table_builder) = create_client_table("MismatchedPrimaryFieldType".to_string());
886
887 let table = table_builder.primary_field(structs::FieldType::String).unwrap()
888 .add_field("FirstKey".to_string(), structs::FieldType::I64).unwrap()
889 .add_optional_field("OptionalKey".to_string(), FieldType::String).unwrap()
890 .build().unwrap();
891
892 c.create_table(table).unwrap();
893
894 let mismatched_primary_field_type = structs::Entry::new()
895 .set_primary_field(Field::I64(123123)).unwrap()
896 .add_field("FirstKey".to_string(), Field::String("testing".to_string())).unwrap()
897 .build().unwrap();
898
899 let result = c.insert("MismatchedPrimaryFieldType".to_string(), mismatched_primary_field_type);
900
901 if let Err(e) = result {
902 match e {
903 DatabaseError::MismatchedFieldType => {},
904 _ => panic!("Expected MismatchedField type, not {}", e),
905 };
906 } else {
907 panic!("Expected MismatchedFieldType error but received none");
908 };
909 }
910
911 #[test]
912 fn entry_mismatched_secondary_field_type() {
913 let (mut c, table_builder) = create_client_table("MismatchedSecondaryFieldType".to_string());
914
915 let table = table_builder.primary_field(structs::FieldType::String).unwrap()
916 .add_field("FirstKey".to_string(), structs::FieldType::I64).unwrap()
917 .add_optional_field("OptionalKey".to_string(), FieldType::String).unwrap()
918 .build().unwrap();
919
920 c.create_table(table).unwrap();
921
922 let mismatched_optional_field = structs::Entry::new()
923 .set_primary_field(Field::String("This should also fail".to_string())).unwrap()
924 .add_field("FirstKey".to_string(), Field::String("Should fail".to_string())).unwrap()
925 .build().unwrap();
926
927 let result = c.insert("MismatchedSecondaryFieldType".to_string(), mismatched_optional_field);
928
929 if let Err(e) = result {
930 match e {
931 DatabaseError::MismatchedFieldType => {},
932 _ => panic!("Expected MismatchedField type, not {}", e),
933 };
934 } else {
935 panic!("Expected MismatchedFieldType error but received none");
936 };
937 }
938
939 #[test]
940 fn entry_extra_undefined_field() {
941 let (mut c, table_builder) = create_client_table("EntryExtraUndefiniedField".to_string());
942
943 let table = table_builder.primary_field(structs::FieldType::String).unwrap()
944 .add_field("FirstKey".to_string(), structs::FieldType::I64).unwrap()
945 .add_optional_field("OptionalKey".to_string(), FieldType::String).unwrap()
946 .build().unwrap();
947
948 c.create_table(table).unwrap();
949
950 let extra_field = structs::Entry::new()
951 .set_primary_field(Field::String("This should also fail".to_string())).unwrap()
952 .add_field("FirstKey".to_string(), Field::I64(123123)).unwrap()
953 .add_field("OptionalKey".to_string(), Field::String("My second field".to_string())).unwrap()
954 .add_field("ExtraField".to_string(), Field::String("ExtraField".to_string())).unwrap()
955 .build().unwrap();
956
957 let result = c.insert("EntryExtraUndefiniedField".to_string(), extra_field);
958
959 if let Err(e) = result {
960 match e {
961 DatabaseError::UnsupportedField(_) => {},
962 _ => panic!("Expected UnsupportedField, got {}", e),
963 };
964 } else {
965 panic!("Expected UnsupportedField, got none")
966 }
967 }
968
969 #[test]
970 fn entry_table_does_not_exist() {
971 let (mut c, table_builder) = create_client_table("EntryTableDoesNotExist".to_string());
972
973 let table = table_builder.primary_field(structs::FieldType::String).unwrap()
974 .add_field("FirstKey".to_string(), structs::FieldType::I64).unwrap()
975 .add_optional_field("OptionalKey".to_string(), FieldType::String).unwrap()
976 .build().unwrap();
977
978 c.create_table(table).unwrap();
979
980 let extra_field = structs::Entry::new()
981 .set_primary_field(Field::String("This should also fail".to_string())).unwrap()
982 .add_field("FirstKey".to_string(), Field::I64(123123)).unwrap()
983 .add_field("OptionalKey".to_string(), Field::String("My second field".to_string())).unwrap()
984 .build().unwrap();
985
986 let result = c.insert("EntryTableDoesNotExist2".to_string(), extra_field);
987
988 if let Err(e) = result {
989 match e {
990 DatabaseError::TableDoesNotExist(_) => {},
991 _ => panic!("Expected TableDoesNotExist, got {}", e),
992 };
993 } else {
994 panic!("Expected TableDoesNotExist, got none")
995 }
996 }
997
998 #[test]
999 fn entry_success_without_optional() {
1000 let (mut c, table_builder) = create_client_table("EntrySuccessWithoutOptional".to_string());
1001
1002 let table = table_builder.primary_field(structs::FieldType::String).unwrap()
1003 .add_field("FirstKey".to_string(), structs::FieldType::I64).unwrap()
1004 .add_optional_field("OptionalKey".to_string(), FieldType::String).unwrap()
1005 .build().unwrap();
1006
1007 c.create_table(table).unwrap();
1008
1009 let extra_field = structs::Entry::new()
1010 .set_primary_field(Field::String("This should Succeed".to_string())).unwrap()
1011 .add_field("FirstKey".to_string(), Field::I64(123123)).unwrap()
1012 .build().unwrap();
1013
1014 let result = c.insert("EntrySuccessWithoutOptional".to_string(), extra_field);
1015
1016 if let Err(e) = result {
1017 panic!("No error expected, received {}", e)
1018 }
1019 }
1020
1021 #[test]
1022 fn entry_success_with_optional() {
1023 let (mut c, table_builder) = create_client_table("EntrySuccessWithOptional".to_string());
1024
1025 let table = table_builder.primary_field(structs::FieldType::String).unwrap()
1026 .add_field("FirstKey".to_string(), structs::FieldType::I64).unwrap()
1027 .add_optional_field("OptionalKey".to_string(), FieldType::String).unwrap()
1028 .build().unwrap();
1029
1030 c.create_table(table).unwrap();
1031
1032 let extra_field = structs::Entry::new()
1033 .set_primary_field(Field::String("This should Succeed".to_string())).unwrap()
1034 .add_field("FirstKey".to_string(), Field::I64(123123)).unwrap()
1035 .add_field("OptionalKey".to_string(), Field::String("My second field".to_string())).unwrap()
1036 .build().unwrap();
1037
1038 let result = c.insert("EntrySuccessWithOptional".to_string(), extra_field);
1039
1040 if let Err(e) = result {
1041 panic!("No error expected, received {}", e)
1042 }
1043 }
1044
1045 #[test]
1046 fn entry_update_success() {
1047 let (mut c, table_builder) = create_client_table("EntryUpdateSuccess".to_string());
1048
1049 let table = table_builder.primary_field(structs::FieldType::String).unwrap()
1050 .add_field("FirstKey".to_string(), structs::FieldType::I64).unwrap()
1051 .add_optional_field("OptionalKey".to_string(), FieldType::String).unwrap()
1052 .build().unwrap();
1053
1054 c.create_table(table).unwrap();
1055
1056 let entry_first = structs::Entry::new()
1057 .set_primary_field(Field::String("This should Succeed".to_string())).unwrap()
1058 .add_field("FirstKey".to_string(), Field::I64(123123)).unwrap()
1059 .add_field("OptionalKey".to_string(), Field::String("My first entry".to_string())).unwrap()
1060 .build().unwrap();
1061
1062 let result = c.insert("EntryUpdateSuccess".to_string(), entry_first);
1063
1064 if let Err(e) = result {
1065 panic!("No error expected, received {}", e)
1066 }
1067
1068 let entry_second = structs::Entry::new()
1069 .set_primary_field(Field::String("This should Succeed".to_string())).unwrap()
1070 .add_field("FirstKey".to_string(), Field::I64(123123)).unwrap()
1071 .add_field("OptionalKey".to_string(), Field::String("My second entry".to_string())).unwrap()
1072 .build().unwrap();
1073
1074 let result = c.update("EntryUpdateSuccess".to_string(), entry_second.clone());
1075
1076 if let Err(e) = result {
1077 panic!("No error expected, received {}", e)
1078 }
1079
1080 let curent_entry = c.get("EntryUpdateSuccess".to_string(), Field::String("This should Succeed".to_string())).unwrap();
1081 assert!(curent_entry.primary_field==entry_second.primary_field);
1082 assert!(curent_entry.fields==entry_second.fields);
1083 }
1084
1085 #[test]
1086 fn entry_failure_second_insert() {
1087 let (mut c, table_builder) = create_client_table("SecondInsertFailure".to_string());
1088
1089 let table = table_builder.primary_field(structs::FieldType::String).unwrap()
1090 .add_field("FirstKey".to_string(), structs::FieldType::I64).unwrap()
1091 .add_optional_field("OptionalKey".to_string(), FieldType::String).unwrap()
1092 .build().unwrap();
1093
1094 c.create_table(table).unwrap();
1095
1096 let entry_first = structs::Entry::new()
1097 .set_primary_field(Field::String("This should Succeed".to_string())).unwrap()
1098 .add_field("FirstKey".to_string(), Field::I64(123123)).unwrap()
1099 .add_field("OptionalKey".to_string(), Field::String("My first entry".to_string())).unwrap()
1100 .build().unwrap();
1101
1102 let result = c.insert("SecondInsertFailure".to_string(), entry_first);
1103
1104 if let Err(e) = result {
1105 panic!("No error expected, received {}", e)
1106 }
1107
1108 let entry_second = structs::Entry::new()
1109 .set_primary_field(Field::String("This should Succeed".to_string())).unwrap()
1110 .add_field("FirstKey".to_string(), Field::I64(123123)).unwrap()
1111 .add_field("OptionalKey".to_string(), Field::String("My second entry".to_string())).unwrap()
1112 .build().unwrap();
1113
1114 let result = c.insert("SecondInsertFailure".to_string(), entry_second);
1115
1116 if let Err(e) = result {
1117 match e {
1118 DatabaseError::EntryExists => {},
1119 _ => panic!("Expected EntryExists, got {}", e),
1120 };
1121 } else {
1122 panic!("Expected EntryExists, got none")
1123 }
1124 }
1125
1126 #[test]
1127 fn database_open_close() {
1128 let (mut c, table_builder) = create_client_table("OpenSaveCloseOpen".to_string());
1129
1130 let table = table_builder.primary_field(structs::FieldType::String).unwrap()
1131 .add_field("FirstKey".to_string(), structs::FieldType::I64).unwrap()
1132 .add_optional_field("OptionalKey".to_string(), FieldType::String).unwrap()
1133 .build().unwrap();
1134
1135 c.create_table(table).unwrap();
1136
1137 let entry_first = structs::Entry::new()
1138 .set_primary_field(Field::String("This should Succeed".to_string())).unwrap()
1139 .add_field("FirstKey".to_string(), Field::I64(123123)).unwrap()
1140 .add_field("OptionalKey".to_string(), Field::String("My first entry".to_string())).unwrap()
1141 .build().unwrap();
1142
1143 let result = c.insert_or_update("OpenSaveCloseOpen".to_string(), entry_first.clone());
1144
1145 if let Err(e) = result {
1146 panic!("No error expected, received {}", e)
1147 }
1148
1149 c.save().unwrap();
1150 drop(c);
1151
1152 let mut temp_dir_path = temp_dir();
1153 temp_dir_path.push("OpenSaveCloseOpen.db");
1154
1155 let mut c = Client::open(temp_dir_path).unwrap();
1156 let curent_entry = c.get("OpenSaveCloseOpen".to_string(), Field::String("This should Succeed".to_string())).unwrap();
1157 assert!(curent_entry.primary_field==entry_first.primary_field);
1158 assert!(curent_entry.fields==entry_first.fields);
1159 }
1160
1161 #[test]
1162 fn query_item() {
1163 let (mut c, table_builder) = create_client_table("QueryItems".to_string());
1164
1165 let table = table_builder.primary_field(structs::FieldType::String).unwrap()
1166 .add_field("FirstKey".to_string(), structs::FieldType::I64).unwrap()
1167 .add_optional_field("OptionalKey".to_string(), FieldType::String).unwrap()
1168 .build().unwrap();
1169
1170 c.create_table(table).unwrap();
1171
1172 let entry_first = structs::Entry::new()
1173 .set_primary_field(Field::String("This should Succeed".to_string())).unwrap()
1174 .add_field("FirstKey".to_string(), Field::I64(123123)).unwrap()
1175 .add_field("OptionalKey".to_string(), Field::String("My first entry".to_string())).unwrap()
1176 .build().unwrap();
1177
1178 let result = c.insert("QueryItems".to_string(), entry_first.clone());
1179
1180 if let Err(e) = result {
1181 panic!("No error expected, received {}", e)
1182 }
1183
1184 let entry_second = structs::Entry::new()
1185 .set_primary_field(Field::String("This should Succeed too".to_string())).unwrap()
1186 .add_field("FirstKey".to_string(), Field::I64(12312312)).unwrap()
1187 .add_field("OptionalKey".to_string(), Field::String("My second entry".to_string())).unwrap()
1188 .build().unwrap();
1189
1190 let result = c.insert("QueryItems".to_string(), entry_second.clone());
1191
1192 if let Err(e) = result {
1193 panic!("No error expected, received {}", e)
1194 }
1195
1196 let first_query = c.query("QueryItems".to_string(), HashMap::from_iter(vec![("OptionalKey".to_string(), Field::String("My first entry".to_string()))])).unwrap();
1197 let second_query = c.query("QueryItems".to_string(), HashMap::from_iter(vec![("FirstKey".to_string(),Field::I64(12312312))])).unwrap();
1198
1199 assert!(first_query[0].primary_field==entry_first.primary_field);
1200 assert!(first_query[0].fields==entry_first.fields);
1201 assert!(first_query.len() == 1);
1202
1203 assert!(second_query[0].primary_field==entry_second.primary_field);
1204 assert!(second_query[0].fields==entry_second.fields);
1205 assert!(second_query.len() == 1);
1206 }
1207}