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}