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::{type_name, Any};
7use std::fmt::Debug;
8
9use downcast_rs::{impl_downcast, 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 /// mergin 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 /// mergin 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 /// Return the number of stored attributes, i.e. the number of used slots in the storage (not
147 /// its length).
148 #[must_use = "unused return value"]
149 fn n_attributes(&self) -> usize;
150
151 /// Merge attributes to specified index
152 ///
153 /// # Arguments
154 ///
155 /// - `trans: &mut Transaction` -- Transaction used for synchronization.
156 /// - `out: DartIdentifier` -- Identifier to associate the result with.
157 /// - `lhs_inp: DartIdentifier` -- Identifier of one attribute value to merge.
158 /// - `rhs_inp: DartIdentifier` -- Identifier of the other attribute value to merge.
159 ///
160 /// # Behavior (pseudo-code)
161 ///
162 /// ```text
163 /// let new_val = match (attributes.remove(lhs_inp), attributes.remove(rhs_inp)) {
164 /// (Some(v1), Some(v2)) => AttributeUpdate::merge(v1, v2),
165 /// (Some(v), None) | (None, Some(v)) => AttributeUpdate::merge_undefined(Some(v)),
166 /// None, None => AttributeUpdate::merge_undefined(None),
167 /// }
168 /// attributes.set(out, new_val);
169 /// ```
170 ///
171 /// # Errors
172 ///
173 /// This method will fail, returning an error, if:
174 /// - the transaction cannot be completed
175 /// - the merge fails (e.g. because one merging value is missing)
176 ///
177 /// The returned error can be used in conjunction with transaction control to avoid any
178 /// modifications in case of failure at attribute level. The user can then choose, through its
179 /// transaction control policy, to retry or abort as he wishes.
180 fn merge(
181 &self,
182 trans: &mut Transaction,
183 out: DartIdType,
184 lhs_inp: DartIdType,
185 rhs_inp: DartIdType,
186 ) -> TransactionClosureResult<(), AttributeError>;
187
188 /// Split attribute to specified indices
189 ///
190 /// # Arguments
191 ///
192 /// - `trans: &mut Transaction` -- Transaction used for synchronization.
193 /// - `lhs_out: DartIdentifier` -- Identifier to associate the result with.
194 /// - `rhs_out: DartIdentifier` -- Identifier to associate the result with.
195 /// - `inp: DartIdentifier` -- Identifier of the attribute value to split.
196 ///
197 /// # Behavior pseudo-code
198 ///
199 /// ```text
200 /// (val_lhs, val_rhs) = AttributeUpdate::split(attributes.remove(inp).unwrap());
201 /// attributes[lhs_out] = val_lhs;
202 /// attributes[rhs_out] = val_rhs;
203 ///
204 /// ```
205 ///
206 /// # Errors
207 ///
208 /// This method will fail, returning an error, if:
209 /// - the transaction cannot be completed
210 /// - the split fails (e.g. because there is no value to split from)
211 ///
212 /// The returned error can be used in conjunction with transaction control to avoid any
213 /// modifications in case of failure at attribute level. The user can then choose, through its
214 /// transaction control policy, to retry or abort as he wishes.
215 fn split(
216 &self,
217 trans: &mut Transaction,
218 lhs_out: DartIdType,
219 rhs_out: DartIdType,
220 inp: DartIdType,
221 ) -> TransactionClosureResult<(), AttributeError>;
222}
223
224impl_downcast!(UnknownAttributeStorage);
225
226/// # Generic attribute storage trait
227///
228/// This trait defines attribute-specific methods. The documentation describes the expected behavior
229/// of each method. "ID" and "index" are used interchangeably.
230pub trait AttributeStorage<A: AttributeBind>: UnknownAttributeStorage {
231 #[allow(clippy::missing_errors_doc)]
232 /// Read the value of an element at a given index.
233 ///
234 /// # Arguments
235 ///
236 /// - `trans: &mut Transaction` -- Transaction used for synchronization.
237 /// - `index: A::IdentifierType` -- Cell index.
238 ///
239 /// # Return / Errors
240 ///
241 /// This method is meant to be called in a context where the returned `Result` is used to
242 /// validate the transaction passed as argument. Errors should not be processed manually,
243 /// only processed via the `?` operator.
244 ///
245 /// # Panics
246 ///
247 /// The method:
248 /// - should panic if the index lands out of bounds
249 /// - may panic if the index cannot be converted to `usize`
250 fn read(&self, trans: &mut Transaction, id: A::IdentifierType) -> StmClosureResult<Option<A>>;
251
252 #[allow(clippy::missing_errors_doc)]
253 /// Write the value of an element at a given index and return the old value.
254 ///
255 /// # Arguments
256 ///
257 /// - `trans: &mut Transaction` -- Transaction used for synchronization.
258 /// - `index: A::IdentifierType` -- Cell index.
259 /// - `val: A` -- Attribute value.
260 ///
261 /// # Return / Errors
262 ///
263 /// This method is meant to be called in a context where the returned `Result` is used to
264 /// validate the transaction passed as argument. Errors should not be processed manually,
265 /// only processed via the `?` operator.
266 ///
267 /// # Panics
268 ///
269 /// The method:
270 /// - should panic if the index lands out of bounds
271 /// - may panic if the index cannot be converted to `usize`
272 fn write(
273 &self,
274 trans: &mut Transaction,
275 id: A::IdentifierType,
276 val: A,
277 ) -> StmClosureResult<Option<A>>;
278
279 #[allow(clippy::missing_errors_doc)]
280 /// Remove the value at a given index and return it.
281 ///
282 /// # Arguments
283 ///
284 /// - `trans: &mut Transaction` -- Transaction used for synchronization.
285 /// - `index: A::IdentifierType` -- Cell index.
286 ///
287 /// # Return / Errors
288 ///
289 /// This method is meant to be called in a context where the returned `Result` is used to
290 /// validate the transaction passed as argument. Errors should not be processed manually,
291 /// only processed via the `?` operator.
292 ///
293 /// # Panics
294 ///
295 /// The method:
296 /// - should panic if the index lands out of bounds
297 /// - may panic if the index cannot be converted to `usize`
298 fn remove(&self, trans: &mut Transaction, id: A::IdentifierType)
299 -> StmClosureResult<Option<A>>;
300}