ctxbuilder/
entry.rs

1use std::{
2    any::{Any, TypeId},
3    collections::hash_map,
4    marker::PhantomData,
5};
6
7type InnerEntry<'c> =
8    hash_map::Entry<'c, (TypeId, Option<&'static str>), Box<dyn Any + Send + Sync>>;
9
10/// View into a single entry in a context
11#[derive(Debug)]
12pub struct Entry<'c, T> {
13    main: Option<&'c T>,
14    inner: InnerEntry<'c>,
15    _phantom_data: PhantomData<T>,
16}
17
18impl<'c, T> Entry<'c, T> {
19    pub(crate) fn new(main: Option<&'c T>, inner: InnerEntry<'c>) -> Self {
20        Self {
21            main,
22            inner,
23            _phantom_data: PhantomData,
24        }
25    }
26}
27
28impl<'c, T: Send + Sync + 'static> Entry<'c, T> {
29    /// Ensures a value is in the entry by inserting the default if empty, and returns a reference
30    /// to the value in the entry
31    pub fn or_insert(self, default: T) -> &'c T {
32        match (self.main, self.inner) {
33            // entry is vacant, but main contains something: return main
34            (Some(main), InnerEntry::Vacant(_)) => main,
35            // entry is occuped: return inner
36            // main is empty: insert inner
37            (_, inner) => inner
38                .or_insert(Box::new(default))
39                .downcast_ref()
40                .expect("downcast_ref on T"),
41        }
42    }
43
44    /// Ensures a value is in the entry by inserting the result of the default function if empty,
45    /// and returns a reference to the value in the entry
46    pub fn or_insert_with<F: FnOnce() -> T>(self, default: F) -> &'c T {
47        match (self.main, self.inner) {
48            // entry is vacant, but main contains something: return main
49            (Some(main), InnerEntry::Vacant(_)) => main,
50            // entry is occuped: return inner
51            // main is empty: insert inner
52            (_, inner) => inner
53                .or_insert_with(|| Box::new(default()))
54                .downcast_ref()
55                .expect("downcast_ref on T"),
56        }
57    }
58
59    /// Provides in-place mutable access to an occupied entry before any potential inserts into the
60    /// context
61    pub fn and_modify<F: FnOnce(&mut T)>(self, f: F) -> Self {
62        Entry::new(
63            // Since `and_modify` ensures we transition into `Occupied` state, we don't need to
64            // keep the inherited value.
65            None,
66            self.inner
67                .and_modify(|v| f(v.downcast_mut().expect("downcast_mut on T"))),
68        )
69    }
70}
71
72impl<'c, T: Default + Send + Sync + 'static> Entry<'c, T> {
73    /// Ensures a value is in the entry by inserting the default value if empty, and returns a
74    /// reference to the value in the entry
75    pub fn or_default(self) -> &'c T {
76        // We need to use `or_insert` here, because we need to build a `Box` for `T` specifically
77        #[allow(clippy::unwrap_or_default)]
78        self.main.unwrap_or_else(|| {
79            self.inner
80                .or_insert(Box::<T>::default())
81                .downcast_ref()
82                .expect("downcast_ref on T")
83        })
84    }
85}