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*/