honeycomb_core/attributes/
traits.rs

1//! Generic attributes implementation
2//!
3//! This module contains all code used to handle attribute genericity in the context of mesh
4//! representation through combinatorial maps embedded data.
5
6use std::any::{Any, type_name};
7use std::fmt::Debug;
8
9use downcast_rs::{Downcast, impl_downcast};
10use fast_stm::TransactionClosureResult;
11
12use crate::attributes::AttributeError;
13use crate::cmap::{DartIdType, OrbitPolicy};
14use crate::stm::{StmClosureResult, Transaction};
15
16/// # Generic attribute trait
17///
18/// This trait is used to describe how a values of a given attribute are merged and split during
19/// sewing and unsewing operations.
20///
21/// ## Example
22///
23/// A detailed example is provided in the [user guide][UG].
24///
25/// [UG]: https://lihpc-computational-geometry.github.io/honeycomb/user-guide/usage/attributes.html
26pub trait AttributeUpdate: Sized + Send + Sync + Clone + Copy {
27    /// Merging routine, i.e. how to obtain a new value from two existing ones.
28    ///
29    /// # Errors
30    ///
31    /// You may use [`AttributeError::FailedMerge`] to model a possible failure in your attribute
32    /// merging process.
33    fn merge(attr1: Self, attr2: Self) -> Result<Self, AttributeError>;
34
35    /// Splitting routine, i.e. how to obtain the two new values from a single one.
36    ///
37    /// # Errors
38    ///
39    /// You may use [`AttributeError::FailedSplit`] to model a possible failure in your attribute
40    /// merging process.
41    fn split(attr: Self) -> Result<(Self, Self), AttributeError>;
42
43    #[allow(clippy::missing_errors_doc)]
44    /// Fallback merging routine, i.e. how to obtain a new value from a single existing one.
45    ///
46    /// The returned value directly affects the behavior of sewing methods: For example, if this
47    /// method returns an error for a given attribute, the `sew` method will fail. This allows the
48    /// user to define some attribute-specific behavior and enable fallbacks when it makes sense.
49    ///
50    /// # Return / Errors
51    ///
52    /// The default implementation fails and returns [`AttributeError::InsufficientData`]. You may
53    /// override the implementation and use [`AttributeError::FailedMerge`] to model another
54    /// possible failure.
55    fn merge_incomplete(_: Self) -> Result<Self, AttributeError> {
56        Err(AttributeError::InsufficientData(
57            "merge",
58            type_name::<Self>(),
59        ))
60    }
61
62    /// Fallback merging routine, i.e. how to obtain a new value from no existing one.
63    ///
64    /// The returned value directly affects the behavior of sewing methods: For example, if this
65    /// method returns an error for a given attribute, the `sew` method will fail. This allows the
66    /// user to define some attribute-specific behavior and enable fallbacks when it makes sense.
67    ///
68    /// # Errors
69    ///
70    /// The default implementation fails and returns [`AttributeError::InsufficientData`].
71    fn merge_from_none() -> Result<Self, AttributeError> {
72        Err(AttributeError::InsufficientData(
73            "merge",
74            type_name::<Self>(),
75        ))
76    }
77
78    /// Fallback splitting routine, i.e. how to obtain two new values from no existing one.
79    ///
80    /// The returned value directly affects the behavior of sewing methods: For example, if this
81    /// method returns an error for a given attribute, the `unsew` method will fail. This allows the
82    /// user to define some attribute-specific behavior and enable fallbacks when it makes sense.
83    /// value).
84    ///
85    /// # Errors
86    ///
87    /// The default implementation fails and returns [`AttributeError::InsufficientData`].
88    fn split_from_none() -> Result<(Self, Self), AttributeError> {
89        Err(AttributeError::InsufficientData(
90            "split",
91            type_name::<Self>(),
92        ))
93    }
94}
95
96/// # Generic attribute trait
97///
98/// This trait is used to describe how a given attribute binds to the map, and how it should be
99/// stored in memory.
100///
101/// ## Example
102///
103/// A detailed example is provided in the [user guide][UG].
104///
105/// [UG]: https://lihpc-computational-geometry.github.io/honeycomb/user-guide/usage/attributes.html
106pub trait AttributeBind: Debug + Sized + Any {
107    /// Storage type used for the attribute.
108    type StorageType: AttributeStorage<Self>;
109
110    /// Identifier type of the entity the attribute is bound to.
111    type IdentifierType: From<DartIdType> + num_traits::ToPrimitive + Clone;
112
113    /// [`OrbitPolicy`] determining the kind of topological entity to which the attribute
114    /// is associated.
115    const BIND_POLICY: OrbitPolicy;
116}
117
118/// # Generic attribute storage trait
119///
120/// This trait defines attribute-agnostic functions & methods. The documentation describes the
121/// expected behavior of each item. “ID” and “index” are used interchangeably.
122pub trait UnknownAttributeStorage: Any + Debug + Downcast {
123    /// Constructor
124    ///
125    /// # Arguments
126    ///
127    /// - `length: usize` -- Initial length/capacity of the storage. It should correspond to
128    ///   the upper bound of IDs used to index the attribute's values, i.e. the number of darts
129    ///   including the null dart.
130    ///
131    /// # Return
132    ///
133    /// Return a [Self] instance which yields correct accesses over the ID range `0..length`.
134    #[must_use = "unused return value"]
135    fn new(length: usize) -> Self
136    where
137        Self: Sized;
138
139    /// Extend the storage's length
140    ///
141    /// # Arguments
142    ///
143    /// - `length: usize` -- length of which the storage should be extended.
144    fn extend(&mut self, length: usize);
145
146    /// Set a value to `None`
147    ///
148    /// # Arguments
149    ///
150    /// - `t: &mut Transaction` -- Transaction used for synchronization.
151    /// - `id: DartIdType` -- ID of the value to clear / set to `None`.
152    ///
153    /// # Errors
154    ///
155    /// This method is meant to be called in a context where the returned `Result` is used to
156    /// validate the transaction passed as argument. Errors should not be processed manually,
157    /// only processed via the `?` operator.
158    fn clear_slot(&self, t: &mut Transaction, id: DartIdType) -> StmClosureResult<()>;
159
160    /// Return the number of stored attributes, i.e. the number of used slots in the storage (not
161    /// its length).
162    #[must_use = "unused return value"]
163    fn n_attributes(&self) -> usize;
164
165    /// Merge attributes to specified index
166    ///
167    /// # Arguments
168    ///
169    /// - `t: &mut Transaction` -- Transaction used for synchronization.
170    /// - `out: DartIdentifier` -- Identifier to associate the result with.
171    /// - `lhs_inp: DartIdentifier` -- Identifier of one attribute value to merge.
172    /// - `rhs_inp: DartIdentifier` -- Identifier of the other attribute value to merge.
173    ///
174    /// # Behavior (pseudo-code)
175    ///
176    /// ```text
177    /// let new_val = match (attributes.remove(lhs_inp), attributes.remove(rhs_inp)) {
178    ///     (Some(v1), Some(v2)) => AttributeUpdate::merge(v1, v2),
179    ///     (Some(v), None) | (None, Some(v)) => AttributeUpdate::merge_undefined(Some(v)),
180    ///     None, None => AttributeUpdate::merge_undefined(None),
181    /// }
182    /// attributes.set(out, new_val);
183    /// ```
184    ///
185    /// # Errors
186    ///
187    /// This method will fail, returning an error, if:
188    /// - the transaction cannot be completed
189    /// - the merge fails (e.g. because one merging value is missing)
190    ///
191    /// The returned error can be used in conjunction with transaction control to avoid any
192    /// modifications in case of failure at attribute level. The user can then choose, through its
193    /// transaction control policy, to retry or abort as he wishes.
194    fn merge(
195        &self,
196        t: &mut Transaction,
197        out: DartIdType,
198        lhs_inp: DartIdType,
199        rhs_inp: DartIdType,
200    ) -> TransactionClosureResult<(), AttributeError>;
201
202    /// Split attribute to specified indices
203    ///
204    /// # Arguments
205    ///
206    /// - `t: &mut Transaction` -- Transaction used for synchronization.
207    /// - `lhs_out: DartIdentifier` -- Identifier to associate the result with.
208    /// - `rhs_out: DartIdentifier` -- Identifier to associate the result with.
209    /// - `inp: DartIdentifier` -- Identifier of the attribute value to split.
210    ///
211    /// # Behavior pseudo-code
212    ///
213    /// ```text
214    /// (val_lhs, val_rhs) = AttributeUpdate::split(attributes.remove(inp).unwrap());
215    /// attributes[lhs_out] = val_lhs;
216    /// attributes[rhs_out] = val_rhs;
217    ///
218    /// ```
219    ///
220    /// # Errors
221    ///
222    /// This method will fail, returning an error, if:
223    /// - the transaction cannot be completed
224    /// - the split fails (e.g. because there is no value to split from)
225    ///
226    /// The returned error can be used in conjunction with transaction control to avoid any
227    /// modifications in case of failure at attribute level. The user can then choose, through its
228    /// transaction control policy, to retry or abort as he wishes.
229    fn split(
230        &self,
231        t: &mut Transaction,
232        lhs_out: DartIdType,
233        rhs_out: DartIdType,
234        inp: DartIdType,
235    ) -> TransactionClosureResult<(), AttributeError>;
236}
237
238impl_downcast!(UnknownAttributeStorage);
239
240/// # Generic attribute storage trait
241///
242/// This trait defines attribute-specific methods. The documentation describes the expected behavior
243/// of each method. "ID" and "index" are used interchangeably.
244pub trait AttributeStorage<A: AttributeBind>: UnknownAttributeStorage {
245    #[allow(clippy::missing_errors_doc)]
246    /// Read the value of an element at a given index.
247    ///
248    /// # Arguments
249    ///
250    /// - `t: &mut Transaction` -- Transaction used for synchronization.
251    /// - `index: A::IdentifierType` -- Cell index.
252    ///
253    /// # Return / Errors
254    ///
255    /// This method is meant to be called in a context where the returned `Result` is used to
256    /// validate the transaction passed as argument. Errors should not be processed manually,
257    /// only processed via the `?` operator.
258    ///
259    /// # Panics
260    ///
261    /// The method:
262    /// - should panic if the index lands out of bounds
263    /// - may panic if the index cannot be converted to `usize`
264    fn read(&self, t: &mut Transaction, id: A::IdentifierType) -> StmClosureResult<Option<A>>;
265
266    #[allow(clippy::missing_errors_doc)]
267    /// Write the value of an element at a given index and return the old value.
268    ///
269    /// # Arguments
270    ///
271    /// - `t: &mut Transaction` -- Transaction used for synchronization.
272    /// - `index: A::IdentifierType` -- Cell index.
273    /// - `val: A` -- Attribute value.
274    ///
275    /// # Return / Errors
276    ///
277    /// This method is meant to be called in a context where the returned `Result` is used to
278    /// validate the transaction passed as argument. Errors should not be processed manually,
279    /// only processed via the `?` operator.
280    ///
281    /// # Panics
282    ///
283    /// The method:
284    /// - should panic if the index lands out of bounds
285    /// - may panic if the index cannot be converted to `usize`
286    fn write(
287        &self,
288        t: &mut Transaction,
289        id: A::IdentifierType,
290        val: A,
291    ) -> StmClosureResult<Option<A>>;
292
293    #[allow(clippy::missing_errors_doc)]
294    /// Remove the value at a given index and return it.
295    ///
296    /// # Arguments
297    ///
298    /// - `t: &mut Transaction` -- Transaction used for synchronization.
299    /// - `index: A::IdentifierType` -- Cell index.
300    ///
301    /// # Return / Errors
302    ///
303    /// This method is meant to be called in a context where the returned `Result` is used to
304    /// validate the transaction passed as argument. Errors should not be processed manually,
305    /// only processed via the `?` operator.
306    ///
307    /// # Panics
308    ///
309    /// The method:
310    /// - should panic if the index lands out of bounds
311    /// - may panic if the index cannot be converted to `usize`
312    fn remove(&self, t: &mut Transaction, id: A::IdentifierType) -> StmClosureResult<Option<A>>;
313}