hit_data/index/
index.rs

1use crate::index::list_helpers::{
2    get_parent_index_entry, get_parent_property_value, mutate_insert_in_reference_array,
3};
4use crate::index::move_object::move_object;
5use crate::index::reference_helpers::remove_reference_from_parent_array_from_property;
6use crate::index::reference_index_helpers::{
7    index_object_references, index_reference, unindex_reference, unindex_reference_from_property,
8};
9use crate::index::remove_helpers::{get_references, remove_object_helper};
10use crate::index::subobject_helpers::insert_subobject_in_array;
11use crate::index::{IndexEntry, IndexEntryProperty, IndexEntryRef};
12use crate::object_data::Id;
13use crate::object_data::ObjectValue;
14use crate::object_data::ObjectValues;
15use crate::object_data::Reference;
16use crate::HitError;
17use std::collections::BTreeMap;
18use std::collections::{btree_map::Iter, HashMap};
19
20use super::{
21    find_references_before_deletion::find_references_recursive,
22    reference_helpers::mutate_remove_from_reference_array,
23};
24
25#[derive(Clone)]
26pub struct Index {
27    pub(in crate) index: BTreeMap<Id, IndexEntryRef>,
28    id: Id,
29}
30
31impl Index {
32    pub fn new_for_import(id: &str) -> Index {
33        Index {
34            index: BTreeMap::new(),
35            id: id.to_string(),
36        }
37    }
38
39    pub fn new(id: &str, values: ObjectValues) -> Result<Index, HitError> {
40        let mut index = Index {
41            index: BTreeMap::new(),
42            id: id.to_string(),
43        };
44        //Disallow references and subobjects
45        for (_, value) in values.iter() {
46            match value {
47                ObjectValue::Reference(_) => {
48                    return Err(HitError::CanOnlySetScalarValuesInInsertedObject())
49                }
50                ObjectValue::VecReference(_) => {
51                    return Err(HitError::CanOnlySetScalarValuesInInsertedObject())
52                }
53                ObjectValue::SubObject(_) => {
54                    return Err(HitError::CanOnlySetScalarValuesInInsertedObject())
55                }
56                ObjectValue::VecSubObjects(_) => {
57                    return Err(HitError::CanOnlySetScalarValuesInInsertedObject())
58                }
59                _ => {}
60            }
61        }
62        index.insert_raw(id, values, None)?;
63        Ok(index)
64    }
65
66    pub fn get_main_object_id(&self) -> &Id {
67        return &self.id;
68    }
69
70    pub fn get(&self, id: &str) -> Option<IndexEntryRef> {
71        match self.index.get(id) {
72            Some(entry) => Some(entry.clone()),
73            None => None,
74        }
75    }
76
77    pub fn get_mut(&mut self, id: &str) -> Option<&mut IndexEntryRef> {
78        return self.index.get_mut(id);
79    }
80
81    pub fn get_value(&self, id: &str, property: &str) -> Option<ObjectValue> {
82        let obj = self.get(id)?;
83        let obj = obj.borrow();
84        let value = obj.get(&property);
85        Some(value.clone())
86    }
87
88    pub fn set_value(
89        &mut self,
90        id: &str,
91        property: &str,
92        value: ObjectValue,
93    ) -> Result<(), HitError> {
94        //remove reference for old value
95        // TODO : should be put in the ObjectValue::Reference case of the below match ?
96        unindex_reference_from_property(self, id, property)?;
97
98        match value.clone() {
99            ObjectValue::Null => {}
100            ObjectValue::Bool(_) => {}
101            ObjectValue::Date(_) => {}
102            ObjectValue::F32(_) => {}
103            ObjectValue::Reference(value) => {
104                index_reference(self, &value, property, id)?;
105            }
106            ObjectValue::String(_) => {}
107            _ => return Err(HitError::CanOnlySetScalarValues()),
108        }
109
110        let entry = self.get(id).ok_or(HitError::IDNotFound(id.to_string(), "set_value".into()))?;
111        entry.borrow_mut().set(property, value)?;
112        Ok(())
113    }
114
115    pub fn iter(&self) -> Iter<Id, IndexEntryRef> {
116        return self.index.iter();
117    }
118
119    pub fn contains(&self, id: &str) -> bool {
120        return self.index.contains_key(id);
121    }
122
123    pub fn insert(
124        &mut self,
125        id: &str,
126        values: ObjectValues,
127        parent: IndexEntryProperty,
128        before_id: Option<String>,
129    ) -> Result<(), HitError> {
130        self.insert_quietly(id, values, parent, before_id)?;
131        //dispatch value to parent property
132        let (entry, parent) =
133            get_parent_index_entry(self, &id)?.ok_or(HitError::InvalidParentID(id.to_string()))?;
134        Index::dispatch_value_property(entry, &parent.property);
135        Ok(())
136    }
137
138    /**
139     * Used for import
140     */
141    pub(in crate::index) fn insert_raw(
142        &mut self,
143        id: &str,
144        values: ObjectValues,
145        parent: Option<IndexEntryProperty>,
146    ) -> Result<(), HitError> {
147        //check id doesnt exist
148        if self.index.contains_key(id) {
149            return Err(HitError::DuplicateID(id.to_string()));
150        }
151        self.index.insert(
152            id.to_string(),
153            IndexEntry::new(id.to_string(), values, parent.clone()),
154        );
155        Ok(())
156    }
157
158    fn insert_quietly(
159        &mut self,
160        id: &str,
161        values: ObjectValues,
162        parent: IndexEntryProperty,
163        before_id: Option<String>,
164    ) -> Result<(), HitError> {
165        // insert
166        self.insert_raw(id, values.clone(), Some(parent.clone()))?;
167
168        //index references to other objects
169        index_object_references(self, values, id)?;
170
171        // update the list of ids in the parent
172        insert_subobject_in_array(self, parent, id, before_id)?;
173
174        Ok(())
175    }
176
177    pub fn move_reference(
178        &mut self,
179        id: &str,
180        target: IndexEntryProperty,
181        before_id: Option<Id>,
182    ) -> Result<(), HitError> {
183        let target_entry = {
184            self.get_mut(&target.id)
185                .ok_or(HitError::IDNotFound(target.id.to_string(), "move_reference".into()))?
186        };
187        let data = get_parent_property_value(&target_entry, &target);
188
189        // throw error if reference not found
190        match &data {
191            ObjectValue::VecReference(data) => {
192                if !data.iter().any(|r| r.id == id) {
193                    return Err(HitError::ReferenceNotFound());
194                }
195                match &before_id {
196                    Some(before_id) => {
197                        if !data.iter().any(|r| r.id == id) {
198                            return Err(HitError::InvalidBeforeId(before_id.clone()));
199                        }
200                    }
201                    None => {}
202                }
203            }
204            ObjectValue::Null => {
205                return Err(HitError::ReferenceNotFound());
206            }
207            _ => return Err(HitError::CannotInsertReferenceInThisDataType()),
208        }
209
210        let data = mutate_remove_from_reference_array(data, id)?.unwrap_or(vec![]);
211        let data =
212            mutate_insert_in_reference_array(ObjectValue::VecReference(data), id, before_id)?;
213        //update the value in the index entry
214        target_entry.borrow_mut().data.insert(
215            target.clone().property,
216            ObjectValue::VecReference(data.clone()),
217        );
218
219        Index::dispatch_value(
220            target_entry.clone(),
221            &target.property,
222            ObjectValue::VecReference(data),
223        );
224        Ok(())
225    }
226
227    pub fn insert_reference(
228        &mut self,
229        id: &str,
230        target: IndexEntryProperty,
231        before_id: Option<Id>,
232    ) -> Result<(), HitError> {
233        {
234            let target_entry = {
235                self.get_mut(&target.id)
236                    .ok_or(HitError::IDNotFound(target.id.to_string(), "insert_reference".into()))?
237            };
238            let data = get_parent_property_value(&target_entry, &target);
239
240            match &data {
241                ObjectValue::VecReference(data) => {
242                    // TODO ? throw error if reference already exists
243                    // if data.iter().any(|r| r.id == id) {
244                    //  return Err(HitError::CannotInsertReferenceTwice());
245                    // }
246                }
247                ObjectValue::Null => {}
248                _ => return Err(HitError::CannotInsertReferenceInThisDataType()),
249            }
250
251            //generate mutated vector
252            let data = mutate_insert_in_reference_array(data, id, before_id)?;
253            let value = ObjectValue::VecReference(data);
254
255            //update the value in the index entry
256            target_entry
257                .borrow_mut()
258                .data
259                .insert(target.clone().property, value.clone());
260        }
261
262        //update reference index
263        index_reference(
264            self,
265            &Reference { id: id.to_string() },
266            &target.property,
267            &target.id,
268        )?;
269        //send the value as an event
270        {
271            let target_entry = {
272                self.get_mut(&target.id)
273                    .ok_or(HitError::IDNotFound(target.id.to_string(), "insert_reference".into()))?
274            };
275            let data = get_parent_property_value(&target_entry, &target);
276            Index::dispatch_value(target_entry.clone(), &target.property, data);
277        }
278
279        Ok(())
280    }
281
282    pub fn remove_reference(
283        &mut self,
284        id: &str,
285        parent: IndexEntryProperty,
286    ) -> Result<(), HitError> {
287        let value = remove_reference_from_parent_array_from_property(self, parent.clone(), id)?;
288
289        unindex_reference(self, parent.clone(), id)?;
290        //dispatch event
291        let entry = self
292            .get(&parent.clone().id)
293            .ok_or(HitError::IDNotFound(parent.clone().id.to_string(), "remove_reference".into()))?;
294        Index::dispatch_value(entry, &parent.property, value);
295        Ok(())
296    }
297
298    pub(in crate) fn get_references(&self, id: &str) -> Result<Vec<IndexEntryProperty>, HitError> {
299        get_references(&self, id)
300    }
301
302    pub fn find_references_recursive(
303        &self,
304        id: &str,
305    ) -> Result<(HashMap<String, Vec<IndexEntryProperty>>, Vec<String>), HitError> {
306        find_references_recursive(self, id)
307    }
308
309    pub fn remove_object(&mut self, id: &str) -> Result<Vec<String>, HitError> {
310        let (parent_entry, parent) =
311            get_parent_index_entry(self, &id)?.ok_or(HitError::CannotDeleteRootObject())?;
312
313        let (refs, id_list) = find_references_recursive(self, id)?;
314        if refs.len() > 0 {
315            return Err(HitError::CannotDeleteObjectWithReferences(refs));
316        }
317        remove_object_helper(self, id)?;
318
319        //remove from ref index the references in the object's data
320        Index::dispatch_value_property(parent_entry, &parent.property);
321        Ok(id_list)
322    }
323
324    pub fn move_object(
325        &mut self,
326        id: &str,
327        property: IndexEntryProperty,
328        before_id: Option<String>,
329    ) -> Result<(), HitError> {
330        move_object(self, id, property, before_id)
331    }
332
333    fn dispatch_value_property(entry: IndexEntryRef, property: &str) {
334        let value = entry.borrow().get(property).clone();
335        Index::dispatch_value(entry, property, value)
336    }
337
338    fn dispatch_value(entry: IndexEntryRef, property: &str, value: ObjectValue) {
339        entry.borrow_mut().dispatch_value(property, value);
340    }
341}
342
343#[cfg(test)]
344mod tests {
345    use linked_hash_map::LinkedHashMap;
346
347    use crate::index::Index;
348    use crate::HitError;
349    use crate::ObjectValue;
350    use crate::Reference;
351
352    #[test]
353    fn it_should_create_a_new_index_with_values() {
354        let mut values = LinkedHashMap::new();
355        values.insert("test".into(), ObjectValue::Bool(true));
356        values.insert("testString".into(), ObjectValue::String("value".into()));
357        assert!(Index::new("id", values).is_ok());
358    }
359
360    #[test]
361    fn it_should_fail_creating_a_new_index_with_reference_values() {
362        let mut values = LinkedHashMap::new();
363        values.insert(
364            "reference".into(),
365            ObjectValue::Reference(Reference { id: "a".into() }),
366        );
367        assert!(matches!(
368            Index::new("id", values).err(),
369            Some(HitError::CanOnlySetScalarValuesInInsertedObject())
370        ));
371    }
372
373    #[test]
374    fn it_should_fail_creating_a_new_index_with_reference_array_values() {
375        let mut values = LinkedHashMap::new();
376        values.insert(
377            "reference".into(),
378            ObjectValue::VecReference(vec![Reference { id: "a".into() }]),
379        );
380        assert!(matches!(
381            Index::new("id", values).err(),
382            Some(HitError::CanOnlySetScalarValuesInInsertedObject())
383        ));
384    }
385    #[test]
386    fn it_should_fail_creating_a_new_index_with_subobject_values() {
387        let mut values = LinkedHashMap::new();
388        values.insert(
389            "reference".into(),
390            ObjectValue::SubObject(Reference { id: "a".into() }),
391        );
392        assert!(Index::new("id", values).is_err());
393    }
394    #[test]
395    fn it_should_fail_creating_a_new_index_with_subobject_array_values() {
396        let mut values = LinkedHashMap::new();
397        values.insert(
398            "reference".into(),
399            ObjectValue::VecSubObjects(vec![Reference { id: "a".into() }]),
400        );
401        assert!(Index::new("id", values).is_err());
402    }
403
404    #[test]
405    fn it_should_get_existing_data() {
406        let mut values = LinkedHashMap::new();
407        values.insert("test".into(), ObjectValue::Bool(true));
408        let index = Index::new("id", values).ok().unwrap();
409
410        let item = index.get("id").unwrap();
411        let item = item.borrow();
412
413        let prop = item.get("test");
414        match prop {
415            ObjectValue::Bool(value) => {
416                assert_eq!(value, &true);
417            }
418            _ => panic!("Should be a boolean"),
419        }
420    }
421}