type_set/
lib.rs

1#![deny(
2    clippy::dbg_macro,
3    missing_copy_implementations,
4    rustdoc::missing_crate_level_docs,
5    missing_debug_implementations,
6    nonstandard_style,
7    unused_qualifications
8)]
9#![warn(missing_docs, clippy::pedantic, clippy::perf, clippy::cargo)]
10#![allow(clippy::missing_panics_doc, clippy::module_name_repetitions)]
11/*!
12
13[`TypeSet`] is a collection for heterogeneous types. Each type can only exist once in the set, and
14can only be retrieved by naming the type.
15
16Because types can only be retrieved by naming them, rust's module system allows module-private
17storage in a shared `TypeSet`.
18
19Currently, this crate imposes `Send + Sync` bounds on the stored types, but future versions may
20offer variants without those bounds and/or with Clone bounds.
21
22Implementation is based on
23- <https://github.com/hyperium/http/blob/master/src/extensions.rs>
24- <https://github.com/kardeiz/type-map/blob/master/src/lib.rs>
25- <https://github.com/http-rs/http-types/blob/main/src/extensions.rs>
26*/
27use std::{
28    any::{type_name, Any, TypeId},
29    collections::BTreeMap,
30    fmt::{self, Debug, Formatter},
31};
32
33/// Types for interacting with a mutable view into a `TypeSet` for a given type
34pub mod entry;
35use entry::Entry;
36
37struct Value {
38    any: Box<dyn Any + Send + Sync>,
39    name: &'static str,
40}
41
42impl Value {
43    fn new<T: Any + Send + Sync + 'static>(value: T) -> Self {
44        Self {
45            any: Box::new(value),
46            name: type_name::<T>(),
47        }
48    }
49
50    fn downcast_mut<T: Any + Send + Sync + 'static>(&mut self) -> Option<&mut T> {
51        debug_assert_eq!(type_name::<T>(), self.name);
52        self.any.downcast_mut()
53    }
54
55    fn downcast<T: Any + Send + Sync + 'static>(self) -> Option<T> {
56        debug_assert_eq!(type_name::<T>(), self.name);
57        self.any.downcast().map(|t| *t).ok()
58    }
59
60    fn downcast_ref<T: Any + Send + Sync + 'static>(&self) -> Option<&T> {
61        debug_assert_eq!(type_name::<T>(), self.name);
62        self.any.downcast_ref()
63    }
64}
65
66type Key = TypeId;
67
68macro_rules! unwrap {
69    ($x:expr) => {
70        match $x {
71            #[cfg(debug_assertions)]
72            x => x.unwrap(),
73            #[cfg(not(debug_assertions))]
74            x => unsafe { x.unwrap_unchecked() },
75        }
76    };
77}
78use unwrap;
79
80/// A collection for heterogenous types
81///
82/// Note that there is currently no way to iterate over the collection, as there may be types stored
83/// that cannot be named by the calling code
84#[derive(Default)]
85pub struct TypeSet(BTreeMap<Key, Value>);
86
87fn field_with(f: impl Fn(&mut Formatter) -> fmt::Result) -> impl Debug {
88    struct DebugWith<F>(F);
89
90    impl<F> Debug for DebugWith<F>
91    where
92        F: Fn(&mut Formatter) -> fmt::Result,
93    {
94        fn fmt(&self, f: &mut Formatter) -> fmt::Result {
95            self.0(f)
96        }
97    }
98
99    DebugWith(f)
100}
101
102impl Debug for TypeSet {
103    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
104        f.debug_tuple("TypeSet")
105            .field(&field_with(|f| {
106                let mut values = self.0.values().map(|v| v.name).collect::<Vec<_>>();
107                values.sort_unstable();
108                f.debug_set().entries(values).finish()
109            }))
110            .finish()
111    }
112}
113
114fn key<T: 'static>() -> Key {
115    TypeId::of::<T>()
116}
117
118impl TypeSet {
119    /// Create an empty `TypeSet`.
120    #[must_use]
121    pub const fn new() -> Self {
122        Self(BTreeMap::new())
123    }
124
125    /// Returns true if the `TypeSet` contains zero types.
126    #[must_use]
127    pub fn is_empty(&self) -> bool {
128        self.0.is_empty()
129    }
130
131    /// Returns the number of distinct types in this `TypeSet`.
132    #[must_use]
133    pub fn len(&self) -> usize {
134        self.0.len()
135    }
136
137    /// Gets the corresponding type in the set for in-place manipulation.
138    ///
139    /// See [`Entry`] for usage.
140    pub fn entry<T: Send + Sync + 'static>(&mut self) -> Entry<'_, T> {
141        Entry::new(self.0.entry(key::<T>()))
142    }
143
144    /// Insert a value into this `TypeSet`.
145    ///
146    /// If a value of this type already exists, it will be replaced and returned.
147    ///
148    /// ## Example
149    /// ```rust
150    /// let mut set = type_set::TypeSet::new().with("hello");
151    /// let previous = set.insert("world");
152    /// assert_eq!(set.get::<&'static str>(), Some(&"world"));
153    /// assert_eq!(previous, Some("hello"));
154    /// ```
155    pub fn insert<T: Send + Sync + 'static>(&mut self, value: T) -> Option<T> {
156        self.entry().insert(value)
157    }
158
159    /// Chainable constructor to add a type to this `TypeSet`
160    ///
161    /// ## Example
162    /// ```rust
163    /// let set = type_set::TypeSet::new().with("hello");
164    /// assert_eq!(set.get::<&'static str>(), Some(&"hello"));
165    /// ```
166    #[must_use]
167    pub fn with<T: Send + Sync + 'static>(mut self, value: T) -> Self {
168        self.insert(value);
169        self
170    }
171
172    /// Check if this `TypeSet` contains a value for type T
173    ///
174    /// ## Example
175    ///
176    /// ```rust
177    /// let set = type_set::TypeSet::new().with("hello");
178    /// assert!(set.contains::<&'static str>());
179    /// assert!(!set.contains::<String>());
180    /// ```
181    #[must_use]
182    pub fn contains<T: Send + Sync + 'static>(&self) -> bool {
183        #[cfg(feature = "log")]
184        log::trace!(
185            "contains {}?: {}",
186            type_name::<T>(),
187            self.0.contains_key(&TypeId::of::<T>())
188        );
189        self.0.contains_key(&key::<T>())
190    }
191
192    /// Immutably borrow a value that has been inserted into this `TypeSet`.
193    #[must_use]
194    pub fn get<T: Send + Sync + 'static>(&self) -> Option<&T> {
195        #[cfg(feature = "log")]
196        log::trace!("getting {}", type_name::<T>(),);
197        self.0
198            .get(&key::<T>())
199            .map(|value| unwrap!(value.downcast_ref()))
200    }
201
202    /// Attempt to mutably borrow to a value that has been inserted into this `TypeSet`.
203    ///
204    /// ## Example
205    ///
206    /// ```rust
207    /// let mut set = type_set::TypeSet::new().with(String::from("hello"));
208    /// if let Some(string) = set.get_mut::<String>() {
209    ///     string.push_str(" world");
210    /// }
211    /// assert_eq!(set.get::<String>().unwrap(), "hello world");
212    /// ```
213    pub fn get_mut<T: Send + Sync + 'static>(&mut self) -> Option<&mut T> {
214        self.0
215            .get_mut(&key::<T>())
216            .map(|value| unwrap!(value.downcast_mut()))
217    }
218
219    /// Remove a value from this `TypeSet`.
220    ///
221    /// If a value of this type exists, it will be returned.
222    ///
223    /// ## Example
224    ///
225    /// ```rust
226    /// let mut set = type_set::TypeSet::new().with("hello");
227    /// assert_eq!(set.take::<&'static str>(), Some("hello"));
228    /// assert_eq!(set.take::<&'static str>(), None);
229    /// ```
230    pub fn take<T: Send + Sync + 'static>(&mut self) -> Option<T> {
231        self.entry().take()
232    }
233
234    /// Get a value from this `TypeSet` or populate it with the provided default.
235    ///
236    /// Identical to [`Entry::or_insert`]
237    ///
238    /// If building T is expensive, use [`TypeSet::get_or_insert_with`] or [`Entry::or_insert_with`]
239    ///
240    /// ## Example
241    ///
242    /// ```rust
243    /// let mut set = type_set::TypeSet::new();
244    /// assert_eq!(set.get_or_insert("hello"), &mut "hello");
245    /// assert_eq!(set.get_or_insert("world"), &mut "hello");
246    /// ```
247    pub fn get_or_insert<T: Send + Sync + 'static>(&mut self, default: T) -> &mut T {
248        self.entry().or_insert(default)
249    }
250
251    /// Get a value from this `TypeSet` or populate it with the provided default function.
252    ///
253    /// Identical to [`Entry::or_insert_with`]
254    ///
255    /// Prefer this to [`TypeSet::get_or_insert`] when building type T is expensive, since it will only be
256    /// executed when T is absent.
257    ///
258    /// ## Example
259    ///
260    /// ```rust
261    /// let mut set = type_set::TypeSet::new();
262    /// assert_eq!(set.get_or_insert_with(|| String::from("hello")), "hello");
263    /// assert_eq!(set.get_or_insert_with::<String>(|| panic!("this is never called")), "hello");
264    /// ```
265    pub fn get_or_insert_with<T: Send + Sync + 'static>(
266        &mut self,
267        default: impl FnOnce() -> T,
268    ) -> &mut T {
269        self.entry().or_insert_with(default)
270    }
271
272    /// Ensure a value is present by filling with [`Default::default`]
273    ///
274    /// Identical to [`Entry::or_default`].
275    ///
276    /// ## Example
277    ///
278    /// ```rust
279    /// let mut set = type_set::TypeSet::new().with(10usize);
280    /// let ten: usize = *set.get_or_insert_default();
281    /// assert_eq!(ten, 10);
282    /// ```
283    pub fn get_or_insert_default<T: Default + Send + Sync + 'static>(&mut self) -> &mut T {
284        self.entry().or_default()
285    }
286
287    /// Merge another `TypeSet` into this one, replacing any collisions
288    ///
289    ///
290    /// ## Example
291    ///
292    /// ```rust
293    /// let mut set_a = type_set::TypeSet::new().with(8u8).with("hello");
294    /// let set_b = type_set::TypeSet::new().with(32u32).with("world");
295    /// set_a.merge(set_b);
296    /// assert_eq!(set_a.get::<u8>(), Some(&8));
297    /// assert_eq!(set_a.get::<u32>(), Some(&32));
298    /// assert_eq!(set_a.get::<&'static str>(), Some(&"world"));
299    /// ```
300    pub fn merge(&mut self, other: TypeSet) {
301        self.0.extend(other.0);
302    }
303}