honeycomb_core/attributes/
manager.rs

1//! attribute super structure code
2//!
3//! this module contains all code used to implement a manager struct, used to handle generic
4//! attributes embedded in a given combinatorial map.
5
6use std::{any::TypeId, collections::HashMap};
7
8use crate::attributes::{
9    AttributeBind, AttributeError, AttributeStorage, AttributeUpdate, UnknownAttributeStorage,
10};
11use crate::cmap::{DartIdType, OrbitPolicy};
12use crate::stm::{StmClosureResult, Transaction, TransactionClosureResult};
13
14// convenience macros
15
16macro_rules! get_storage {
17    ($slf: ident, $id: ident) => {
18        let probably_storage = match A::BIND_POLICY {
19            OrbitPolicy::Vertex | OrbitPolicy::VertexLinear => {
20                $slf.vertices.get(&TypeId::of::<A>())
21            }
22            OrbitPolicy::Edge => $slf.edges.get(&TypeId::of::<A>()),
23            OrbitPolicy::Face | OrbitPolicy::FaceLinear => $slf.faces.get(&TypeId::of::<A>()),
24            OrbitPolicy::Volume | OrbitPolicy::VolumeLinear => $slf.volumes.get(&TypeId::of::<A>()),
25            OrbitPolicy::Custom(_) => $slf.others.get(&TypeId::of::<A>()),
26        };
27        let $id = probably_storage
28            .map(|m| m.downcast_ref::<<A as AttributeBind>::StorageType>())
29            .flatten();
30    };
31}
32
33#[cfg(test)]
34macro_rules! get_storage_mut {
35    ($slf: ident, $id: ident) => {
36        let probably_storage = match A::BIND_POLICY {
37            OrbitPolicy::Vertex | OrbitPolicy::VertexLinear => {
38                $slf.vertices.get_mut(&TypeId::of::<A>())
39            }
40            OrbitPolicy::Edge => $slf.edges.get_mut(&TypeId::of::<A>()),
41            OrbitPolicy::Face | OrbitPolicy::FaceLinear => $slf.faces.get_mut(&TypeId::of::<A>()),
42            OrbitPolicy::Volume | OrbitPolicy::VolumeLinear => {
43                $slf.volumes.get_mut(&TypeId::of::<A>())
44            }
45            OrbitPolicy::Custom(_) => $slf.others.get_mut(&TypeId::of::<A>()),
46        };
47        let $id = probably_storage
48            .map(|m| m.downcast_mut::<<A as AttributeBind>::StorageType>())
49            .flatten();
50    };
51}
52
53/// Main attribute storage structure.
54///
55/// **This structure is not meant to be used directly**.
56///
57/// This structure is used to store all generic attributes that the user may add to the
58/// combinatorial map he's building.
59///
60/// # Implementation
61///
62/// The structure uses hashmaps in order to store each attribute's dedicated storage. Which storage
63/// is used is determined by the associated type [`AttributeBind::StorageType`].
64///
65/// The key type used by the map is each attribute's [`TypeId`]. This implies that all attributes
66/// must have a different (unique) type. For example, two decimal-valued attribute will need to be
67/// wrapped in different dedicated structures.
68///
69/// Using the [`TypeId`] as the key value for collections yields a cleaner API, where the only
70/// argument passed to access methods is the ID of the cell of which they want the attribute. The
71/// actual attribute type is specified by passing a generic to the method. This bypasses any issues
72/// linked to literal-typed keys, such as typos, naming conventions, portability, etc.
73///
74/// Generics passed in access methods also have a secondary usage. To store heterogeneous
75/// collections, the internal hashmaps uses `Box<dyn UnknownAttributeStorage>` as their value type.
76/// Some operations require us to downcast the stored object (implementing
77/// `UnknownAttributeStorage`) to the correct collection type. This is achieved by using the
78/// `downcast-rs` crate and the associated storage type [`AttributeBind::StorageType`]. What
79/// follows is a simplified version of that code:
80///
81/// ```
82/// # use std::any::TypeId;
83/// # use std::collections::HashMap;
84/// # use honeycomb_core::attributes::{AttributeBind, AttributeStorage, UnknownAttributeStorage};
85/// pub struct Manager {
86///     inner: HashMap<TypeId, Box<dyn UnknownAttributeStorage>>,
87/// }
88///
89/// impl Manager {
90///     pub fn add_storage<A: AttributeBind + 'static>(
91///         &mut self,
92///         size: usize,
93///     ) {
94///         let typeid = TypeId::of::<A>();
95///         let new_storage = <A as AttributeBind>::StorageType::new(size);
96///         self.inner.insert(typeid, Box::new(new_storage));
97///     }
98///
99///     pub fn get_storage<A: AttributeBind>(&self) -> &<A as AttributeBind>::StorageType {
100///         let probably_storage = &self.inner[&TypeId::of::<A>()];
101///         // downcast is possible because:
102///         // - StorageType: AttributeStorage<A>
103///         // - AttributeStorage<A>: UnknownAttributeStorage
104///         probably_storage
105///             .downcast_ref::<<A as AttributeBind>::StorageType>()
106///             .expect("E: could not downcast generic storage to specified attribute type")
107///     }
108/// }
109/// ```
110#[derive(Default)]
111pub(crate) struct AttrStorageManager {
112    /// Vertex attributes' storages.
113    vertices: HashMap<TypeId, Box<dyn UnknownAttributeStorage>>,
114    /// Edge attributes' storages.
115    edges: HashMap<TypeId, Box<dyn UnknownAttributeStorage>>,
116    /// Face attributes' storages.
117    faces: HashMap<TypeId, Box<dyn UnknownAttributeStorage>>,
118    /// Volume attributes' storages.
119    volumes: HashMap<TypeId, Box<dyn UnknownAttributeStorage>>,
120    /// Other storages.
121    others: HashMap<TypeId, Box<dyn UnknownAttributeStorage>>, // Orbit::Custom
122}
123
124unsafe impl Send for AttrStorageManager {}
125unsafe impl Sync for AttrStorageManager {}
126
127/// **General methods**
128impl AttrStorageManager {
129    // attribute-agnostic
130
131    /// Extend the size of all storages in the manager.
132    ///
133    /// # Arguments
134    ///
135    /// - `length: usize` -- Length by which storages should be extended.
136    pub fn extend_storages(&mut self, length: usize) {
137        for storage in self.vertices.values_mut() {
138            storage.extend(length);
139        }
140        for storage in self.edges.values_mut() {
141            storage.extend(length);
142        }
143        for storage in self.faces.values_mut() {
144            storage.extend(length);
145        }
146        for storage in self.volumes.values_mut() {
147            storage.extend(length);
148        }
149        for storage in self.others.values_mut() {
150            storage.extend(length);
151        }
152    }
153
154    // attribute-specific
155
156    /// Add a new storage to the manager.
157    ///
158    /// For a breakdown of the principles used for implementation, refer to the *Explanation*
159    /// section of the [`AttrStorageManager`] documentation entry.
160    ///
161    /// # Arguments
162    ///
163    /// - `A: AttributeBind + 'static` -- Type of the attribute that will be stored.
164    /// - `size: usize` -- Initial size of the new storage.
165    ///
166    /// # Panics
167    ///
168    /// This function will panic if there is already a storage of attribute `A` in the manager.
169    pub fn add_storage<A: AttributeBind + 'static>(&mut self, size: usize) {
170        let typeid = TypeId::of::<A>();
171        let new_storage = <A as AttributeBind>::StorageType::new(size);
172        if match A::BIND_POLICY {
173            OrbitPolicy::Vertex | OrbitPolicy::VertexLinear => {
174                self.vertices.insert(typeid, Box::new(new_storage))
175            }
176            OrbitPolicy::Edge => self.edges.insert(typeid, Box::new(new_storage)),
177            OrbitPolicy::Face | OrbitPolicy::FaceLinear => {
178                self.faces.insert(typeid, Box::new(new_storage))
179            }
180            OrbitPolicy::Volume | OrbitPolicy::VolumeLinear => {
181                self.volumes.insert(typeid, Box::new(new_storage))
182            }
183            OrbitPolicy::Custom(_) => self.others.insert(typeid, Box::new(new_storage)),
184        }
185        .is_some()
186        {
187            eprintln!(
188                "W: Storage of attribute `{}` already exists in the attribute storage manager",
189                std::any::type_name::<A>()
190            );
191            eprintln!("   Continuing...");
192        }
193    }
194
195    #[cfg(test)]
196    /// Extend the size of the storage of a given attribute.
197    ///
198    /// # Arguments
199    ///
200    /// - `A: AttributeBind` -- Attribute of which the storage should be extended.
201    /// - `length: usize` -- Length by which the storage should be extended.
202    ///
203    pub fn extend_storage<A: AttributeBind>(&mut self, length: usize) {
204        get_storage_mut!(self, storage);
205        if let Some(st) = storage {
206            st.extend(length);
207        } else {
208            eprintln!(
209                "W: could not extend storage of attribute {} - storage not found",
210                std::any::type_name::<A>()
211            );
212        }
213    }
214
215    /// Get a reference to the storage of a given attribute.
216    ///
217    /// # Arguments
218    ///
219    /// - `A: AttributeBind` -- Attribute stored by the fetched storage.
220    ///
221    /// # Panics
222    ///
223    /// This method may panic if:
224    /// - there's no storage associated with the specified attribute
225    /// - downcasting `Box<dyn UnknownAttributeStorage>` to `<A as AttributeBind>::StorageType` fails
226    #[cfg(test)]
227    #[must_use = "unused return value"]
228    pub fn get_storage<A: AttributeBind>(&self) -> Option<&<A as AttributeBind>::StorageType> {
229        let probably_storage = match A::BIND_POLICY {
230            OrbitPolicy::Vertex | OrbitPolicy::VertexLinear => &self.vertices[&TypeId::of::<A>()],
231            OrbitPolicy::Edge => &self.edges[&TypeId::of::<A>()],
232            OrbitPolicy::Face | OrbitPolicy::FaceLinear => &self.faces[&TypeId::of::<A>()],
233            OrbitPolicy::Volume | OrbitPolicy::VolumeLinear => &self.volumes[&TypeId::of::<A>()],
234            OrbitPolicy::Custom(_) => &self.others[&TypeId::of::<A>()],
235        };
236        probably_storage.downcast_ref::<<A as AttributeBind>::StorageType>()
237    }
238
239    /// Remove an entire attribute storage from the manager.
240    ///
241    /// This method is useful when implementing routines that uses attributes to run; Those can then be removed
242    /// before the final result is returned.
243    ///
244    /// # Arguments
245    ///
246    /// - `A: AttributeBind` -- Attribute stored by the fetched storage.
247    pub fn remove_storage<A: AttributeBind>(&mut self) {
248        // we could return it ?
249        let _ = match A::BIND_POLICY {
250            OrbitPolicy::Vertex | OrbitPolicy::VertexLinear => {
251                &self.vertices.remove(&TypeId::of::<A>())
252            }
253            OrbitPolicy::Edge => &self.edges.remove(&TypeId::of::<A>()),
254            OrbitPolicy::Face | OrbitPolicy::FaceLinear => &self.faces.remove(&TypeId::of::<A>()),
255            OrbitPolicy::Volume | OrbitPolicy::VolumeLinear => {
256                &self.volumes.remove(&TypeId::of::<A>())
257            }
258            OrbitPolicy::Custom(_) => &self.others.remove(&TypeId::of::<A>()),
259        };
260    }
261}
262
263/// Merge variants.
264impl AttrStorageManager {
265    // attribute-agnostic try
266
267    /*
268    /// Execute a merging operation on all attributes associated with a given orbit
269    /// for specified cells.
270    ///
271    /// # Arguments
272    ///
273    /// - `trans: &mut Transaction` -- Transaction used for synchronization.
274    /// - `orbit_policy: OrbitPolicy` -- Orbit associated with affected attributes.
275    /// - `id_out: DartIdentifier` -- Identifier to write the result to.
276    /// - `id_in_lhs: DartIdentifier` -- Identifier of one attribute value to merge.
277    /// - `id_in_rhs: DartIdentifier` -- Identifier of the other attribute value to merge.
278    ///
279    /// # Errors
280    ///
281    /// This method will fail, returning an error, if:
282    /// - the transaction cannot be completed
283    /// - a merge fails (e.g. because one merging value is missing)
284    ///
285    /// The returned error can be used in conjunction with transaction control to avoid any
286    /// modifications in case of failure at attribute level. The user can then choose, through its
287    /// transaction control policy, to retry or abort as he wishes.
288    pub fn merge_attributes(
289        &self,
290        trans: &mut Transaction,
291        orbit_policy: &OrbitPolicy,
292        id_out: DartIdType,
293        id_in_lhs: DartIdType,
294        id_in_rhs: DartIdType,
295    ) -> CMapResult<()> {
296        match orbit_policy {
297            OrbitPolicy::Vertex | OrbitPolicy::VertexLinear => {
298                self.merge_vertex_attributes(trans, id_out, id_in_lhs, id_in_rhs)
299            }
300            OrbitPolicy::Edge => {
301                self.merge_edge_attributes(trans, id_out, id_in_lhs, id_in_rhs)
302            }
303            OrbitPolicy::Face | OrbitPolicy::FaceLinear => {
304                self.merge_face_attributes(trans, id_out, id_in_lhs, id_in_rhs)
305            }
306            OrbitPolicy::Volume | OrbitPolicy::VolumeLinear => {
307                self.merge_volume_attributes(trans, id_out, id_in_lhs, id_in_rhs)
308            }
309            OrbitPolicy::Custom(_) => unimplemented!(),
310        }
311    }
312    */
313
314    /// Execute a merging operation on all attributes associated with vertices for specified cells.
315    ///
316    /// # Arguments
317    ///
318    /// - `trans: &mut Transaction` -- Transaction used for synchronization.
319    /// - `id_out: DartIdentifier` -- Identifier to write the result to.
320    /// - `id_in_lhs: DartIdentifier` -- Identifier of one attribute value to merge.
321    /// - `id_in_rhs: DartIdentifier` -- Identifier of the other attribute value to merge.
322    ///
323    /// # Errors
324    ///
325    /// This method will fail, returning an error, if:
326    /// - the transaction cannot be completed
327    /// - a merge fails (e.g. because one merging value is missing)
328    ///
329    /// The returned error can be used in conjunction with transaction control to avoid any
330    /// modifications in case of failure at attribute level. The user can then choose, through its
331    /// transaction control policy, to retry or abort as he wishes.
332    pub fn merge_vertex_attributes(
333        &self,
334        trans: &mut Transaction,
335        id_out: DartIdType,
336        id_in_lhs: DartIdType,
337        id_in_rhs: DartIdType,
338    ) -> TransactionClosureResult<(), AttributeError> {
339        for storage in self.vertices.values() {
340            storage.merge(trans, id_out, id_in_lhs, id_in_rhs)?;
341        }
342        Ok(())
343    }
344
345    /// Execute a merging operation on all attributes associated with edges for specified cells.
346    ///
347    /// # Arguments
348    ///
349    /// - `trans: &mut Transaction` -- Transaction used for synchronization.
350    /// - `id_out: DartIdentifier` -- Identifier to write the result to.
351    /// - `id_in_lhs: DartIdentifier` -- Identifier of one attribute value to merge.
352    /// - `id_in_rhs: DartIdentifier` -- Identifier of the other attribute value to merge.
353    ///
354    /// # Errors
355    ///
356    /// This method will fail, returning an error, if:
357    /// - the transaction cannot be completed
358    /// - a merge fails (e.g. because one merging value is missing)
359    ///
360    /// The returned error can be used in conjunction with transaction control to avoid any
361    /// modifications in case of failure at attribute level. The user can then choose, through its
362    /// transaction control policy, to retry or abort as he wishes.
363    pub fn merge_edge_attributes(
364        &self,
365        trans: &mut Transaction,
366        id_out: DartIdType,
367        id_in_lhs: DartIdType,
368        id_in_rhs: DartIdType,
369    ) -> TransactionClosureResult<(), AttributeError> {
370        for storage in self.edges.values() {
371            storage.merge(trans, id_out, id_in_lhs, id_in_rhs)?;
372        }
373        Ok(())
374    }
375
376    /// Execute a merging operation on all attributes associated with faces for specified cells.
377    ///
378    /// # Arguments
379    ///
380    /// - `trans: &mut Transaction` -- Transaction used for synchronization.
381    /// - `id_out: DartIdentifier` -- Identifier to write the result to.
382    /// - `id_in_lhs: DartIdentifier` -- Identifier of one attribute value to merge.
383    /// - `id_in_rhs: DartIdentifier` -- Identifier of the other attribute value to merge.
384    ///
385    /// # Errors
386    ///
387    /// This method will fail, returning an error, if:
388    /// - the transaction cannot be completed
389    /// - a merge fails (e.g. because one merging value is missing)
390    ///
391    /// The returned error can be used in conjunction with transaction control to avoid any
392    /// modifications in case of failure at attribute level. The user can then choose, through its
393    /// transaction control policy, to retry or abort as he wishes.
394    pub fn merge_face_attributes(
395        &self,
396        trans: &mut Transaction,
397        id_out: DartIdType,
398        id_in_lhs: DartIdType,
399        id_in_rhs: DartIdType,
400    ) -> TransactionClosureResult<(), AttributeError> {
401        for storage in self.faces.values() {
402            storage.merge(trans, id_out, id_in_lhs, id_in_rhs)?;
403        }
404        Ok(())
405    }
406
407    /*
408    /// Execute a merging operation on all attributes associated with volumes for specified cells.
409    ///
410    /// # Arguments
411    ///
412    /// - `trans: &mut Transaction` -- Transaction used for synchronization.
413    /// - `id_out: DartIdentifier` -- Identifier to write the result to.
414    /// - `id_in_lhs: DartIdentifier` -- Identifier of one attribute value to merge.
415    /// - `id_in_rhs: DartIdentifier` -- Identifier of the other attribute value to merge.
416    ///
417    /// # Errors
418    ///
419    /// This method will fail, returning an error, if:
420    /// - the transaction cannot be completed
421    /// - a merge fails (e.g. because one merging value is missing)
422    ///
423    /// The returned error can be used in conjunction with transaction control to avoid any
424    /// modifications in case of failure at attribute level. The user can then choose, through its
425    /// transaction control policy, to retry or abort as he wishes.
426    pub fn merge_volume_attributes(
427        &self,
428        trans: &mut Transaction,
429        id_out: DartIdType,
430        id_in_lhs: DartIdType,
431        id_in_rhs: DartIdType,
432    ) -> CMapResult<()> {
433        for storage in self.volumes.values() {
434            storage.merge(trans, id_out, id_in_lhs, id_in_rhs)?;
435        }
436        Ok(())
437    }
438    */
439
440    // attribute-specific
441
442    /// Merge given attribute values.
443    ///
444    /// # Arguments
445    ///
446    /// - `A: AttributeBind + AttributeUpdate` -- Attribute to merge values of.
447    /// - `trans: &mut Transaction` -- Transaction used for synchronization.
448    /// - `id_out: DartIdentifier` -- Identifier to write the result to.
449    /// - `id_in_lhs: DartIdentifier` -- Identifier of one attribute value to merge.
450    /// - `id_in_rhs: DartIdentifier` -- Identifier of the other attribute value to merge.
451    ///
452    /// # Errors
453    ///
454    /// This method will fail, returning an error, if:
455    /// - the transaction cannot be completed
456    /// - the merge fails (e.g. because one merging value is missing)
457    ///
458    /// The returned error can be used in conjunction with transaction control to avoid any
459    /// modifications in case of failure at attribute level. The user can then choose, through its
460    /// transaction control policy, to retry or abort as he wishes.
461    #[cfg(test)]
462    pub fn merge_attribute<A: AttributeBind + AttributeUpdate>(
463        &self,
464        trans: &mut Transaction,
465        id_out: DartIdType,
466        id_in_lhs: DartIdType,
467        id_in_rhs: DartIdType,
468    ) -> TransactionClosureResult<(), AttributeError> {
469        get_storage!(self, storage);
470        if let Some(st) = storage {
471            st.merge(trans, id_out, id_in_lhs, id_in_rhs)
472        } else {
473            eprintln!(
474                "W: could not update storage of attribute {} - storage not found",
475                std::any::type_name::<A>()
476            );
477            Ok(())
478        }
479    }
480}
481
482/// Split variants.
483impl AttrStorageManager {
484    // attribute-agnostic try
485
486    /*
487    /// Execute a splitting operation on all attributes associated with a given orbit
488    /// for specified cells.
489    ///
490    /// # Arguments
491    ///
492    /// - `trans: &mut Transaction` -- Transaction used for synchronization.
493    /// - `orbit_policy: OrbitPolicy` -- Orbit associated with affected attributes.
494    /// - `id_out_lhs: DartIdentifier` -- Identifier to write the result to.
495    /// - `id_out_rhs: DartIdentifier` -- Identifier to write the result to.
496    /// - `id_in: DartIdentifier` -- Identifier of the attribute value to split.
497    ///
498    /// # Errors
499    ///
500    /// This method will fail, returning an error, if:
501    /// - the transaction cannot be completed
502    /// - a split fails (e.g. because there is no value to split from)
503    ///
504    /// The returned error can be used in conjunction with transaction control to avoid any
505    /// modifications in case of failure at attribute level. The user can then choose, through its
506    /// transaction control policy, to retry or abort as he wishes.
507    pub fn split_attributes(
508        &self,
509        trans: &mut Transaction,
510        orbit_policy: &OrbitPolicy,
511        id_out_lhs: DartIdType,
512        id_out_rhs: DartIdType,
513        id_in: DartIdType,
514    ) -> CMapResult<()> {
515        match orbit_policy {
516            OrbitPolicy::Vertex | OrbitPolicy::VertexLinear => {
517                self.split_vertex_attributes(trans, id_out_lhs, id_out_rhs, id_in)
518            }
519            OrbitPolicy::Edge => {
520                self.split_edge_attributes(trans, id_out_lhs, id_out_rhs, id_in)
521            }
522            OrbitPolicy::Face | OrbitPolicy::FaceLinear => {
523                self.split_face_attributes(trans, id_out_lhs, id_out_rhs, id_in)
524            }
525            OrbitPolicy::Volume | OrbitPolicy::VolumeLinear => {
526                self.split_volume_attributes(trans, id_out_lhs, id_out_rhs, id_in)
527            }
528            OrbitPolicy::Custom(_) => unimplemented!(),
529        }
530    }
531    */
532
533    /// Execute a splitting operation on all attributes associated with vertices for specified cells.
534    ///
535    /// # Arguments
536    ///
537    /// - `trans: &mut Transaction` -- Transaction used for synchronization.
538    /// - `id_out_lhs: DartIdentifier` -- Identifier to write the result to.
539    /// - `id_out_rhs: DartIdentifier` -- Identifier to write the result to.
540    /// - `id_in: DartIdentifier` -- Identifier of the attribute value to split.
541    ///
542    /// # Errors
543    ///
544    /// This method will fail, returning an error, if:
545    /// - the transaction cannot be completed
546    /// - a split fails (e.g. because there is no value to split from)
547    ///
548    /// The returned error can be used in conjunction with transaction control to avoid any
549    /// modifications in case of failure at attribute level. The user can then choose, through its
550    /// transaction control policy, to retry or abort as he wishes.
551    pub fn split_vertex_attributes(
552        &self,
553        trans: &mut Transaction,
554        id_out_lhs: DartIdType,
555        id_out_rhs: DartIdType,
556        id_in: DartIdType,
557    ) -> TransactionClosureResult<(), AttributeError> {
558        for storage in self.vertices.values() {
559            storage.split(trans, id_out_lhs, id_out_rhs, id_in)?;
560        }
561        Ok(())
562    }
563
564    /// Execute a splitting operation on all attributes associated with edges for specified cells.
565    ///
566    /// # Arguments
567    ///
568    /// - `trans: &mut Transaction` -- Transaction used for synchronization.
569    /// - `id_out_lhs: DartIdentifier` -- Identifier to write the result to.
570    /// - `id_out_rhs: DartIdentifier` -- Identifier to write the result to.
571    /// - `id_in: DartIdentifier` -- Identifier of the attribute value to split.
572    ///
573    /// # Errors
574    ///
575    /// This method will fail, returning an error, if:
576    /// - the transaction cannot be completed
577    /// - a split fails (e.g. because there is no value to split from)
578    ///
579    /// The returned error can be used in conjunction with transaction control to avoid any
580    /// modifications in case of failure at attribute level. The user can then choose, through its
581    /// transaction control policy, to retry or abort as he wishes.
582    pub fn split_edge_attributes(
583        &self,
584        trans: &mut Transaction,
585        id_out_lhs: DartIdType,
586        id_out_rhs: DartIdType,
587        id_in: DartIdType,
588    ) -> TransactionClosureResult<(), AttributeError> {
589        for storage in self.edges.values() {
590            storage.split(trans, id_out_lhs, id_out_rhs, id_in)?;
591        }
592        Ok(())
593    }
594
595    /// Execute a splitting operation on all attributes associated with faces for specified cells.
596    ///
597    /// # Arguments
598    ///
599    /// - `trans: &mut Transaction` -- Transaction used for synchronization.
600    /// - `id_out_lhs: DartIdentifier` -- Identifier to write the result to.
601    /// - `id_out_rhs: DartIdentifier` -- Identifier to write the result to.
602    /// - `id_in: DartIdentifier` -- Identifier of the attribute value to split.
603    ///
604    /// # Errors
605    ///
606    /// This method will fail, returning an error, if:
607    /// - the transaction cannot be completed
608    /// - a split fails (e.g. because there is no value to split from)
609    ///
610    /// The returned error can be used in conjunction with transaction control to avoid any
611    /// modifications in case of failure at attribute level. The user can then choose, through its
612    /// transaction control policy, to retry or abort as he wishes.
613    pub fn split_face_attributes(
614        &self,
615        trans: &mut Transaction,
616        id_out_lhs: DartIdType,
617        id_out_rhs: DartIdType,
618        id_in: DartIdType,
619    ) -> TransactionClosureResult<(), AttributeError> {
620        for storage in self.faces.values() {
621            storage.split(trans, id_out_lhs, id_out_rhs, id_in)?;
622        }
623        Ok(())
624    }
625
626    /*
627    /// Execute a splitting operation on all attributes associated with volumes for specified cells.
628    ///
629    /// # Errors
630    ///
631    /// This method will fail, returning an error, if:
632    /// - the transaction cannot be completed
633    /// - a split fails (e.g. because there is no value to split from)
634    ///
635    /// The returned error can be used in conjunction with transaction control to avoid any
636    /// modifications in case of failure at attribute level. The user can then choose, through its
637    /// transaction control policy, to retry or abort as he wishes.
638    pub fn split_volume_attributes(
639        &self,
640        trans: &mut Transaction,
641        id_out_lhs: DartIdType,
642        id_out_rhs: DartIdType,
643        id_in: DartIdType,
644    ) -> CMapResult<()> {
645        for storage in self.volumes.values() {
646            storage.split(trans, id_out_lhs, id_out_rhs, id_in)?;
647        }
648        Ok(())
649    }
650    */
651
652    // attribute-specific
653
654    /// Split given attribute value.
655    ///
656    /// # Arguments
657    ///
658    /// - `A: AttributeBind + AttributeUpdate` -- Attribute to split value of.
659    /// - `trans: &mut Transaction` -- Transaction used for synchronization.
660    /// - `id_out_lhs: DartIdentifier` -- Identifier to write the result to.
661    /// - `id_out_rhs: DartIdentifier` -- Identifier to write the result to.
662    /// - `id_in: DartIdentifier` -- Identifier of the attribute value to split.
663    ///
664    /// # Errors
665    ///
666    /// This method will fail, returning an error, if:
667    /// - the transaction cannot be completed
668    /// - the split fails (e.g. because there is no value to split from)
669    ///
670    /// The returned error can be used in conjunction with transaction control to avoid any
671    /// modifications in case of failure at attribute level. The user can then choose, through its
672    /// transaction control policy, to retry or abort as he wishes.
673    #[cfg(test)]
674    pub fn split_attribute<A: AttributeBind + AttributeUpdate>(
675        &self,
676        trans: &mut Transaction,
677        id_out_lhs: DartIdType,
678        id_out_rhs: DartIdType,
679        id_in: DartIdType,
680    ) -> TransactionClosureResult<(), AttributeError> {
681        get_storage!(self, storage);
682        if let Some(st) = storage {
683            st.split(trans, id_out_lhs, id_out_rhs, id_in)
684        } else {
685            eprintln!(
686                "W: could not update storage of attribute {} - storage not found",
687                std::any::type_name::<A>()
688            );
689            Ok(())
690        }
691    }
692}
693
694/// **Attribute read & write methods**
695impl AttrStorageManager {
696    // regular
697
698    #[allow(clippy::missing_errors_doc)]
699    /// Get the value of an attribute.
700    ///
701    /// # Arguments
702    ///
703    /// - `id: A::IdentifierType` -- Cell ID to which the attribute is associated.
704    ///
705    /// # Generic
706    ///
707    /// - `A: AttributeBind` -- Type of the attribute fetched.
708    ///
709    /// # Return / Errors
710    ///
711    /// This method is meant to be called in a context where the returned `Result` is used to
712    /// validate the transaction passed as argument. The result should not be processed manually,
713    /// only used via the `?` operator.
714    ///
715    /// # Panics
716    ///
717    /// This method may panic if:
718    /// - there's no storage associated with the specified attribute
719    /// - downcasting `Box<dyn UnknownAttributeStorage>` to `<A as AttributeBind>::StorageType` fails
720    /// - the index lands out of bounds
721    pub fn read_attribute<A: AttributeBind>(
722        &self,
723        trans: &mut Transaction,
724        id: A::IdentifierType,
725    ) -> StmClosureResult<Option<A>> {
726        get_storage!(self, storage);
727        if let Some(st) = storage {
728            st.read(trans, id)
729        } else {
730            eprintln!(
731                "W: could not update storage of attribute {} - storage not found",
732                std::any::type_name::<A>()
733            );
734            Ok(None)
735        }
736    }
737
738    #[allow(clippy::missing_errors_doc)]
739    /// Set the value of an attribute, and return the old one.
740    ///
741    /// # Arguments
742    ///
743    /// - `id: A::IdentifierType` -- ID of the cell to which the attribute is associated.
744    /// - `val: A` -- New value of the attribute for the given ID.
745    ///
746    /// # Generic
747    ///
748    /// - `A: AttributeBind` -- Type of the attribute being set.
749    ///
750    /// # Return / Errors
751    ///
752    /// This method is meant to be called in a context where the returned `Result` is used to
753    /// validate the transaction passed as argument. The result should not be processed manually,
754    /// only used via the `?` operator.
755    ///
756    /// # Panics
757    ///
758    /// This method may panic if:
759    /// - there's no storage associated with the specified attribute
760    /// - downcasting `Box<dyn UnknownAttributeStorage>` to `<A as AttributeBind>::StorageType` fails
761    /// - the index lands out of bounds
762    pub fn write_attribute<A: AttributeBind>(
763        &self,
764        trans: &mut Transaction,
765        id: A::IdentifierType,
766        val: A,
767    ) -> StmClosureResult<Option<A>> {
768        get_storage!(self, storage);
769        if let Some(st) = storage {
770            st.write(trans, id, val)
771        } else {
772            eprintln!(
773                "W: could not update storage of attribute {} - storage not found",
774                std::any::type_name::<A>()
775            );
776            Ok(None)
777        }
778    }
779
780    #[allow(clippy::missing_errors_doc)]
781    /// Remove the an item from an attribute storage, and return it.
782    ///
783    /// # Arguments
784    ///
785    /// - `id: A::IdentifierType` -- Cell ID to which the attribute is associated.
786    ///
787    /// # Generic
788    ///
789    /// - `A: AttributeBind` -- Type of the attribute fetched.
790    ///
791    /// # Return / Errors
792    ///
793    /// This method is meant to be called in a context where the returned `Result` is used to
794    /// validate the transaction passed as argument. The result should not be processed manually,
795    /// only used via the `?` operator.
796    ///
797    /// # Panics
798    ///
799    /// This method may panic if:
800    /// - there's no storage associated with the specified attribute
801    /// - downcasting `Box<dyn UnknownAttributeStorage>` to `<A as AttributeBind>::StorageType` fails
802    /// - the index lands out of bounds
803    pub(crate) fn remove_attribute<A: AttributeBind + AttributeUpdate>(
804        &self,
805        trans: &mut Transaction,
806        id: A::IdentifierType,
807    ) -> StmClosureResult<Option<A>> {
808        get_storage!(self, storage);
809        if let Some(st) = storage {
810            st.remove(trans, id)
811        } else {
812            eprintln!(
813                "W: could not update storage of attribute {} - storage not found",
814                std::any::type_name::<A>()
815            );
816            Ok(None)
817        }
818    }
819}