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::HashMap,
30 fmt::{self, Debug, Formatter},
31 hash::{BuildHasherDefault, Hasher},
32};
33
34/// Types for interacting with a mutable view into a `TypeSet` for a given type
35pub mod entry;
36use entry::Entry;
37
38struct Value {
39 any: Box<dyn Any + Send + Sync>,
40 name: &'static str,
41}
42
43impl Value {
44 fn new<T: Any + Send + Sync + 'static>(value: T) -> Self {
45 Self {
46 any: Box::new(value),
47 name: type_name::<T>(),
48 }
49 }
50
51 fn downcast_mut<T: Any + Send + Sync + 'static>(&mut self) -> Option<&mut T> {
52 debug_assert_eq!(type_name::<T>(), self.name);
53 self.any.downcast_mut()
54 }
55
56 fn downcast<T: Any + Send + Sync + 'static>(self) -> Option<T> {
57 debug_assert_eq!(type_name::<T>(), self.name);
58 self.any.downcast().map(|t| *t).ok()
59 }
60
61 fn downcast_ref<T: Any + Send + Sync + 'static>(&self) -> Option<&T> {
62 debug_assert_eq!(type_name::<T>(), self.name);
63 self.any.downcast_ref()
64 }
65}
66
67type Key = TypeId;
68
69/// A [`Hasher`] that uses a [`TypeId`]'s value directly as the hash.
70///
71/// `TypeId` is already a high-quality hash, so feeding it through a general-purpose hasher would be
72/// wasted work and would slow down every lookup. This hasher simply captures the value `TypeId`
73/// emits. `TypeId` currently hashes itself with a single `write_u64`, but `write_u128` and the
74/// `write` byte-slice path are implemented as well so that a future change to `TypeId`'s
75/// representation cannot silently produce a constant hash.
76#[derive(Default)]
77struct IdHasher(u64);
78
79impl Hasher for IdHasher {
80 fn finish(&self) -> u64 {
81 self.0
82 }
83
84 fn write(&mut self, bytes: &[u8]) {
85 for &byte in bytes {
86 self.0 = self.0.rotate_left(8) ^ u64::from(byte);
87 }
88 }
89
90 fn write_u64(&mut self, value: u64) {
91 self.0 = value;
92 }
93
94 #[allow(clippy::cast_possible_truncation)]
95 fn write_u128(&mut self, value: u128) {
96 self.0 = (value as u64) ^ ((value >> 64) as u64);
97 }
98}
99
100type Map = HashMap<Key, Value, BuildHasherDefault<IdHasher>>;
101
102macro_rules! unwrap {
103 ($x:expr) => {
104 match $x {
105 #[cfg(debug_assertions)]
106 x => x.unwrap(),
107 #[cfg(not(debug_assertions))]
108 x => unsafe { x.unwrap_unchecked() },
109 }
110 };
111}
112use unwrap;
113
114/// A collection for heterogenous types
115///
116/// Note that there is currently no way to iterate over the collection, as there may be types stored
117/// that cannot be named by the calling code
118#[derive(Default)]
119pub struct TypeSet(Map);
120
121fn field_with(f: impl Fn(&mut Formatter) -> fmt::Result) -> impl Debug {
122 struct DebugWith<F>(F);
123
124 impl<F> Debug for DebugWith<F>
125 where
126 F: Fn(&mut Formatter) -> fmt::Result,
127 {
128 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
129 self.0(f)
130 }
131 }
132
133 DebugWith(f)
134}
135
136impl Debug for TypeSet {
137 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
138 f.debug_tuple("TypeSet")
139 .field(&field_with(|f| {
140 let mut values = self.0.values().map(|v| v.name).collect::<Vec<_>>();
141 values.sort_unstable();
142 f.debug_set().entries(values).finish()
143 }))
144 .finish()
145 }
146}
147
148fn key<T: 'static>() -> Key {
149 TypeId::of::<T>()
150}
151
152impl TypeSet {
153 /// Create an empty `TypeSet`.
154 #[must_use]
155 pub const fn new() -> Self {
156 Self(HashMap::with_hasher(BuildHasherDefault::new()))
157 }
158
159 /// Create an empty `TypeSet` with space for at least `capacity` distinct types.
160 ///
161 /// Preallocating avoids the incremental reallocations that occur as types are inserted, which is
162 /// worthwhile when the number of types is known ahead of time or when reusing a `TypeSet` across
163 /// many fill/[`clear`](TypeSet::clear) cycles.
164 ///
165 /// ## Example
166 ///
167 /// ```rust
168 /// let mut set = type_set::TypeSet::with_capacity(2);
169 /// set.insert("hello");
170 /// set.insert(1usize);
171 /// assert_eq!(set.get::<&'static str>(), Some(&"hello"));
172 /// ```
173 #[must_use]
174 pub fn with_capacity(capacity: usize) -> Self {
175 Self(HashMap::with_capacity_and_hasher(
176 capacity,
177 BuildHasherDefault::new(),
178 ))
179 }
180
181 /// Remove all types from this `TypeSet`, retaining the allocated capacity for reuse.
182 ///
183 /// This is the cheap way to reuse a `TypeSet` allocation: clearing and refilling avoids
184 /// reallocating the backing storage that constructing a fresh `TypeSet` would require.
185 ///
186 /// ## Example
187 ///
188 /// ```rust
189 /// let mut set = type_set::TypeSet::new().with("hello").with(1usize);
190 /// set.clear();
191 /// assert!(set.is_empty());
192 /// assert_eq!(set.get::<&'static str>(), None);
193 /// ```
194 pub fn clear(&mut self) {
195 self.0.clear();
196 }
197
198 /// Reserve capacity for at least `additional` more distinct types.
199 ///
200 /// ## Example
201 ///
202 /// ```rust
203 /// let mut set = type_set::TypeSet::new();
204 /// set.reserve(4);
205 /// set.insert("hello");
206 /// ```
207 pub fn reserve(&mut self, additional: usize) {
208 self.0.reserve(additional);
209 }
210
211 /// Shrink the capacity of this `TypeSet` as much as possible.
212 ///
213 /// ## Example
214 ///
215 /// ```rust
216 /// let mut set = type_set::TypeSet::with_capacity(16);
217 /// set.insert("hello");
218 /// set.shrink_to_fit();
219 /// assert_eq!(set.get::<&'static str>(), Some(&"hello"));
220 /// ```
221 pub fn shrink_to_fit(&mut self) {
222 self.0.shrink_to_fit();
223 }
224
225 /// Returns true if the `TypeSet` contains zero types.
226 #[must_use]
227 pub fn is_empty(&self) -> bool {
228 self.0.is_empty()
229 }
230
231 /// Returns the number of distinct types in this `TypeSet`.
232 #[must_use]
233 pub fn len(&self) -> usize {
234 self.0.len()
235 }
236
237 /// Gets the corresponding type in the set for in-place manipulation.
238 ///
239 /// See [`Entry`] for usage.
240 pub fn entry<T: Send + Sync + 'static>(&mut self) -> Entry<'_, T> {
241 Entry::new(self.0.entry(key::<T>()))
242 }
243
244 /// Insert a value into this `TypeSet`.
245 ///
246 /// If a value of this type already exists, it will be replaced and returned.
247 ///
248 /// ## Example
249 /// ```rust
250 /// let mut set = type_set::TypeSet::new().with("hello");
251 /// let previous = set.insert("world");
252 /// assert_eq!(set.get::<&'static str>(), Some(&"world"));
253 /// assert_eq!(previous, Some("hello"));
254 /// ```
255 pub fn insert<T: Send + Sync + 'static>(&mut self, value: T) -> Option<T> {
256 self.entry().insert(value)
257 }
258
259 /// Chainable constructor to add a type to this `TypeSet`
260 ///
261 /// ## Example
262 /// ```rust
263 /// let set = type_set::TypeSet::new().with("hello");
264 /// assert_eq!(set.get::<&'static str>(), Some(&"hello"));
265 /// ```
266 #[must_use]
267 pub fn with<T: Send + Sync + 'static>(mut self, value: T) -> Self {
268 self.insert(value);
269 self
270 }
271
272 /// Check if this `TypeSet` contains a value for type T
273 ///
274 /// ## Example
275 ///
276 /// ```rust
277 /// let set = type_set::TypeSet::new().with("hello");
278 /// assert!(set.contains::<&'static str>());
279 /// assert!(!set.contains::<String>());
280 /// ```
281 #[must_use]
282 pub fn contains<T: Send + Sync + 'static>(&self) -> bool {
283 #[cfg(feature = "log")]
284 log::trace!(
285 "contains {}?: {}",
286 type_name::<T>(),
287 self.0.contains_key(&TypeId::of::<T>())
288 );
289 self.0.contains_key(&key::<T>())
290 }
291
292 /// Immutably borrow a value that has been inserted into this `TypeSet`.
293 #[must_use]
294 pub fn get<T: Send + Sync + 'static>(&self) -> Option<&T> {
295 #[cfg(feature = "log")]
296 log::trace!("getting {}", type_name::<T>());
297 self.0
298 .get(&key::<T>())
299 .map(|value| unwrap!(value.downcast_ref()))
300 }
301
302 /// Attempt to mutably borrow to a value that has been inserted into this `TypeSet`.
303 ///
304 /// ## Example
305 ///
306 /// ```rust
307 /// let mut set = type_set::TypeSet::new().with(String::from("hello"));
308 /// if let Some(string) = set.get_mut::<String>() {
309 /// string.push_str(" world");
310 /// }
311 /// assert_eq!(set.get::<String>().unwrap(), "hello world");
312 /// ```
313 pub fn get_mut<T: Send + Sync + 'static>(&mut self) -> Option<&mut T> {
314 self.0
315 .get_mut(&key::<T>())
316 .map(|value| unwrap!(value.downcast_mut()))
317 }
318
319 /// Remove a value from this `TypeSet`.
320 ///
321 /// If a value of this type exists, it will be returned.
322 ///
323 /// ## Example
324 ///
325 /// ```rust
326 /// let mut set = type_set::TypeSet::new().with("hello");
327 /// assert_eq!(set.take::<&'static str>(), Some("hello"));
328 /// assert_eq!(set.take::<&'static str>(), None);
329 /// ```
330 pub fn take<T: Send + Sync + 'static>(&mut self) -> Option<T> {
331 self.entry().take()
332 }
333
334 /// Get a value from this `TypeSet` or populate it with the provided default.
335 ///
336 /// Identical to [`Entry::or_insert`]
337 ///
338 /// If building T is expensive, use [`TypeSet::get_or_insert_with`] or [`Entry::or_insert_with`]
339 ///
340 /// ## Example
341 ///
342 /// ```rust
343 /// let mut set = type_set::TypeSet::new();
344 /// assert_eq!(set.get_or_insert("hello"), &mut "hello");
345 /// assert_eq!(set.get_or_insert("world"), &mut "hello");
346 /// ```
347 pub fn get_or_insert<T: Send + Sync + 'static>(&mut self, default: T) -> &mut T {
348 self.entry().or_insert(default)
349 }
350
351 /// Get a value from this `TypeSet` or populate it with the provided default function.
352 ///
353 /// Identical to [`Entry::or_insert_with`]
354 ///
355 /// Prefer this to [`TypeSet::get_or_insert`] when building type T is expensive, since it will only be
356 /// executed when T is absent.
357 ///
358 /// ## Example
359 ///
360 /// ```rust
361 /// let mut set = type_set::TypeSet::new();
362 /// assert_eq!(set.get_or_insert_with(|| String::from("hello")), "hello");
363 /// assert_eq!(set.get_or_insert_with::<String>(|| panic!("this is never called")), "hello");
364 /// ```
365 pub fn get_or_insert_with<T: Send + Sync + 'static>(
366 &mut self,
367 default: impl FnOnce() -> T,
368 ) -> &mut T {
369 self.entry().or_insert_with(default)
370 }
371
372 /// Ensure a value is present by filling with [`Default::default`]
373 ///
374 /// Identical to [`Entry::or_default`].
375 ///
376 /// ## Example
377 ///
378 /// ```rust
379 /// let mut set = type_set::TypeSet::new().with(10usize);
380 /// let ten: usize = *set.get_or_insert_default();
381 /// assert_eq!(ten, 10);
382 /// ```
383 pub fn get_or_insert_default<T: Default + Send + Sync + 'static>(&mut self) -> &mut T {
384 self.entry().or_default()
385 }
386
387 /// Merge another `TypeSet` into this one, replacing any collisions
388 ///
389 ///
390 /// ## Example
391 ///
392 /// ```rust
393 /// let mut set_a = type_set::TypeSet::new().with(8u8).with("hello");
394 /// let set_b = type_set::TypeSet::new().with(32u32).with("world");
395 /// set_a.merge(set_b);
396 /// assert_eq!(set_a.get::<u8>(), Some(&8));
397 /// assert_eq!(set_a.get::<u32>(), Some(&32));
398 /// assert_eq!(set_a.get::<&'static str>(), Some(&"world"));
399 /// ```
400 pub fn merge(&mut self, other: TypeSet) {
401 self.0.extend(other.0);
402 }
403}