broomdog/
lib.rs

1//! # broomdog ๐Ÿงน๐Ÿ•
2//!
3//! `broomdog` is a Rust library providing a type map.
4//!
5//! ## what is a type map?
6//!
7//! `broomdog`'s type map is a map of `std::any::TypeId` keys to type-erased
8//! values. There may be at most one value for each key, which means the map
9//! stores singleton values of different types.
10//!
11//! ## type values
12//! Values of `TypeMap` may be any value that is `Any + Send + Sync`.
13use rustc_hash::FxHashMap;
14use snafu::prelude::*;
15
16use std::{
17    any::{Any, TypeId},
18    ops::{Deref, DerefMut},
19    sync::{Arc, Mutex},
20};
21
22#[cfg(doctest)]
23pub mod doctest {
24    #[doc = include_str!("../README.md")]
25    pub struct ReadmeDoctests;
26}
27
28#[derive(Debug, Snafu)]
29pub enum BroomdogErr {
30    #[snafu(display("Cannot downcast_ref from {} to {to}", from.unwrap_or_else(|| "unknown")))]
31    DowncastRef {
32        from: Option<&'static str>,
33        to: &'static str,
34    },
35
36    #[snafu(display("Cannot downcast_mut from {} to {to}", from.unwrap_or_else(|| "unknown")))]
37    DowncastMut {
38        from: Option<&'static str>,
39        to: &'static str,
40    },
41
42    #[snafu(display("The type of a new value '{new_type}' doesn't match the type of an old value '{}'", old_type.unwrap_or_else(|| "unknown")))]
43    Mismatch {
44        new_type: &'static str,
45        old_type: Option<&'static str>,
46    },
47
48    #[snafu(display("Values are still on loan:  {}", loans.iter().map(|k| k.name()).collect::<Vec<_>>().join(", ")))]
49    Unify { loans: Vec<TypeKey> },
50
51    #[snafu(display("Type {name} is already exclusively loaned"))]
52    ExclusiveLoan { name: &'static str },
53
54    #[snafu(display("Type {name} is already loaned"))]
55    Loan { name: &'static str },
56
57    #[snafu(display("Types are still loaned: \n{}", names.concat()))]
58    ManyLoans { names: Vec<&'static str> },
59}
60
61/// A key for a type-erased value.
62#[derive(Clone, Copy, Debug)]
63pub struct TypeKey {
64    type_id: TypeId,
65    #[cfg(debug_assertions)]
66    type_name: &'static str,
67}
68
69impl std::fmt::Display for TypeKey {
70    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
71        #[cfg(debug_assertions)]
72        {
73            f.write_str(self.type_name)
74        }
75        #[cfg(not(debug_assertions))]
76        {
77            f.write_str("_")
78        }
79    }
80}
81
82impl std::hash::Hash for TypeKey {
83    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
84        self.type_id.hash(state);
85    }
86}
87
88impl PartialEq for TypeKey {
89    fn eq(&self, other: &Self) -> bool {
90        self.type_id == other.type_id
91    }
92}
93
94impl Eq for TypeKey {}
95
96impl TypeKey {
97    /// Create a new `TypeKey`.
98    pub fn new<T: Any + Send + Sync>() -> Self {
99        TypeKey {
100            type_id: TypeId::of::<T>(),
101            #[cfg(debug_assertions)]
102            type_name: std::any::type_name::<T>(),
103        }
104    }
105
106    /// Returns the name of the type, if compiled with `debug_assertions`,
107    /// otherwise `"unknown"`.
108    pub const fn name(&self) -> &'static str {
109        #[cfg(debug_assertions)]
110        {
111            self.type_name
112        }
113        #[cfg(not(debug_assertions))]
114        {
115            "unknown"
116        }
117    }
118}
119
120/// A type-erased value.
121#[derive(Debug)]
122pub struct TypeValue {
123    inner: Box<dyn Any + Send + Sync>,
124    #[cfg(debug_assertions)]
125    type_name: &'static str,
126}
127
128impl<T: Any + Send + Sync> From<Box<T>> for TypeValue {
129    fn from(inner: Box<T>) -> Self {
130        TypeValue {
131            inner,
132            #[cfg(debug_assertions)]
133            type_name: std::any::type_name::<T>(),
134        }
135    }
136}
137
138impl TypeValue {
139    /// Create a new type value from a value.
140    pub fn new<T: Any + Send + Sync>(value: T) -> Self {
141        TypeValue::from(Box::new(value))
142    }
143
144    /// Return the name of the type this value holds, if possible.
145    ///
146    /// This only returns `Some` when compiled with `debug_assertions`.
147    pub fn type_name(&self) -> Option<&'static str> {
148        #[cfg(debug_assertions)]
149        {
150            Some(self.type_name)
151        }
152        #[cfg(not(debug_assertions))]
153        {
154            None
155        }
156    }
157
158    /// Attempt to downcast the value to the given parameterized type,
159    /// if the downcast fails (because the type givin doesn't match) the
160    /// original `TypeValue` will be returned wrapped in `Err`.
161    pub fn downcast<T: Any + Send + Sync + 'static>(
162        self,
163    ) -> std::result::Result<Box<T>, TypeValue> {
164        self.inner.downcast::<T>().map_err(|inner| TypeValue {
165            inner,
166            #[cfg(debug_assertions)]
167            type_name: self.type_name,
168        })
169    }
170
171    /// Attempt to downcast a reference to the given parameterized type.
172    pub fn downcast_ref<T: Any + Send + Sync + 'static>(
173        &self,
174    ) -> std::result::Result<&T, BroomdogErr> {
175        self.inner
176            .downcast_ref::<T>()
177            .with_context(|| DowncastRefSnafu {
178                from: self.type_name(),
179                to: std::any::type_name::<T>(),
180            })
181    }
182
183    /// Attempt to downcast a mutable reference to the given parameterized type.
184    pub fn downcast_mut<T: Any + Send + Sync + 'static>(
185        &mut self,
186    ) -> std::result::Result<&mut T, BroomdogErr> {
187        let from = self.type_name();
188        self.inner
189            .downcast_mut::<T>()
190            .with_context(move || DowncastMutSnafu {
191                from,
192                to: std::any::type_name::<T>(),
193            })
194    }
195}
196
197/// A shared, type-erased value.
198pub struct Loan {
199    inner: Arc<TypeValue>,
200}
201
202impl Deref for Loan {
203    type Target = TypeValue;
204
205    fn deref(&self) -> &Self::Target {
206        &self.inner
207    }
208}
209
210/// An exclusive type-erased value.
211///
212/// When the loan is dropped the inner value is sent back to the [`TypeMap`] it
213/// originated from.
214pub struct LoanMut {
215    inner: Option<TypeValue>,
216    outer: Arc<Mutex<Option<TypeValue>>>,
217}
218
219impl Drop for LoanMut {
220    fn drop(&mut self) {
221        // UNWRAP: safe because this is the only time we take
222        let inner = self.inner.take().unwrap();
223        let mut outer = self.outer.lock().unwrap();
224        *outer = Some(inner);
225    }
226}
227
228impl Deref for LoanMut {
229    type Target = TypeValue;
230
231    fn deref(&self) -> &Self::Target {
232        // UNWRAP: safe because inner is only `None` _after_ `drop`
233        self.inner.as_ref().unwrap()
234    }
235}
236
237impl DerefMut for LoanMut {
238    fn deref_mut(&mut self) -> &mut Self::Target {
239        // UNWRAP: safe because inner is only `None` _after_ `drop`
240        self.inner.as_mut().unwrap()
241    }
242}
243
244/// The internal state of a [`TypeValue`] within a [`TypeMap`].
245#[derive(Debug)]
246pub enum InnerLoan {
247    // The type is available for any kind of loan.
248    Owned(TypeValue),
249    // The type has been loaned but can be loaned again.
250    Loan(Arc<TypeValue>),
251    // The type has been loaned exclusively and cannot be loaned again until unified.
252    LoanMut(Arc<Mutex<Option<TypeValue>>>),
253}
254
255impl From<TypeValue> for InnerLoan {
256    fn from(value: TypeValue) -> Self {
257        InnerLoan::Owned(value)
258    }
259}
260
261impl InnerLoan {
262    pub fn is_owned(&self) -> bool {
263        matches!(self, InnerLoan::Owned(_))
264    }
265
266    pub fn as_owned(&self, name: &'static str) -> std::result::Result<&TypeValue, BroomdogErr> {
267        match self {
268            InnerLoan::Owned(a) => Ok(a),
269            InnerLoan::Loan(_) => Err(BroomdogErr::Loan { name }),
270            InnerLoan::LoanMut(_) => Err(BroomdogErr::ExclusiveLoan { name }),
271        }
272    }
273
274    pub fn as_owned_mut(
275        &mut self,
276        name: &'static str,
277    ) -> std::result::Result<&mut TypeValue, BroomdogErr> {
278        match self {
279            InnerLoan::Owned(a) => Ok(a),
280            InnerLoan::Loan(_) => Err(BroomdogErr::Loan { name }),
281            InnerLoan::LoanMut(_) => Err(BroomdogErr::ExclusiveLoan { name }),
282        }
283    }
284
285    pub fn into_owned(self, name: &'static str) -> std::result::Result<TypeValue, InnerLoan> {
286        match self {
287            InnerLoan::Owned(value) => Ok(value),
288            InnerLoan::Loan(arc) => match Arc::try_unwrap(arc) {
289                Ok(value) => {
290                    log::trace!("unified {name}");
291                    Ok(value)
292                }
293                Err(arc) => Err(InnerLoan::Loan(arc)),
294            },
295            InnerLoan::LoanMut(arc_mut_opt) => {
296                let mut guard = arc_mut_opt.lock().unwrap();
297                let may_value = guard.take();
298                drop(guard);
299                if let Some(value) = may_value {
300                    log::trace!("unified {name}");
301                    Ok(value)
302                } else {
303                    log::error!(
304                        "'{name}' cannot be converted to owned as it is loaned exclusively"
305                    );
306                    Err(InnerLoan::LoanMut(arc_mut_opt))
307                }
308            }
309        }
310    }
311
312    pub fn into_loaned(self, name: &'static str) -> std::result::Result<Arc<TypeValue>, InnerLoan> {
313        match self {
314            InnerLoan::Owned(value) => Ok(Arc::new(value)),
315            InnerLoan::Loan(arc) => Ok(arc),
316            InnerLoan::LoanMut(arc_mut_opt) => {
317                let maybe_value = arc_mut_opt.lock().unwrap().take();
318                if let Some(value) = maybe_value {
319                    log::trace!("converted exclusive {name} loan to non-exclusive");
320                    Ok(Arc::new(value))
321                } else {
322                    log::error!(
323                        "'{name}' cannot be converted into a loan as it is loaned exclusively"
324                    );
325                    Err(InnerLoan::LoanMut(arc_mut_opt))
326                }
327            }
328        }
329    }
330
331    pub fn downcast_ref<T: Any + Send + Sync>(
332        &self,
333        name: &'static str,
334    ) -> std::result::Result<&T, BroomdogErr> {
335        match self {
336            InnerLoan::Owned(value) => value.downcast_ref(),
337            InnerLoan::Loan(arc) => arc.downcast_ref(),
338            InnerLoan::LoanMut(_) => Err(BroomdogErr::ExclusiveLoan { name }),
339        }
340    }
341
342    pub fn downcast_mut<T: Any + Send + Sync>(
343        &mut self,
344        name: &'static str,
345    ) -> std::result::Result<&mut T, BroomdogErr> {
346        let owned = self.as_owned_mut(name)?;
347        owned.downcast_mut()
348    }
349}
350
351/// A map of type identifiers to type-erased values.
352#[derive(Default, Debug)]
353pub struct TypeMap {
354    inner: FxHashMap<TypeKey, InnerLoan>,
355}
356
357impl Deref for TypeMap {
358    type Target = FxHashMap<TypeKey, InnerLoan>;
359
360    fn deref(&self) -> &Self::Target {
361        &self.inner
362    }
363}
364
365impl DerefMut for TypeMap {
366    fn deref_mut(&mut self) -> &mut Self::Target {
367        &mut self.inner
368    }
369}
370
371impl TypeMap {
372    /// Insert a value into the type map.
373    pub fn insert_value<T: Any + Send + Sync>(
374        &mut self,
375        value: T,
376    ) -> std::result::Result<Option<T>, BroomdogErr> {
377        let key = TypeKey::new::<T>();
378        if let Some(old_value) = self
379            .inner
380            .insert(key, InnerLoan::Owned(TypeValue::new(value)))
381        {
382            let old_value = old_value
383                .into_owned(key.name())
384                .map_err(|_| BroomdogErr::Loan {
385                    name: std::any::type_name::<T>(),
386                })?;
387            log::trace!("inserted {key} and am returning old value");
388            old_value
389                .downcast()
390                .map(|box_t| Some(*box_t))
391                .map_err(|old_value| BroomdogErr::Mismatch {
392                    new_type: std::any::type_name::<T>(),
393                    old_type: old_value.type_name(),
394                })
395        } else {
396            log::trace!("inserted {key}");
397            Ok(None)
398        }
399    }
400
401    /// Returns a reference to the value of the given parameterized type.
402    pub fn get_value<T: Any + Send + Sync>(&self) -> std::result::Result<Option<&T>, BroomdogErr> {
403        let key = TypeKey::new::<T>();
404        if let Some(value) = self.inner.get(&key) {
405            let t = value.downcast_ref(key.name())?;
406            Ok(Some(t))
407        } else {
408            Ok(None)
409        }
410    }
411
412    /// Returns a mutable reference to the value of the given parameterized
413    /// type.
414    pub fn get_value_mut<T: Any + Send + Sync>(
415        &mut self,
416    ) -> std::result::Result<Option<&mut T>, BroomdogErr> {
417        let key = TypeKey::new::<T>();
418        if let Some(value) = self.inner.get_mut(&key) {
419            let t = value.downcast_mut(key.name())?;
420            Ok(Some(t))
421        } else {
422            Ok(None)
423        }
424    }
425
426    /// Indefinitely loans the typed value for sharing across threads.
427    ///
428    /// Returns an error if the typed value is already exclusively loaned.
429    ///
430    /// [`TypeMap::unify`] should be called before any call to
431    /// [`TypeMap::get_value`] or [`TypeMap::get_value_mut`], or those will
432    /// result in an error.
433    ///
434    /// Additionally, if [`TypeMap::loan_mut`] is called before the returned
435    /// `Loan` is dropped, that call will err.
436    pub fn loan(&mut self, key: TypeKey) -> std::result::Result<Option<Loan>, BroomdogErr> {
437        if let Some(inner) = self.inner.remove(&key) {
438            log::trace!("loaning {key}");
439            match inner.into_loaned(key.name()) {
440                Ok(arc) => {
441                    self.inner.insert(key, InnerLoan::Loan(arc.clone()));
442                    Ok(Some(Loan { inner: arc }))
443                }
444                Err(inner) => {
445                    self.inner.insert(key, inner);
446                    ExclusiveLoanSnafu { name: key.name() }.fail()
447                }
448            }
449        } else {
450            log::trace!("can't loan {key}, DNE");
451            Ok(None)
452        }
453    }
454
455    /// Indefinitely loans the typed value for exclusive mutation.
456    ///
457    /// [`TypeMap::unify`] should be called before any call to
458    /// [`TypeMap::get_value`] or [`TypeMap::get_value_mut`], or those will
459    /// result in an error.
460    ///
461    /// Additionally, if [`TypeMap::loan`] or [`TypeMap::loan_mut`] are called
462    /// again before the returned `LoanMut` is dropped, those calls will err.
463    pub fn loan_mut(&mut self, key: TypeKey) -> std::result::Result<Option<LoanMut>, BroomdogErr> {
464        if let Some(value) = self.inner.remove(&key) {
465            log::trace!("exclusively loaning {key}");
466            if value.is_owned() {
467                // UNWRAP: safe because we just checked that this value is owned
468                let value = value.into_owned(key.name()).unwrap();
469                let outer = Arc::new(Mutex::new(None));
470                self.inner.insert(key, InnerLoan::LoanMut(outer.clone()));
471                Ok(Some(LoanMut {
472                    inner: Some(value),
473                    outer,
474                }))
475            } else {
476                LoanSnafu { name: key.name() }.fail()
477            }
478        } else {
479            log::trace!("can't exclusively loan {key}, DNE");
480            Ok(None)
481        }
482    }
483
484    /// Return whether all values are unified.
485    pub fn is_unified(&self) -> bool {
486        self.inner
487            .iter()
488            .all(|(_ty_key, loan): (_, &InnerLoan)| loan.is_owned())
489    }
490
491    /// Attempts to unify the map, converting all loaned values back into owned
492    /// values.
493    ///
494    /// This must be called before using [`TypeMap::get_value`] or
495    /// [`TypeMap::get_value_mut`].
496    ///
497    /// If the map fails to unify, the map will remain in a consistent state.
498    pub fn unify(&mut self) -> std::result::Result<(), BroomdogErr> {
499        let mut names = vec![];
500        for (key, inner_loan) in std::mem::take(&mut self.inner) {
501            let loan = match inner_loan.into_owned(key.name()) {
502                Ok(value) => InnerLoan::Owned(value),
503                Err(loan) => {
504                    names.push(key.name());
505                    loan
506                }
507            };
508            let _ = self.inner.insert(key, loan);
509        }
510        if names.is_empty() {
511            Ok(())
512        } else {
513            ManyLoansSnafu { names }.fail()
514        }
515    }
516}