thot_core/db/
collection.rs

1//! Defines the Collection trait for database implementations to use.
2use super::error::Error as DbError;
3use super::resources::object::StandardObject;
4use super::resources::search_filter::{
5    ResourceIdSearchFilter as RidFilter, StandardPropertiesSearchFilter as StdPropsFilter,
6};
7use crate::result::{Error, Result};
8use std::collections::{HashMap, HashSet};
9use uuid::Uuid;
10
11// *************
12// *** types ***
13// *************
14
15pub type ObjectMap<T> = HashMap<Uuid, T>;
16
17// ******************
18// *** Collection ***
19// ******************
20
21/// Collections.
22#[derive(Debug)]
23pub struct Collection<T: StandardObject> {
24    objects: ObjectMap<T>,
25}
26
27impl<T: StandardObject> Collection<T> {
28    pub fn new() -> Self {
29        Collection {
30            objects: HashMap::new(),
31        }
32    }
33
34    /// Finds all objects with `properties` matching the search criteria.
35    pub fn find(&self, search: &StdPropsFilter) -> HashSet<T> {
36        let mut matched = self.objects.clone();
37        matched.retain(|_, obj| obj.properties().matches(search));
38
39        let mut objs = HashSet::with_capacity(matched.len());
40        for obj in matched.values() {
41            objs.insert(obj.clone());
42        }
43
44        objs
45    }
46
47    /// Finds a single instance of an object with `properties` matching the search filter.
48    pub fn find_one(&self, search: &StdPropsFilter) -> Option<T> {
49        let matched = self.find(search);
50
51        // return random element
52        for obj in matched.iter() {
53            return Some(obj.clone());
54        }
55
56        // Empty
57        None
58    }
59
60    /// Inserts a single object into the database.
61    ///
62    /// # Errors
63    /// + If an object with the same resource id already exists.
64    pub fn insert_one(&mut self, obj: T) -> Result {
65        let id = obj.properties().rid.id.clone();
66        if self.objects.contains_key(&id) {
67            return Err(Error::DbError(DbError::AlreadyExists(id)));
68        }
69
70        self.objects.insert(id, obj);
71        Ok(())
72    }
73
74    /// Updates an object given its universal id.
75    /// The object's universal id is not altered from the object passed in for the update,
76    /// however other aspects of the resource id may be.
77    ///
78    /// # Returns
79    /// The old value.
80    ///
81    /// # Errors
82    /// + If an object with the given universal id is not found.
83    pub fn update(&mut self, uid: Uuid, mut obj: T) -> Result<T> {
84        if !self.objects.contains_key(&uid) {
85            return Err(Error::DbError(DbError::DoesNotExist(uid)));
86        }
87
88        obj.properties_mut().rid.id = uid.clone();
89        let old_val = self.objects.insert(uid.clone(), obj);
90        if old_val.is_none() {
91            // @unreachable
92            return Err(Error::DbError(DbError::DoesNotExist(uid)));
93        }
94
95        Ok(old_val.unwrap())
96    }
97
98    /// Updates the properties a single object.
99    ///
100    /// # Returns
101    /// Returns the orginal value.
102    ///
103    /// # Errors
104    /// + If no objects match the search.
105    /// + If more than one object matches the search.
106    pub fn update_one(&mut self, search: &RidFilter, mut obj: T) -> Result<T> {
107        // serach for match
108        let mut matched = self.objects.clone();
109        matched.retain(|_, obj| obj.properties().rid.matches(search));
110
111        if matched.is_empty() {
112            return Err(Error::DbError(DbError::NoMatches));
113        } else if matched.len() > 1 {
114            return Err(Error::DbError(DbError::MultipleMatches));
115        }
116
117        // get rid of match
118        let uid = matched.into_keys().next();
119        if uid.is_none() {
120            // @unreachable
121            return Err(Error::DbError(DbError::NoMatches));
122        }
123        let uid = uid.unwrap();
124
125        // update
126        obj.properties_mut().rid.id = uid.clone();
127        let old_val = self.objects.insert(uid.clone(), obj);
128        if old_val.is_none() {
129            // should not be reachable
130            return Err(Error::DbError(DbError::DoesNotExist(uid)));
131        }
132
133        Ok(old_val.unwrap())
134    }
135
136    /// Updates an object if one is found, otherwise inserts it as new.
137    /// If inserting a new object, the resource id is taken as is.
138    /// If updating a previous object, the resource id aremains as the original's.
139    ///
140    /// # Returns
141    /// `None` if the object was newly inserted, or `Some` with the old value if it was updated.
142    ///
143    /// # Errors
144    /// + If more than one object matches the search.
145    pub fn update_or_insert_one(&mut self, search: &RidFilter, obj: T) -> Result<Option<T>> {
146        let mut matched = self.objects.clone();
147        matched.retain(|_, obj| obj.properties().rid.matches(search));
148
149        match matched.len() {
150            0 => {
151                self.insert_one(obj)?;
152                Ok(None)
153            }
154            1 => {
155                let uid = matched.into_keys().next();
156                if uid.is_none() {
157                    // @unreachable
158                    // not sure aobut which error is best to put here.
159                    return Err(Error::DbError(DbError::NoMatches));
160                }
161
162                let uid = uid.unwrap();
163                let old_val = self.update(uid, obj)?;
164                Ok(Some(old_val))
165            }
166            _ => Err(Error::DbError(DbError::MultipleMatches)),
167        }
168    }
169
170    /// Returns the number of objects in the collection.
171    pub fn len(&self) -> usize {
172        self.objects.len()
173    }
174}
175
176#[cfg(test)]
177#[path = "./collection_test.rs"]
178mod collection_test;
179
180/* ***************************************
181* Addtional functionality not yet needed.
182* ***************************************
183
184   /// Inserts a vector of objects into the database.
185   pub fn insert_many(&mut self, objs: Vec<T>) -> Result {
186       for obj in objs{
187           if let Some(found) = self.objects.get(&obj) {
188               return Err(Error::DbError(DbError::AlreadyExists(obj.properties().rid.clone())));
189           }
190       }
191
192       for obj in objs {
193           self.objects.insert(obj);
194       }
195
196       Ok(())
197   }
198
199   /// Replaces a single object.
200   ///
201   /// # Returns
202   /// Returns the original object.
203   pub fn replace_one(&mut self, search: StdPropsFilter, obj: T) -> Option<T> {
204       for s_obj in self.objects {
205           if s_obj.properties().matches(&search) {
206               self.objects.remove(&s_obj);
207               self.objects.insert(obj);
208               return Some(s_obj);
209           }
210       }
211
212       None
213   }
214
215   /// Update all objects that match the search filter.
216   ///
217   /// # Returns
218   /// Returns a vector of the orginal values.
219   pub fn update_many(&mut self, search: StdPropsFilter, update: T) -> Vec<T> {
220
221   }
222
223   /// Deletes a single object.
224   ///
225   /// # Returns
226   /// Returns the removed object.
227   pub fn delete_one(&mut self, search: StdPropsFilter) -> Option<T> {
228       for obj in self.objects {
229           if obj.properties.matches(search) {
230
231           }
232       }
233   }
234
235   /// Deletes all object matching the search filter.
236   ///
237   /// # Returns
238   /// Returns a vector of the removed objects.
239   pub fn delete_many(&mut self, search: StdPropsFilter) -> Vec<T> {
240
241   }
242
243* End of additional functionality.
244*/