Skip to main content

shuttle_engine/runtime/task/
labels.rs

1//! Labels are a way to attach an arbitrary set of values with a task, with the only
2//! constraint being that at most one value of a given type `T` can be attached at any time,
3//! using `get::<T>()` to retrieve, and `set::<T>(..)` to set the value.
4//! Labels behave almost identically to [Extensions](https://docs.rs/http/latest/http/struct.Extensions.html)
5//! in the `http` crate.
6
7/*
8** The code below is directly copied from https://github.com/hyperium/http/blob/master/src/extensions.rs
9** but renaming 'Extensions' to 'Labels'
10**
11** The key idea is to keep a HashMap (named `AnyMap`) that maps the `TypeId` for a type
12** to its associated value, so a `get::<T>()` is translated to `get(TypeId::of::<T>())`.
13*/
14
15use std::any::{Any, TypeId};
16use std::collections::HashMap;
17use std::fmt;
18use std::hash::{BuildHasherDefault, Hasher};
19
20type AnyMap = HashMap<TypeId, Box<dyn AnyClone>, BuildHasherDefault<IdHasher>>;
21
22// With TypeIds as keys, there's no need to hash them. They are already hashes
23// themselves, coming from the compiler. The IdHasher just holds the u64 of
24// the TypeId, and then returns it, instead of doing any bit fiddling.
25#[derive(Default)]
26struct IdHasher(u64);
27
28impl Hasher for IdHasher {
29    fn write(&mut self, _: &[u8]) {
30        unreachable!("TypeId calls write_u64");
31    }
32
33    #[inline]
34    fn write_u64(&mut self, id: u64) {
35        self.0 = id;
36    }
37
38    #[inline]
39    fn finish(&self) -> u64 {
40        self.0
41    }
42}
43
44/// A collections of assigned Labels
45///
46/// `Labels` can be used to store extra data associated with running tasks.
47#[derive(Clone, Default)]
48pub struct Labels {
49    // If Labels are never used, no need to carry around an empty HashMap.
50    // That's 3 words. Instead, this is only 1 word.
51    map: Option<Box<AnyMap>>,
52}
53
54impl Labels {
55    /// Create an empty `Labels`.
56    #[inline]
57    pub fn new() -> Labels {
58        Labels { map: None }
59    }
60
61    /// Insert a type into this `Labels`.
62    ///
63    /// If a label of this type already existed, it will
64    /// be returned.
65    ///
66    /// # Example
67    ///
68    /// ```
69    /// # use shuttle_engine::current::Labels;
70    /// let mut ext = Labels::new();
71    /// assert!(ext.insert(5i32).is_none());
72    /// assert!(ext.insert(4u8).is_none());
73    /// assert_eq!(ext.insert(9i32), Some(5i32));
74    /// ```
75    pub fn insert<T: Clone + 'static>(&mut self, val: T) -> Option<T> {
76        self.map
77            .get_or_insert_with(Box::default)
78            .insert(TypeId::of::<T>(), Box::new(val))
79            .and_then(|boxed| boxed.into_any().downcast().ok().map(|boxed| *boxed))
80    }
81
82    /// Get a reference to a type previously inserted on this `Labels`.
83    ///
84    /// # Example
85    ///
86    /// ```
87    /// # use shuttle_engine::current::Labels;
88    /// let mut ext = Labels::new();
89    /// assert!(ext.get::<i32>().is_none());
90    /// ext.insert(5i32);
91    ///
92    /// assert_eq!(ext.get::<i32>(), Some(&5i32));
93    /// ```
94    pub fn get<T: 'static>(&self) -> Option<&T> {
95        self.map
96            .as_ref()
97            .and_then(|map| map.get(&TypeId::of::<T>()))
98            .and_then(|boxed| (**boxed).as_any().downcast_ref())
99    }
100
101    /// Get a mutable reference to a type previously inserted on this `Labels`.
102    ///
103    /// # Example
104    ///
105    /// ```
106    /// # use shuttle_engine::current::Labels;
107    /// let mut ext = Labels::new();
108    /// ext.insert(String::from("Hello"));
109    /// ext.get_mut::<String>().unwrap().push_str(" World");
110    ///
111    /// assert_eq!(ext.get::<String>().unwrap(), "Hello World");
112    /// ```
113    pub fn get_mut<T: 'static>(&mut self) -> Option<&mut T> {
114        self.map
115            .as_mut()
116            .and_then(|map| map.get_mut(&TypeId::of::<T>()))
117            .and_then(|boxed| (**boxed).as_any_mut().downcast_mut())
118    }
119
120    /// Get a mutable reference to a type, inserting `value` if not already present on this
121    /// `Labels`.
122    ///
123    /// # Example
124    ///
125    /// ```
126    /// # use shuttle_engine::current::Labels;
127    /// let mut ext = Labels::new();
128    /// *ext.get_or_insert(1i32) += 2;
129    ///
130    /// assert_eq!(*ext.get::<i32>().unwrap(), 3);
131    /// ```
132    pub fn get_or_insert<T: Clone + 'static>(&mut self, value: T) -> &mut T {
133        self.get_or_insert_with(|| value)
134    }
135
136    /// Get a mutable reference to a type, inserting the value created by `f` if not already present
137    /// on this `Labels`.
138    ///
139    /// # Example
140    ///
141    /// ```
142    /// # use shuttle_engine::current::Labels;
143    /// let mut ext = Labels::new();
144    /// *ext.get_or_insert_with(|| 1i32) += 2;
145    ///
146    /// assert_eq!(*ext.get::<i32>().unwrap(), 3);
147    /// ```
148    pub fn get_or_insert_with<T: Clone + 'static, F: FnOnce() -> T>(&mut self, f: F) -> &mut T {
149        let out = self
150            .map
151            .get_or_insert_with(Box::default)
152            .entry(TypeId::of::<T>())
153            .or_insert_with(|| Box::new(f()));
154        (**out).as_any_mut().downcast_mut().unwrap()
155    }
156
157    /// Get a mutable reference to a type, inserting the type's default value if not already present
158    /// on this `Labels`.
159    ///
160    /// # Example
161    ///
162    /// ```
163    /// # use shuttle_engine::current::Labels;
164    /// let mut ext = Labels::new();
165    /// *ext.get_or_insert_default::<i32>() += 2;
166    ///
167    /// assert_eq!(*ext.get::<i32>().unwrap(), 2);
168    /// ```
169    pub fn get_or_insert_default<T: Default + Clone + 'static>(&mut self) -> &mut T {
170        self.get_or_insert_with(T::default)
171    }
172
173    /// Remove a type from this `Labels`.
174    ///
175    /// If a label of this type existed, it will be returned.
176    ///
177    /// # Example
178    ///
179    /// ```
180    /// # use shuttle_engine::current::Labels;
181    /// let mut ext = Labels::new();
182    /// ext.insert(5i32);
183    /// assert_eq!(ext.remove::<i32>(), Some(5i32));
184    /// assert!(ext.get::<i32>().is_none());
185    /// ```
186    pub fn remove<T: 'static>(&mut self) -> Option<T> {
187        self.map
188            .as_mut()
189            .and_then(|map| map.remove(&TypeId::of::<T>()))
190            .and_then(|boxed| boxed.into_any().downcast().ok().map(|boxed| *boxed))
191    }
192
193    /// Clear all inserted labels
194    ///
195    /// # Example
196    ///
197    /// ```
198    /// # use shuttle_engine::current::Labels;
199    /// let mut ext = Labels::new();
200    /// ext.insert(5i32);
201    /// ext.clear();
202    ///
203    /// assert!(ext.get::<i32>().is_none());
204    /// ```
205    #[inline]
206    pub fn clear(&mut self) {
207        if let Some(ref mut map) = self.map {
208            map.clear();
209        }
210    }
211
212    /// Check whether the label set is empty or not.
213    ///
214    /// # Example
215    ///
216    /// ```
217    /// # use shuttle_engine::current::Labels;
218    /// let mut ext = Labels::new();
219    /// assert!(ext.is_empty());
220    /// ext.insert(5i32);
221    /// assert!(!ext.is_empty());
222    /// ```
223    #[inline]
224    pub fn is_empty(&self) -> bool {
225        self.map.as_ref().is_none_or(|map| map.is_empty())
226    }
227
228    /// Get the number of Labels available.
229    ///
230    /// # Example
231    ///
232    /// ```
233    /// # use shuttle_engine::current::Labels;
234    /// let mut ext = Labels::new();
235    /// assert_eq!(ext.len(), 0);
236    /// ext.insert(5i32);
237    /// assert_eq!(ext.len(), 1);
238    /// ```
239    #[inline]
240    pub fn len(&self) -> usize {
241        self.map.as_ref().map_or(0, |map| map.len())
242    }
243
244    /// Extends `self` with another `Labels`.
245    ///
246    /// If an instance of a specific type exists in both, the one in `self` is overwritten with the
247    /// one from `other`.
248    ///
249    /// # Example
250    ///
251    /// ```
252    /// # use shuttle_engine::current::Labels;
253    /// let mut ext_a = Labels::new();
254    /// ext_a.insert(8u8);
255    /// ext_a.insert(16u16);
256    ///
257    /// let mut ext_b = Labels::new();
258    /// ext_b.insert(4u8);
259    /// ext_b.insert("hello");
260    ///
261    /// ext_a.extend(ext_b);
262    /// assert_eq!(ext_a.len(), 3);
263    /// assert_eq!(ext_a.get::<u8>(), Some(&4u8));
264    /// assert_eq!(ext_a.get::<u16>(), Some(&16u16));
265    /// assert_eq!(ext_a.get::<&'static str>().copied(), Some("hello"));
266    /// ```
267    pub fn extend(&mut self, other: Self) {
268        if let Some(other) = other.map {
269            if let Some(map) = &mut self.map {
270                map.extend(*other);
271            } else {
272                self.map = Some(other);
273            }
274        }
275    }
276}
277
278impl fmt::Debug for Labels {
279    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
280        f.debug_struct("Labels").finish()
281    }
282}
283
284trait AnyClone: Any {
285    fn clone_box(&self) -> Box<dyn AnyClone>;
286    fn as_any(&self) -> &dyn Any;
287    fn as_any_mut(&mut self) -> &mut dyn Any;
288    fn into_any(self: Box<Self>) -> Box<dyn Any>;
289}
290
291impl<T: Clone + 'static> AnyClone for T {
292    fn clone_box(&self) -> Box<dyn AnyClone> {
293        Box::new(self.clone())
294    }
295
296    fn as_any(&self) -> &dyn Any {
297        self
298    }
299
300    fn as_any_mut(&mut self) -> &mut dyn Any {
301        self
302    }
303
304    fn into_any(self: Box<Self>) -> Box<dyn Any> {
305        self
306    }
307}
308
309impl Clone for Box<dyn AnyClone> {
310    fn clone(&self) -> Self {
311        (**self).clone_box()
312    }
313}
314
315#[test]
316fn test_labels() {
317    #[derive(Clone, Debug, PartialEq)]
318    struct MyType(i32);
319
320    let mut labels = Labels::new();
321
322    labels.insert(5i32);
323    labels.insert(MyType(10));
324
325    assert_eq!(labels.get(), Some(&5i32));
326    assert_eq!(labels.get_mut(), Some(&mut 5i32));
327
328    let ext2 = labels.clone();
329
330    assert_eq!(labels.remove::<i32>(), Some(5i32));
331    assert!(labels.get::<i32>().is_none());
332
333    // clone still has it
334    assert_eq!(ext2.get(), Some(&5i32));
335    assert_eq!(ext2.get(), Some(&MyType(10)));
336
337    assert_eq!(labels.get::<bool>(), None);
338    assert_eq!(labels.get(), Some(&MyType(10)));
339}