kpdb/types/
group.rs

1// Copyright (c) 2016-2017 Martijn Rijkeboer <mrr@sru-systems.com>
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9use super::custom_icon_uuid::CustomIconUuid;
10use super::entry::Entry;
11use super::entry_uuid::EntryUuid;
12use super::group_uuid::GroupUuid;
13use super::icon::Icon;
14use super::times::Times;
15use chrono::{DateTime, Utc};
16use std::collections::vec_deque::VecDeque;
17use std::ptr;
18
19/// A group in the database.
20#[derive(Clone, Debug, PartialEq)]
21pub struct Group {
22    /// The date and time this group was created.
23    pub creation_time: DateTime<Utc>,
24
25    /// The identifier of this group's custom icon if any.
26    pub custom_icon_uuid: Option<CustomIconUuid>,
27
28    /// Default auto-type sequence.
29    pub def_auto_type_sequence: String,
30
31    /// Whether auto-type is enabled.
32    pub enable_auto_type: Option<bool>,
33
34    /// Whether searching is enabled.
35    pub enable_searching: Option<bool>,
36
37    /// Vector with entries that belong to this group.
38    pub entries: Vec<Entry>,
39
40    /// Whether this group expires.
41    pub expires: bool,
42
43    /// The date and time this group will expire if expires is true.
44    pub expiry_time: DateTime<Utc>,
45
46    /// Vector with subgroups of this group.
47    pub groups: Vec<Group>,
48
49    /// This group's icon.
50    pub icon: Icon,
51
52    /// Whether this group is expanded.
53    pub is_expanded: bool,
54
55    /// The date and time this group was last accessed.
56    pub last_accessed: DateTime<Utc>,
57
58    /// The date and time this group was last modified.
59    pub last_modified: DateTime<Utc>,
60
61    /// The identifier of the last top visible entry.
62    pub last_top_visible_entry: EntryUuid,
63
64    /// The date and time the location of this group was changed.
65    pub location_changed: DateTime<Utc>,
66
67    /// The name of this group.
68    pub name: String,
69
70    /// The notes of this group.
71    pub notes: String,
72
73    /// The usage count of this group.
74    pub usage_count: i32,
75
76    /// The identifier of this group.
77    pub uuid: GroupUuid,
78
79    /// The parent groups GroupUUID.
80    pub parent: GroupUuid,
81}
82
83impl Group {
84    /// Create a new group.
85    ///
86    /// # Examples
87    ///
88    /// ```rust
89    /// use kpdb::Group;
90    ///
91    /// let group = Group::new("Websites");
92    /// ```
93    pub fn new<S: Into<String>>(name: S) -> Group {
94        let mut group = Group::default();
95        group.name = name.into();
96        group.uuid = GroupUuid::new_random();
97        group
98    }
99
100    /// Add an entry to the current group.
101    ///
102    /// # Examples
103    ///
104    /// ```rust
105    /// use kpdb::{Entry, Group};
106    ///
107    /// let mut group = Group::new("group");
108    /// let entry = Entry::new();
109    ///
110    /// assert_eq!(group.entries.len(), 0);
111    /// group.add_entry(entry.clone());
112    /// assert_eq!(group.entries.len(), 1);
113    /// assert_eq!(group.entries[0], entry);
114    /// ```
115    pub fn add_entry(&mut self, entry: Entry) {
116        self.entries.push(entry);
117    }
118
119    /// Add a sub group to the current group.
120    ///
121    /// # Examples
122    ///
123    /// ```rust
124    /// use kpdb::Group;
125    ///
126    /// let mut root = Group::new("root");
127    /// let child = Group::new("child");
128    ///
129    /// assert_eq!(root.groups.len(), 0);
130    /// root.add_group(child.clone());
131    /// assert_eq!(root.groups.len(), 1);
132    /// assert_eq!(root.groups[0], child);
133    /// ```
134    pub fn add_group(&mut self, group: Group) {
135        self.groups.push(group);
136    }
137
138    /// Returns an iterator over the group and sub groups.
139    ///
140    /// # Examples
141    ///
142    /// ```rust
143    /// use kpdb::Group;
144    ///
145    /// let mut root = Group::new("root");
146    /// let sub_1 = Group::new("sub_1");
147    /// let sub_2 = Group::new("sub_2");
148    /// root.add_group(sub_1.clone());
149    /// root.add_group(sub_2.clone());
150    ///
151    /// let mut iterator = root.iter();
152    /// assert_eq!(iterator.next(), Some(&root));
153    /// assert_eq!(iterator.next(), Some(&sub_1));
154    /// assert_eq!(iterator.next(), Some(&sub_2));
155    /// assert_eq!(iterator.next(), None);
156    /// ```
157    pub fn iter(&self) -> Iter {
158        Iter::new(self)
159    }
160
161    /// Returns an iterator that allows modifying each group.
162    ///
163    /// # Examples
164    ///
165    /// ```rust
166    /// use kpdb::Group;
167    ///
168    /// let mut root = Group::new("root");
169    /// for group in root.iter_mut() {
170    ///     group.name = String::from("name");
171    /// }
172    /// assert_eq!(root.name, "name");
173    /// ```
174    pub fn iter_mut(&mut self) -> IterMut {
175        IterMut::new(self)
176    }
177
178    /// Remove an entry from the current group.
179    ///
180    /// # Examples
181    ///
182    /// ```rust
183    /// use kpdb::{Entry, Group};
184    ///
185    /// let mut group = Group::new("Sample");
186    /// let entry = Entry::new();
187    /// group.add_entry(entry.clone());
188    /// assert_eq!(group.entries.len(), 1);
189    /// assert_eq!(group.remove_entry(entry.uuid), Some(entry));
190    /// assert_eq!(group.entries.len(), 0);
191    /// ```
192    pub fn remove_entry(&mut self, entry_uuid: EntryUuid) -> Option<Entry> {
193        match self.entries.iter().position(|x| x.uuid == entry_uuid) {
194            Some(x) => Some(self.entries.remove(x)),
195            None => None,
196        }
197    }
198
199    /// Remove a sub group from the current group.
200    ///
201    /// # Examples
202    ///
203    /// ```rust
204    /// use kpdb::Group;
205    ///
206    /// let mut parent = Group::new("Parent");
207    /// let child = Group::new("Child");
208    /// parent.add_group(child.clone());
209    /// assert_eq!(parent.groups.len(), 1);
210    /// assert_eq!(parent.remove_group(child.uuid), Some(child));
211    /// assert_eq!(parent.groups.len(), 0);
212    /// ```
213    pub fn remove_group(&mut self, group_uuid: GroupUuid) -> Option<Group> {
214        match self.groups.iter().position(|x| x.uuid == group_uuid) {
215            Some(x) => Some(self.groups.remove(x)),
216            None => None,
217        }
218    }
219}
220
221impl Default for Group {
222    fn default() -> Group {
223        let now = Utc::now();
224        Group {
225            creation_time: now,
226            custom_icon_uuid: None,
227            def_auto_type_sequence: String::new(),
228            enable_auto_type: None,
229            enable_searching: None,
230            entries: Vec::new(),
231            expires: false,
232            expiry_time: now,
233            groups: Vec::new(),
234            icon: Icon::Folder,
235            is_expanded: true,
236            last_accessed: now,
237            last_modified: now,
238            last_top_visible_entry: EntryUuid::nil(),
239            location_changed: now,
240            name: String::new(),
241            notes: String::new(),
242            usage_count: 0,
243            uuid: GroupUuid::nil(),
244            parent: GroupUuid::nil(),
245        }
246    }
247}
248
249impl Times for Group {
250    fn creation_time(&self) -> DateTime<Utc> {
251        self.creation_time
252    }
253
254    fn expires(&self) -> bool {
255        self.expires
256    }
257
258    fn expiry_time(&self) -> DateTime<Utc> {
259        self.expiry_time
260    }
261
262    fn last_accessed(&self) -> DateTime<Utc> {
263        self.last_accessed
264    }
265
266    fn last_modified(&self) -> DateTime<Utc> {
267        self.last_modified
268    }
269
270    fn location_changed(&self) -> DateTime<Utc> {
271        self.location_changed
272    }
273
274    fn usage_count(&self) -> i32 {
275        self.usage_count
276    }
277
278    fn set_creation_time(&mut self, val: DateTime<Utc>) {
279        self.creation_time = val;
280    }
281
282    fn set_expires(&mut self, val: bool) {
283        self.expires = val;
284    }
285
286    fn set_expiry_time(&mut self, val: DateTime<Utc>) {
287        self.expiry_time = val;
288    }
289
290    fn set_last_accessed(&mut self, val: DateTime<Utc>) {
291        self.last_accessed = val;
292    }
293
294    fn set_last_modified(&mut self, val: DateTime<Utc>) {
295        self.last_modified = val;
296    }
297
298    fn set_location_changed(&mut self, val: DateTime<Utc>) {
299        self.location_changed = val;
300    }
301
302    fn set_usage_count(&mut self, val: i32) {
303        self.usage_count = val;
304    }
305}
306
307/// Immutable group iterator.
308pub struct Iter<'a> {
309    curr: Option<&'a Group>,
310    todo: VecDeque<&'a Group>,
311}
312
313impl<'a> Iter<'a> {
314    fn new(group: &'a Group) -> Iter<'a> {
315        let mut queue = VecDeque::new();
316        queue.push_back(group);
317        Iter {
318            curr: None,
319            todo: queue,
320        }
321    }
322}
323
324impl<'a> Iterator for Iter<'a> {
325    type Item = &'a Group;
326
327    fn next(&mut self) -> Option<&'a Group> {
328        match self.curr.take() {
329            Some(group) => {
330                for sub in group.groups.iter() {
331                    self.todo.push_back(sub);
332                }
333            }
334            None => {}
335        }
336        self.curr = self.todo.pop_front();
337        self.curr
338    }
339}
340
341/// Mutable group iterator.
342pub struct IterMut<'a> {
343    curr: Option<&'a mut Group>,
344    todo: VecDeque<&'a mut Group>,
345}
346
347impl<'a> IterMut<'a> {
348    fn new(group: &'a mut Group) -> IterMut<'a> {
349        let mut queue = VecDeque::new();
350        queue.push_back(group);
351        IterMut {
352            curr: None,
353            todo: queue,
354        }
355    }
356}
357
358impl<'a> Iterator for IterMut<'a> {
359    type Item = &'a mut Group;
360
361    fn next(&mut self) -> Option<&'a mut Group> {
362        match self.curr.take() {
363            Some(group) => {
364                for sub in group.groups.iter_mut() {
365                    self.todo.push_back(sub);
366                }
367            }
368            None => {}
369        }
370        let curr = self.todo.pop_front();
371        self.curr = unsafe { ptr::read(&curr) };
372        curr
373    }
374}
375
376#[cfg(test)]
377mod tests {
378
379    use super::*;
380    use crate::types::EntryUuid;
381    use crate::types::GroupUuid;
382    use crate::types::Icon;
383    use crate::utils::test::approx_equal_datetime;
384    use chrono::Utc;
385
386    #[test]
387    fn test_new_returns_correct_instance() {
388        let now = Utc::now();
389        let name = "Root";
390        let group = Group::new(name.clone());
391        assert!(approx_equal_datetime(group.creation_time, now));
392        assert_eq!(group.custom_icon_uuid, None);
393        assert_eq!(group.def_auto_type_sequence, "");
394        assert_eq!(group.enable_auto_type, None);
395        assert_eq!(group.enable_searching, None);
396        assert_eq!(group.entries, Vec::new());
397        assert_eq!(group.expires, false);
398        assert!(approx_equal_datetime(group.expiry_time, now));
399        assert_eq!(group.groups, Vec::new());
400        assert_eq!(group.icon, Icon::Folder);
401        assert_eq!(group.is_expanded, true);
402        assert!(approx_equal_datetime(group.last_accessed, now));
403        assert!(approx_equal_datetime(group.last_modified, now));
404        assert_eq!(group.last_top_visible_entry, EntryUuid::nil());
405        assert!(approx_equal_datetime(group.location_changed, now));
406        assert_eq!(group.name, name);
407        assert_eq!(group.notes, "");
408        assert_eq!(group.usage_count, 0);
409        assert!(group.uuid != GroupUuid::nil());
410    }
411
412    #[test]
413    fn test_add_entry_adds_entry() {
414        let mut group = Group::new("group");
415        let entry = Entry::new();
416
417        assert_eq!(group.entries.len(), 0);
418        group.add_entry(entry.clone());
419        assert_eq!(group.entries.len(), 1);
420        assert_eq!(group.entries[0], entry);
421    }
422
423    #[test]
424    fn test_add_group_adds_group() {
425        let mut root = Group::new("root");
426        let child = Group::new("child");
427
428        assert_eq!(root.groups.len(), 0);
429        root.add_group(child.clone());
430        assert_eq!(root.groups.len(), 1);
431        assert_eq!(root.groups[0], child);
432    }
433
434    #[test]
435    fn test_iter_returns_correct_iterator() {
436        let mut root = Group::new("root");
437        let mut sub_1 = Group::new("sub_1");
438        let mut sub_2 = Group::new("sub_2");
439        let sub_1_1 = Group::new("sub_1_1");
440        let sub_1_2 = Group::new("sub_1_2");
441        let sub_2_1 = Group::new("sub_2_1");
442        let sub_2_2 = Group::new("sub_2_2");
443        sub_1.add_group(sub_1_1.clone());
444        sub_1.add_group(sub_1_2.clone());
445        sub_2.add_group(sub_2_1.clone());
446        sub_2.add_group(sub_2_2.clone());
447        root.add_group(sub_1.clone());
448        root.add_group(sub_2.clone());
449
450        let mut iterator = root.iter();
451        assert_eq!(iterator.next(), Some(&root));
452        assert_eq!(iterator.next(), Some(&sub_1));
453        assert_eq!(iterator.next(), Some(&sub_2));
454        assert_eq!(iterator.next(), Some(&sub_1_1));
455        assert_eq!(iterator.next(), Some(&sub_1_2));
456        assert_eq!(iterator.next(), Some(&sub_2_1));
457        assert_eq!(iterator.next(), Some(&sub_2_2));
458        assert_eq!(iterator.next(), None);
459    }
460
461    #[test]
462    fn test_iter_mut_returns_correct_iterator() {
463        let mut root = Group::new("root");
464        let sub_1 = Group::new("sub_1");
465        let sub_2 = Group::new("sub_2");
466        root.add_group(sub_1);
467        root.add_group(sub_2);
468
469        let mut num = 0;
470        for group in root.iter_mut() {
471            group.name = num.to_string();
472            num += 1;
473        }
474
475        let mut num = 0;
476        for group in root.iter() {
477            assert_eq!(group.name, num.to_string());
478            num += 1;
479        }
480    }
481
482    #[test]
483    fn test_remove_entry_removes_entry() {
484        let mut group = Group::new("Sample");
485        let entry = Entry::new();
486
487        assert_eq!(group.entries.len(), 0);
488        group.add_entry(entry.clone());
489        assert_eq!(group.entries.len(), 1);
490        assert_eq!(group.remove_entry(entry.uuid), Some(entry.clone()));
491        assert_eq!(group.entries.len(), 0);
492        assert_eq!(group.remove_entry(entry.uuid), None);
493    }
494
495    #[test]
496    fn test_remove_group_removes_group() {
497        let mut parent = Group::new("Parent");
498        let child = Group::new("Child");
499
500        assert_eq!(parent.groups.len(), 0);
501        parent.add_group(child.clone());
502        assert_eq!(parent.groups.len(), 1);
503        assert_eq!(parent.remove_group(child.uuid), Some(child.clone()));
504        assert_eq!(parent.groups.len(), 0);
505        assert_eq!(parent.remove_group(child.uuid), None);
506    }
507
508    #[test]
509    fn test_default_returns_correct_instance() {
510        let now = Utc::now();
511        let group = Group::default();
512        assert!(approx_equal_datetime(group.creation_time, now));
513        assert_eq!(group.custom_icon_uuid, None);
514        assert_eq!(group.def_auto_type_sequence, "");
515        assert_eq!(group.enable_auto_type, None);
516        assert_eq!(group.enable_searching, None);
517        assert_eq!(group.entries, Vec::new());
518        assert_eq!(group.expires, false);
519        assert!(approx_equal_datetime(group.expiry_time, now));
520        assert_eq!(group.groups, Vec::new());
521        assert_eq!(group.icon, Icon::Folder);
522        assert_eq!(group.is_expanded, true);
523        assert!(approx_equal_datetime(group.last_accessed, now));
524        assert!(approx_equal_datetime(group.last_modified, now));
525        assert_eq!(group.last_top_visible_entry, EntryUuid::nil());
526        assert!(approx_equal_datetime(group.location_changed, now));
527        assert_eq!(group.name, "");
528        assert_eq!(group.notes, "");
529        assert_eq!(group.usage_count, 0);
530        assert_eq!(group.uuid, GroupUuid::nil());
531    }
532}