vdb/
lib.rs

1extern crate chrono;
2extern crate serde;
3#[macro_use]
4extern crate serde_derive;
5extern crate serde_json;
6
7//use chrono::{DateTime, Duration, Utc};
8use chrono::{Local, NaiveDateTime};
9use std::collections::{HashMap, HashSet};
10use std::error::Error;
11use std::fmt;
12use std::fs::File;
13use std::io::Read;
14use std::io::Write;
15use std::path::Path;
16
17/// A basic database system to store key/value pairs with few dependencies.
18///
19/// # Examples
20///
21/// ```
22/// use vdb::{Db, Entry, Predicate};
23/// let mut db = Db::new("test-db");
24/// let row_1 = db.add_row(vec![
25///         Entry::new_string("word", "cocina"),
26///         Entry::new_string("translation", "cuisine"),
27///         Entry::new_string("translation", "kitchen"),
28/// ]);
29/// let row_2 = db.add_row(vec![
30///         Entry::new_string("word", "coche"),
31///         Entry::new_string("translation", "car"),
32/// ]);
33///
34/// // Load and save
35/// db.save();
36/// let mut new_db = Db::load("test-db").unwrap();
37/// let row_ids = new_db.find_all_row_ids();
38/// assert_eq!(row_ids.len(), 2);
39///
40/// // Find rows
41/// let row_ids = db.find_row_ids_by_predicate(&vec![Predicate::new_equal_string("word", "coche")], None);
42/// assert_eq!(row_ids, [row_2]);
43/// let entries = db.entries_from_row_ids(&row_ids, &["translation"]);
44/// assert_eq!(entries[0][0], Entry::new_string("translation", "car"));
45///
46/// // Delete
47/// let coche = db.find_first_row_id_by_value("word", &Db::db_string("coche"));
48/// assert_eq!(coche, Some(row_2));
49/// db.delete_rows(&[row_1, row_2]);
50/// let no_coche = db.find_first_row_id_by_value("word", &Db::db_string("coche"));
51/// assert_eq!(no_coche, None);
52/// ```
53
54/// Data types currently implemented in the database
55#[derive(Serialize, Deserialize, PartialEq, Eq, Hash, Clone, Debug)]
56pub enum Data {
57    DbString(String),
58    DbI32(i32),
59    DbDateTime(NaiveDateTime),
60}
61
62impl fmt::Display for Data {
63    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
64        let printable = match self {
65            Data::DbDateTime(date_time) => date_time.format("%Y-%m-%d %H:%M").to_string(),
66            Data::DbI32(number) => format!("{}", number),
67            Data::DbString(string) => string.clone(),
68        };
69        write!(f, "{}", printable)
70    }
71}
72
73impl Data {
74    /// Tests if the data starts with the given string
75    fn starts_with(&self, data: &Data) -> bool {
76        if let (Data::DbString(left), Data::DbString(right)) = (self, data) {
77            left.starts_with(right)
78        } else {
79            false
80        }
81    }
82
83    /// Tests if the data contains the given string
84    fn contains(&self, data: &Data) -> bool {
85        if let (Data::DbString(left), Data::DbString(right)) = (self, data) {
86            left.contains(right)
87        } else {
88            false
89        }
90    }
91
92    /// Returns new DbDateTime with current time as timestamp
93    pub fn now() -> Data {
94        Data::DbDateTime(Local::now().naive_local())
95    }
96
97    pub fn date(&self) -> Option<String> {
98        if let Data::DbDateTime(d) = self {
99            Some(d.format("%Y-%m-%d").to_string())
100        } else {
101            None
102        }
103    }
104}
105
106/// The Row Identifier is used to reference each data set and is used by many methods where the
107/// actual data is not used directly.
108#[derive(Serialize, Deserialize, PartialEq, Eq, Hash, Clone, Debug, Copy, PartialOrd, Ord)]
109pub struct RowId(pub usize);
110
111/// Each RowId has many entries. Comparable to column name+data in relational databases.
112#[derive(Serialize, Deserialize, PartialEq, Eq, Hash, Clone, Debug)]
113pub struct Entry {
114    pub name: String,
115    pub value: Data,
116}
117
118impl Entry {
119    /// Shortcut for creating a new `Entry` with a `DbString`
120    pub fn new_string(name: &str, value: &str) -> Entry {
121        Entry {
122            name: String::from(name),
123            value: Db::db_string(value),
124        }
125    }
126
127    /// Shortcut for creating a new `Entry` with a `DbI32`
128    pub fn new_i32(name: &str, value: i32) -> Entry {
129        Entry {
130            name: String::from(name),
131            value: Db::db_i32(value),
132        }
133    }
134
135    /// # Examples
136    ///
137    /// ```
138    /// use vdb::{Entry,Predicate};
139    /// let a = Entry::new_string("mundo", "world");
140    ///
141    /// assert_eq!(a.compare(&Predicate::new_equal_string("mundo", "world")), true);
142    /// assert_eq!(a.compare(&Predicate::new_contains("mundo", "orl")), true);
143    /// assert_eq!(a.compare(&Predicate::new_equal_string("mundo", "World")), false);
144    /// ```
145    pub fn compare(&self, predicate: &Predicate) -> bool {
146        match &predicate.predicate_type {
147            PredicateType::Any => predicate.entry.name == self.name,
148            PredicateType::Equal => {
149                predicate.entry.name == self.name && predicate.entry.value == self.value
150            }
151            PredicateType::StartsWith => {
152                self.name == predicate.entry.name && self.value.starts_with(&predicate.entry.value)
153            }
154            PredicateType::Contains => {
155                self.name == predicate.entry.name && self.value.contains(&predicate.entry.value)
156            }
157        }
158    }
159
160    pub fn compare_all(entries: &[Entry], predicate: &Predicate) -> bool {
161        for entry in entries {
162            if entry.compare(predicate) {
163                return true;
164            }
165        }
166        false
167    }
168
169    /// Return true if there is any entry with the given name
170    pub fn check_by_name(entries: &[Entry], name: &str) -> bool {
171        for entry in entries {
172            if entry.name == name {
173                return true;
174            }
175        }
176        false
177    }
178
179    /// Return true if there is any entry with the given name
180    pub fn check_by_value(entries: &[Entry], name: &str, value: &Data) -> bool {
181        for entry in entries {
182            if entry.name == name && &entry.value == value {
183                return true;
184            }
185        }
186        false
187    }
188
189    /// Return first `Entry` in a given list that matches `name`
190    pub fn get_first_by_name(entries: &[Entry], name: &str) -> Option<Entry> {
191        for entry in entries {
192            if entry.name == name {
193                return Some(entry.clone());
194            }
195        }
196        None
197    }
198
199    /// Return first `Entry` in a given list that matches `name` as mutable reference
200    pub fn get_first_by_name_mut<'a>(
201        entries: &'a mut Vec<Entry>,
202        name: &str,
203    ) -> Option<&'a mut Entry> {
204        for entry in entries {
205            if entry.name == name {
206                return Some(entry);
207            }
208        }
209        None
210    }
211}
212
213#[derive(PartialEq, Debug)]
214pub enum PredicateType {
215    Equal,
216    StartsWith,
217    Contains,
218    Any,
219}
220
221/// Used to compare database entries, e. g. in queries (fn find_*)
222///
223/// # Examples
224///
225/// ```
226/// use vdb::{Entry,Predicate};
227/// let a = Entry::new_string("mundo", "world");
228///
229/// assert_eq!(a.compare(&Predicate::new_equal_string("mundo", "world")), true);
230/// assert_eq!(a.compare(&Predicate::new_starts_with("mundo", "worl")), true);
231/// assert_eq!(a.compare(&Predicate::new_contains("mundo", "orl")), true);
232/// assert_eq!(a.compare(&Predicate::new_equal_string("mundo", "planet")), false);
233/// ```
234#[derive(Debug)]
235pub struct Predicate {
236    pub predicate_type: PredicateType,
237    pub entry: Entry,
238}
239
240impl Predicate {
241    /// Shortcut for creating a new `Predicate` that tests for equality with a `DbI32`
242    pub fn new_equal_i32(name: &str, value: i32) -> Predicate {
243        Predicate {
244            predicate_type: PredicateType::Equal,
245            entry: Entry {
246                name: String::from(name),
247                value: Db::db_i32(value),
248            },
249        }
250    }
251
252    /// Shortcut for creating a new `Predicate` that searches database for `DbString`s equal to
253    /// `value`
254    pub fn new_any_string(name: &str) -> Predicate {
255        Predicate {
256            predicate_type: PredicateType::Any,
257            entry: Entry {
258                name: String::from(name),
259                value: Db::db_string(""),
260            },
261        }
262    }
263
264    /// Shortcut for creating a new `Predicate` that searches database for `DbString`s equal to
265    /// `value`
266    pub fn new_equal_string(name: &str, value: &str) -> Predicate {
267        Predicate {
268            predicate_type: PredicateType::Equal,
269            entry: Entry {
270                name: String::from(name),
271                value: Db::db_string(value),
272            },
273        }
274    }
275
276    /// Shortcut for creating a new `Predicate` that searches database for `DbString`s starting
277    /// with `value`
278    pub fn new_starts_with(name: &str, value: &str) -> Predicate {
279        Predicate {
280            predicate_type: PredicateType::StartsWith,
281            entry: Entry {
282                name: String::from(name),
283                value: Db::db_string(value),
284            },
285        }
286    }
287    /// Shortcut for creating a new `Predicate` that searches database for `DbString`s that contain
288    /// `value`
289    pub fn new_contains(name: &str, value: &str) -> Predicate {
290        Predicate {
291            predicate_type: PredicateType::Contains,
292            entry: Entry {
293                name: String::from(name),
294                value: Db::db_string(value),
295            },
296        }
297    }
298}
299
300#[derive(Serialize, Deserialize, PartialEq, Clone, Debug)]
301struct Row {
302    pub row_id: RowId,
303    pub entry: Entry,
304}
305
306/// Container for the database. Usually only one is used per application.
307///
308/// # Examples
309///
310/// ```
311/// use vdb::{Db, Entry};
312/// let mut db = Db::new("test-db");
313/// let _row_id = db.add_row(vec![Entry::new_string("mundo", "world")]);
314/// ```
315#[derive(Serialize, Deserialize, PartialEq, Clone, Debug)]
316pub struct Db {
317    full_filename: String,
318    row_max: RowId,
319    by_row_id: HashMap<RowId, Vec<Entry>>,
320    by_name: HashMap<String, HashSet<RowId>>,
321    by_value: HashMap<Entry, HashSet<RowId>>,
322}
323
324impl Db {
325    /// Create new database in memory. The file is not created until `save()` is called.
326    pub fn new(filename: &str) -> Db {
327        Db {
328            full_filename: Db::build_filename(filename),
329            row_max: RowId(0),
330            by_row_id: HashMap::new(),
331            by_name: HashMap::new(),
332            by_value: HashMap::new(),
333        }
334    }
335
336    /// Load a database file from the filesystem under the subdirectory `save/`.
337    ///
338    /// # Errors
339    ///
340    /// May return errors from external modules while opening the file or parsing the contents.
341    pub fn load(filename: &str) -> Result<Db, Box<Error>> {
342        let full_filename = Db::build_filename(filename);
343        let mut file = File::open(full_filename)?;
344        let mut contents = String::new();
345        file.read_to_string(&mut contents)?;
346        let mut db = Db::new(filename);
347        let row_id_map: HashMap<RowId, Vec<Entry>> = serde_json::from_str(&contents)?;
348        for (_row_id, entries) in row_id_map {
349            db.add_row(entries);
350        }
351        Ok(db)
352    }
353
354    /// Save database under the subdirectory `save/` with the same name it was `open`ed or `create`d
355    /// with. The subdirectory `save/` must exist.
356    pub fn save(&mut self) -> Result<(), Box<Error>> {
357        self.by_row_id.retain(|_key, value| !value.is_empty());
358        let path = Path::new(&self.full_filename);
359        let mut file = File::create(&path)?;
360        let serialized = match serde_json::to_string_pretty(&self.by_row_id) {
361            Ok(s) => s,
362            Err(ref e) => {
363                println!("{}|{}", e.description(), e);
364                panic!()
365            }
366        };
367        file.write_all(serialized.as_bytes())?;
368        Ok(())
369    }
370
371    /// Returns the filename of the database
372    pub fn get_name(&self) -> String {
373        // TODO: This assumes that the save prefix is "save/"
374        if self.full_filename.len() < 6 {
375            panic!("Could not Db::get_name()");
376        }
377        self.full_filename[5..].to_string()
378    }
379
380    /// Returns a new Data::DbString
381    pub fn db_string(v: &str) -> Data {
382        Data::DbString(String::from(v))
383    }
384
385    /// Returns a new Data::DbI32
386    pub fn db_i32(v: i32) -> Data {
387        Data::DbI32(v)
388    }
389
390    /// Find a i32 by name
391    /// ```
392    /// use vdb::{Db, Entry};
393    /// let mut db = Db::new("test-db");
394    /// let name = "yoyo";
395    /// let value = 7;
396    /// let _row_id = db.add_row(vec![Entry::new_i32(name, value)]);
397    /// assert_eq!(db.find_first_i32(name), Some(value));
398    /// ```
399    pub fn find_first_i32(&self, name: &str) -> Option<i32> {
400        if let Some(row_id) = self.find_first_row_id_by_name(name) {
401            if let Some(entries) = self.by_row_id.get(&row_id) {
402                if let Some(entry) = Entry::get_first_by_name(entries, name) {
403                    if let Data::DbI32(value) = entry.value {
404                        return Some(value);
405                    }
406                }
407            }
408        }
409        None
410    }
411
412    /// Find a string by name
413    /// ```
414    /// use vdb::{Db, Entry};
415    /// let mut db = Db::new("test-db");
416    /// let name = "yoyo";
417    /// let value = "lila";
418    /// let _row_id = db.add_row(vec![Entry::new_string(name, value)]);
419    /// assert_eq!(db.find_first_string(name), Some(value.to_string()));
420    /// ```
421    pub fn find_first_string(&self, name: &str) -> Option<String> {
422        if let Some(row_id) = self.find_first_row_id_by_name(name) {
423            if let Some(entries) = self.by_row_id.get(&row_id) {
424                if let Some(entry) = Entry::get_first_by_name(entries, name) {
425                    if let Data::DbString(value) = entry.value {
426                        return Some(value);
427                    }
428                }
429            }
430        }
431        None
432    }
433
434    /// Parse `&str` into a `DbDateTime`. The format string is `%Y-%m-%d %H:%M:%S`.
435    pub fn db_datetime(v: &str) -> Result<Data, Box<Error>> {
436        let fmt = "%Y-%m-%d %H:%M:%S";
437        let r = NaiveDateTime::parse_from_str(v, fmt)?;
438        Ok(Data::DbDateTime(r))
439    }
440
441    fn add_name(&mut self, name: String, row_id: RowId) {
442        let row_ids = self.by_name.entry(name).or_insert_with(HashSet::new);
443        row_ids.insert(row_id);
444    }
445
446    fn add_value(&mut self, value: Entry, row_id: RowId) {
447        let row_ids = self.by_value.entry(value).or_insert_with(HashSet::new);
448        row_ids.insert(row_id);
449    }
450
451    /// Add a new row with one i32
452    pub fn add_i32(&mut self, name: &str, value: i32) -> RowId {
453        self.add_row(vec![Entry::new_i32(name, value)])
454    }
455
456    /// Add a new row with one string
457    pub fn add_string(&mut self, name: &str, value: &str) -> RowId {
458        self.add_row(vec![Entry::new_string(name, value)])
459    }
460
461    /// Add a new row with multiple entries.
462    pub fn add_row(&mut self, entries: Vec<Entry>) -> RowId {
463        let row_id = self.next();
464        for entry in &entries {
465            self.add_name(entry.name.clone(), row_id);
466            self.add_value(entry.clone(), row_id);
467        }
468        self.by_row_id.insert(row_id, entries);
469        row_id
470    }
471
472    /// Add a single entry to an existing row. An existing entry with the same name is overwritten.
473    /// If multiple entries with the same name exist, they will be overwritten.
474    pub fn add_or_update_entry(&mut self, row_id: RowId, new_entry: Entry) {
475        self.remove_by_name(row_id, &new_entry.name);
476        self.add_row_id_entry(row_id, new_entry);
477    }
478
479    /// Removes all entries with name 'name' and row 'row_id'. Does not delete the whole row and
480    /// leaves entries with other names.
481    pub fn remove_by_name(&mut self, row_id: RowId, name: &str) {
482        if let Some(entries) = self.by_row_id.get(&row_id) {
483            for entry in entries.iter() {
484                if let Some(row_ids) = self.by_name.get_mut(&entry.name) {
485                    row_ids.remove(&row_id);
486                }
487                if let Some(row_ids) = self.by_value.get_mut(&entry) {
488                    row_ids.remove(&row_id);
489                }
490            }
491        }
492
493        if let Some(entries) = self.by_row_id.get_mut(&row_id) {
494            entries.retain(|entry| entry.name != name);
495        }
496
497        if let Some(entries) = self.by_row_id.get(&row_id) {
498            for entry in entries.iter() {
499                if let Some(row_ids) = self.by_name.get_mut(&entry.name) {
500                    row_ids.insert(row_id);
501                }
502                if let Some(row_ids) = self.by_value.get_mut(&entry) {
503                    row_ids.insert(row_id);
504                }
505            }
506        }
507    }
508
509    /// Removes all entries with row 'row_id'
510    pub fn remove_by_row_id(&mut self, row_id: RowId) {
511        if let Some(entries) = self.by_row_id.get(&row_id) {
512            for entry in entries.iter() {
513                if let Some(row_ids) = self.by_name.get_mut(&entry.name) {
514                    row_ids.remove(&row_id);
515                }
516                if let Some(row_ids) = self.by_value.get_mut(&entry) {
517                    row_ids.remove(&row_id);
518                }
519            }
520        }
521
522        self.by_row_id.remove(&row_id);
523    }
524
525    /// Add a single entry to an existing row. Does not check if entry exists.
526    pub fn add_row_id_entry(&mut self, row_id: RowId, entry: Entry) {
527        self.by_row_id
528            .entry(row_id)
529            .or_insert_with(Vec::new)
530            .push(entry.clone());
531        self.by_name
532            .entry(entry.name.clone())
533            .or_insert_with(HashSet::new)
534            .insert(row_id);
535        self.by_value
536            .entry(entry)
537            .or_insert_with(HashSet::new)
538            .insert(row_id);
539    }
540
541    /// Delete rows in the database
542    ///
543    /// # Examples
544    ///
545    /// ```
546    /// use vdb::{Db, Entry};
547    /// let mut db = Db::new("test-db");
548    /// let row_1 = db.add_row(vec![
549    ///         Entry::new_string("word", "cocina"),
550    ///         Entry::new_string("translation", "cuisine"),
551    ///         Entry::new_string("translation", "kitchen"),
552    /// ]);
553    /// let row_2 = db.add_row(vec![
554    ///         Entry::new_string("word", "coche"),
555    ///         Entry::new_string("translation", "car"),
556    /// ]);
557    /// let coche = db.find_first_row_id_by_value("word", &Db::db_string("coche"));
558    /// assert_eq!(coche, Some(row_2));
559    /// db.delete_rows(&[row_1, row_2]);
560    /// let no_coche = db.find_first_row_id_by_value("word", &Db::db_string("coche"));
561    /// assert_eq!(no_coche, None);
562    /// ```
563    pub fn delete_rows(&mut self, row_ids: &[RowId]) {
564        for row_id in row_ids {
565            self.remove_by_row_id(*row_id);
566        }
567    }
568
569    /// Delete all entries with this name in the whole database.
570    /// Does not delete all rows. Deletes matching entries in the row. The row will be kept if
571    /// there are entries left, otherwise deleted.
572    pub fn delete_entry_all(&mut self, name: &str) {
573        let row_ids = self.find_row_ids_by_name(name);
574        for row_id in row_ids {
575            self.remove_by_name(row_id, name);
576        }
577    }
578
579    /// Return row_ids of entries where an entry with name "name" exists.
580    pub fn find_row_ids_by_name(&self, name: &str) -> Vec<RowId> {
581        if let Some(rows) = self.by_name.get(name) {
582            rows.iter().cloned().collect::<Vec<RowId>>()
583        } else {
584            vec![]
585        }
586    }
587
588    /// Return row_ids of entries that are exactly "value". For partial string matches, use
589    /// Predicates.
590    pub fn find_row_ids_by_value(&self, name: &str, value: &Data) -> Vec<RowId> {
591        let entry = Entry {
592            name: name.to_string(),
593            value: value.clone(),
594        };
595        if let Some(rows) = self.by_value.get(&entry) {
596            rows.iter().cloned().collect::<Vec<RowId>>()
597        } else {
598            vec![]
599        }
600    }
601
602    /// Return reference to first entry found in a given row.
603    pub fn find_first_row_id_by_name(&self, name: &str) -> Option<RowId> {
604        if let Some(rows) = self.by_name.get(name) {
605            rows.iter().cloned().next()
606        } else {
607            None
608        }
609    }
610
611    /// Return reference to first entry found in a given row.
612    pub fn find_first_row_id_by_value(&self, name: &str, value: &Data) -> Option<RowId> {
613        let entry = Entry {
614            name: name.to_string(),
615            value: value.clone(),
616        };
617        if let Some(rows) = self.by_value.get(&entry) {
618            rows.iter().cloned().next()
619        } else {
620            None
621        }
622    }
623
624    /// Return reference to first entry found in a given row.
625    pub fn find_first_entry_by_name(&self, row_id: RowId, name: &str) -> Option<Entry> {
626        Entry::get_first_by_name(&self.by_row_id[&row_id], name)
627    }
628
629    pub fn find_by_predicate(&self, predicate: &Predicate) -> Vec<RowId> {
630        if predicate.predicate_type == PredicateType::Equal {
631            if let Some(row_ids) = self.by_value.get(&predicate.entry) {
632                row_ids.iter().cloned().collect::<Vec<RowId>>()
633            } else {
634                vec![]
635            }
636        } else {
637            self.by_row_id
638                .iter()
639                .filter(|(_row_id, entries)| Entry::compare_all(entries, predicate))
640                .map(|(row_id, _entries)| *row_id)
641                .collect::<Vec<RowId>>()
642        }
643    }
644
645    /// Returns all rows if no predicates are given.
646    /// The first predicate is evaluated first and should have high selectivity, i. e. evaluate to a
647    /// small number of rows, to improve execution time. The number of results can be limited with
648    /// `Some(max_results)`
649    ///
650    /// # Examples
651    ///
652    /// ```
653    /// // Like SQL "select name, value from testdb where name='coche' limit 15"
654    /// use vdb::{Data, Db, Entry, Predicate, RowId};
655    /// let mut db = Db::new("test-db");
656    /// let _id = db.add_row(vec![
657    ///     Entry {
658    ///         name: String::from("set"),
659    ///         value: Db::db_string("es-en"),
660    ///     },
661    ///     Entry {
662    ///         name: String::from("name"),
663    ///         value: Db::db_string("coche"),
664    ///     },
665    ///     Entry {
666    ///         name: String::from("value"),
667    ///         value: Db::db_string("car"),
668    ///     },
669    /// ]);
670    /// let predicates = vec![Predicate::new_equal_string("name", "coche")];
671    /// let row_ids = db.find_row_ids_by_predicate(&predicates, Some(15));
672    /// assert_eq!(row_ids, [RowId(1)]);
673    /// assert_eq!(db.entries_from_row_ids(&row_ids, &["name", "value"])[0][0], Entry::new_string("name", "coche"));
674    /// ```
675    /// See also find_entries_by_predicate()
676    pub fn find_row_ids_by_predicate(
677        &self,
678        predicates: &[Predicate],
679        max_results: Option<usize>,
680    ) -> Vec<RowId> {
681        let max_results = if let Some(max_results) = max_results {
682            max_results
683        } else {
684            self.by_row_id.len()
685        };
686
687        if predicates.is_empty() {
688            self.find_all_row_ids()
689        } else {
690            let predicate0 = &predicates[0];
691            let mut row_ids = self.find_by_predicate(predicate0);
692
693            for predicate in &predicates[1..] {
694                let new_row_ids = row_ids
695                    .iter()
696                    .filter(|&row_id| self.match_row(*row_id, predicate))
697                    .cloned()
698                    .collect::<Vec<RowId>>();
699                row_ids = new_row_ids;
700            }
701            if max_results < row_ids.len() {
702                let _ = row_ids.drain(max_results..).collect::<Vec<RowId>>();
703            }
704            row_ids.sort();
705            row_ids.dedup();
706            row_ids
707        }
708    }
709
710    /// Returns all rows in the database
711    pub fn find_all_row_ids(&self) -> Vec<RowId> {
712        self.by_row_id.keys().cloned().collect::<Vec<RowId>>()
713    }
714
715    #[cfg(test)]
716    pub fn find_entries_by_predicate(
717        &self,
718        predicates: &[Predicate],
719        entries: &[&str],
720    ) -> Vec<Vec<Entry>> {
721        let row_ids = self.find_row_ids_by_predicate(predicates, None);
722        self.entries_from_row_ids(&row_ids, entries)
723    }
724
725    /// Returns entries for given row_ids.
726    pub fn entries_from_row_ids(&self, row_ids: &[RowId], names: &[&str]) -> Vec<Vec<Entry>> {
727        let names = names.iter().map(|s| s.to_string()).collect::<Vec<String>>();
728        let mut result: Vec<Vec<Entry>> = vec![];
729        for row_id in row_ids {
730            let entries = &self.by_row_id[&row_id];
731
732            let mut ordered: Vec<Entry> = vec![];
733            for name in &names {
734                for entry in entries.iter().filter(|entry| &entry.name == name) {
735                    ordered.push(entry.clone());
736                }
737            }
738
739            result.push(ordered);
740        }
741        result
742    }
743
744    /// Check if a predicate is true for a given row_id.
745    fn match_row(&self, row_id: RowId, predicate: &Predicate) -> bool {
746        let entries = &self.by_row_id[&row_id];
747        Entry::compare_all(&entries, predicate)
748    }
749
750    fn next(&mut self) -> RowId {
751        self.row_max.0 += 1;
752        self.row_max
753    }
754
755    fn build_filename(name: &str) -> String {
756        format!("save/{}", name)
757    }
758
759    #[cfg(test)]
760    pub fn debug_rows(&self, row_ids: &[RowId]) -> Vec<Vec<Entry>> {
761        let mut result: Vec<Vec<Entry>> = vec![];
762        for row_id in row_ids {
763            let entries = &self.by_row_id[&row_id];
764            result.push(entries.clone());
765        }
766        result
767    }
768}
769
770mod tests {
771    #[cfg(test)]
772    use super::{Data, Db, Entry, Predicate, RowId};
773    #[cfg(test)]
774    use chrono::NaiveDateTime;
775
776    #[test]
777    fn match_row() {
778        let db = new_db_with_entries("testdb");
779
780        let p1 = Predicate::new_equal_string("name", "coche");
781        let p2 = Predicate::new_starts_with("name", "co");
782        let p3 = Predicate::new_contains("name", "och");
783
784        println!("{:?}", db.debug_rows(&vec![RowId(2)]));
785        println!("{:?}", db.debug_rows(&vec![RowId(1)]));
786
787        assert_eq!(db.match_row(RowId(2), &p1), true);
788        assert_eq!(db.match_row(RowId(2), &p2), true);
789        assert_eq!(db.match_row(RowId(2), &p3), true);
790
791        assert_eq!(db.match_row(RowId(1), &p1), false);
792        assert_eq!(db.match_row(RowId(1), &p2), false);
793        assert_eq!(db.match_row(RowId(1), &p3), false);
794    }
795
796    #[test]
797    fn starts_with_contains() {
798        let s1 = Db::db_string("hello");
799        let s2 = Db::db_string("hello world");
800        let s3 = Db::db_string("o wor");
801        assert!(s2.starts_with(&s1));
802        assert_eq!(s1.starts_with(&s2), false);
803        assert!(s2.contains(&s3));
804        assert_eq!(s3.contains(&s2), false);
805    }
806
807    #[test]
808    fn compare() {
809        let p1 = Predicate::new_equal_string("set", "es-en");
810        let p2 = Predicate::new_starts_with("set", "es");
811        let p3 = Predicate::new_contains("set", "s-e");
812
813        let e1 = Entry {
814            name: String::from("set"),
815            value: Db::db_string("es-en"),
816        };
817
818        let e2 = Entry {
819            name: String::from("set"),
820            value: Db::db_string("en-es"),
821        };
822
823        assert!(e1.compare(&p1));
824        assert_eq!(e2.compare(&p1), false);
825
826        assert!(e1.compare(&p2));
827        assert_eq!(e2.compare(&p2), false);
828
829        assert!(e1.compare(&p3));
830        assert_eq!(e2.compare(&p3), false);
831    }
832
833    #[cfg(test)]
834    fn new_db_with_entries(name: &str) -> Db {
835        let mut db = Db::new(name);
836        let _id = db.add_row(vec![
837            Entry {
838                name: String::from("set"),
839                value: Db::db_string("es-en"),
840            },
841            Entry {
842                name: String::from("name"),
843                value: Db::db_string("disfrutar"),
844            },
845            Entry {
846                name: String::from("value"),
847                value: Db::db_string("to enjoy"),
848            },
849        ]);
850        let _id = db.add_row(vec![
851            Entry {
852                name: String::from("set"),
853                value: Db::db_string("es-en"),
854            },
855            Entry {
856                name: String::from("name"),
857                value: Db::db_string("coche"),
858            },
859            Entry {
860                name: String::from("value"),
861                value: Db::db_string("car"),
862            },
863        ]);
864        db
865    }
866
867    #[cfg(test)]
868    fn check_single_entries(db: &Db) {
869        assert_eq!(db.by_row_id.len(), 2);
870
871        for value in &db.by_value {
872            println!("{:?}", value);
873        }
874        let row_ids = db.find_row_ids_by_value("set", &Db::db_string("es-en"));
875        assert_eq!(row_ids.len(), 2);
876
877        let row_ids = db.find_row_ids_by_value("value", &Db::db_string("car"));
878        println!("find_row_ids_by_value(): {:?}", row_ids);
879        assert_eq!(row_ids.len(), 1);
880    }
881
882    #[test]
883    fn find_row_ids_by_predicate() {
884        let name = "testdb";
885        let db = new_db_with_entries(name);
886
887        let predicates1 = vec![Predicate::new_equal_string("set", "es-en")];
888        let predicates2 = vec![
889            Predicate::new_equal_string("set", "es-en"),
890            Predicate::new_equal_string("name", "disfrutar"),
891        ];
892
893        let row_ids = db.find_row_ids_by_predicate(&predicates1, None);
894        assert_eq!(row_ids, vec![RowId(1), RowId(2)]);
895
896        let row_ids = db.find_row_ids_by_predicate(&predicates2, None);
897        assert_eq!(row_ids, vec![RowId(1)]);
898    }
899
900    #[test]
901    fn find_entries_by_predicate() {
902        let name = "testdb";
903        let db = new_db_with_entries(name);
904
905        let predicates = vec![Predicate::new_equal_string("set", "es-en")];
906
907        let result1 = vec![
908            vec![Entry {
909                name: String::from("name"),
910                value: Db::db_string("disfrutar"),
911            }],
912            vec![Entry {
913                name: String::from("name"),
914                value: Db::db_string("coche"),
915            }],
916        ];
917
918        let result2 = vec![
919            vec![
920                Entry {
921                    name: String::from("name"),
922                    value: Db::db_string("disfrutar"),
923                },
924                Entry {
925                    name: String::from("value"),
926                    value: Db::db_string("to enjoy"),
927                },
928            ],
929            vec![
930                Entry {
931                    name: String::from("name"),
932                    value: Db::db_string("coche"),
933                },
934                Entry {
935                    name: String::from("value"),
936                    value: Db::db_string("car"),
937                },
938            ],
939        ];
940
941        let result = db.find_entries_by_predicate(&predicates, &vec!["name"]);
942        assert_eq!(result, result1);
943
944        let result = db.find_entries_by_predicate(&predicates, &vec!["name", "value"]);
945        assert_eq!(result, result2);
946    }
947
948    #[test]
949    fn load_and_save() {
950        let name = "testdb";
951        let mut db = new_db_with_entries(name);
952        db.save().unwrap();
953        let db = Db::load(name).unwrap();
954        check_single_entries(&db);
955    }
956
957    #[test]
958    fn add_row() {
959        let db = new_db_with_entries("testdb");
960        check_single_entries(&db);
961    }
962
963    #[test]
964    fn data_types() {
965        let t = "Test";
966        assert_eq!(Data::DbString(String::from(t)), Db::db_string(t));
967        let t = 42;
968        assert_eq!(Data::DbI32(t), Db::db_i32(t));
969        let fmt = "%Y-%m-%d %H:%M:%S";
970        let t = "2013-11-22 12:00:00";
971        let dt = NaiveDateTime::parse_from_str(t, fmt).unwrap();
972        assert_eq!(Data::DbDateTime(dt), Db::db_datetime(t).unwrap());
973    }
974
975    #[test]
976    fn add_or_update_entry_add() {
977        let mut db = new_db_with_entries("testdb");
978
979        println!("Before update/add: {:?}", db.debug_rows(&vec![RowId(2)]));
980        db.add_or_update_entry(
981            RowId(2),
982            Entry {
983                name: String::from("new entry"),
984                value: Db::db_string("new entry content"),
985            },
986        );
987        println!("After update/add: {:?}", db.debug_rows(&vec![RowId(2)]));
988
989        for (i, row) in db.by_row_id.iter().enumerate() {
990            println!("row: {} {:?}", i, row);
991        }
992        assert_eq!(db.find_first_row_id_by_name("new entry"), Some(RowId(2)));
993        assert_eq!(
994            db.find_first_row_id_by_value("new entry", &Db::db_string("new entry content")),
995            Some(RowId(2))
996        );
997    }
998
999    #[test]
1000    fn add_or_update_entry_update() {
1001        let mut db = new_db_with_entries("testdb");
1002
1003        println!("{:?}", db.debug_rows(&vec![RowId(2)]));
1004        db.add_or_update_entry(
1005            RowId(2),
1006            Entry {
1007                name: String::from("new entry"),
1008                value: Db::db_string("new entry content"),
1009            },
1010        );
1011        db.add_or_update_entry(
1012            RowId(2),
1013            Entry {
1014                name: String::from("new entry"),
1015                value: Db::db_string("new entry content updated"),
1016            },
1017        );
1018        println!("{:?}", db.debug_rows(&vec![RowId(2)]));
1019        for (row_id, entries) in db.by_row_id.iter() {
1020            println!("{:?}", row_id);
1021            for entry in entries {
1022                println!("    {:?}", entry);
1023            }
1024        }
1025        assert_eq!(
1026            db.find_first_row_id_by_value("new entry", &Db::db_string("new entry content updated")),
1027            Some(RowId(2))
1028        );
1029    }
1030
1031    #[test]
1032    fn delete_entry_all() {
1033        let mut db = new_db_with_entries("testdb");
1034
1035        let mut add_row = |n| {
1036            if let Some(_entry) = db.by_row_id.get(&RowId(n)) {
1037                println!("{:?}", db.debug_rows(&vec![RowId(n)]));
1038            }
1039            db.add_row_id_entry(
1040                RowId(n),
1041                Entry {
1042                    name: String::from("new entry"),
1043                    value: Db::db_string(&format!("new col for row {}", n)),
1044                },
1045            );
1046        };
1047        add_row(1);
1048        add_row(2);
1049        add_row(3);
1050        for (row_id, entries) in db.by_row_id.iter() {
1051            println!("{:?}", row_id);
1052            for entry in entries {
1053                println!("    {:?}", entry);
1054            }
1055        }
1056
1057        let row_ids = db.find_row_ids_by_name("new entry");
1058        assert_eq!(row_ids.len(), 3);
1059        assert!(row_ids.contains(&RowId(1)));
1060        assert!(row_ids.contains(&RowId(2)));
1061        assert!(row_ids.contains(&RowId(3)));
1062
1063        println!("Deleting entries...");
1064        db.delete_entry_all("new entry");
1065
1066        for (row_id, entries) in db.by_row_id.iter() {
1067            println!("{:?}", row_id);
1068            for entry in entries {
1069                println!("    {:?}", entry);
1070            }
1071        }
1072
1073        let row_ids = db.find_row_ids_by_name("new entry");
1074        assert_eq!(row_ids.len(), 0);
1075
1076        let row_ids = db.find_row_ids_by_name("set");
1077        assert_eq!(row_ids.len(), 2);
1078        assert!(row_ids.contains(&RowId(1)));
1079        assert!(row_ids.contains(&RowId(2)));
1080        assert!(!row_ids.contains(&RowId(3)));
1081
1082        let row_ids = db.find_row_ids_by_name("name");
1083        assert_eq!(row_ids.len(), 2);
1084        assert!(row_ids.contains(&RowId(1)));
1085        assert!(row_ids.contains(&RowId(2)));
1086        assert!(!row_ids.contains(&RowId(3)));
1087    }
1088
1089    #[test]
1090    fn find_all_row_ids() {
1091        let db = new_db_with_entries("testdb");
1092        let row_ids = db.find_all_row_ids();
1093        assert_eq!(row_ids.len(), 2);
1094        assert!(row_ids.contains(&RowId(1)));
1095        assert!(row_ids.contains(&RowId(2)));
1096    }
1097
1098    #[test]
1099    fn find_row_ids_by_value() {
1100        let db = new_db_with_entries("testdb");
1101        let entry = Entry::new_string("name", "coche");
1102        let row_id = db.by_value[&entry].iter().next().unwrap();
1103        let entry_new = db.by_row_id[row_id]
1104            .iter()
1105            .filter(|entry| &entry.name == "name")
1106            .next()
1107            .unwrap();
1108        assert_eq!(&entry, entry_new);
1109    }
1110}