kpdb/types/
group.rs

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