Skip to main content

runtime_context/
data.rs

1use better_any::{Tid, TidExt};
2
3/// Thread-safe variant of `Tid`.
4///
5/// This trait is automatically implemented for any `Tid` type that is `Send + Sync`.
6pub trait ShareableTid<'a>: Tid<'a> + Send + Sync {}
7
8impl<'a, T: Tid<'a> + Send + Sync> ShareableTid<'a> for T {}
9
10/// Stored value variants inside a `Context`.
11///
12/// Values may be owned, immutably borrowed, or mutably borrowed.
13pub enum Data<'ty, 'r> {
14    Owned(Box<dyn ShareableTid<'ty>>),
15    Borrowed(&'r dyn ShareableTid<'ty>),
16    Mut(&'r mut dyn ShareableTid<'ty>),
17}
18
19impl<'ty, 'r> Data<'ty, 'r> {
20    /// Downcast to a shared reference of the underlying value.
21    pub fn downcast_ref<'b, T: Tid<'ty>>(&'b self) -> Option<&'b T> {
22        match self {
23            Data::Owned(value) => (**value).downcast_ref(),
24            Data::Borrowed(value) => (*value).downcast_ref(),
25            Data::Mut(value) => (*value).downcast_ref(),
26        }
27    }
28
29    /// Downcast to a mutable reference of the underlying value.
30    pub fn downcast_mut<'b, T: Tid<'ty>>(&'b mut self) -> Option<&'b mut T> {
31        match self {
32            Data::Owned(value) => (**value).downcast_mut(),
33            Data::Mut(value) => (*value).downcast_mut(),
34            _ => None,
35        }
36    }
37
38    /// Convert into an owned value.
39    ///
40    /// Borrowed values are cloned. Returns `Err(self)` when the type does not match.
41    pub fn into_owned<T: Clone + Tid<'ty>>(self) -> Result<T, Self> {
42        match self {
43            Data::Owned(value) => match value.downcast_box::<T>() {
44                Ok(value) => Ok(*value),
45                Err(v) => Err(Data::Owned(v)),
46            },
47            Data::Borrowed(value) => match value.downcast_ref::<T>() {
48                Some(value) => Ok(value.clone()),
49                None => Err(Data::Borrowed(value)),
50            },
51            Data::Mut(value) => match value.downcast_ref::<T>() {
52                Some(value) => Ok(value.clone()),
53                None => Err(Data::Mut(value)),
54            },
55        }
56    }
57
58    /// Take the owned value if present.
59    ///
60    /// Borrowed variants return `Err(self)`.
61    pub fn try_take_owned<T: Tid<'ty>>(self) -> Result<T, Self> {
62        match self {
63            Data::Owned(value) => match value.downcast_box::<T>() {
64                Ok(value) => Ok(*value),
65                Err(v) => Err(Data::Owned(v)),
66            },
67            _ => Err(self),
68        }
69    }
70}
71
72#[cfg(test)]
73mod tests {
74    use better_any::tid;
75
76    use super::*;
77
78    #[derive(Debug, Clone)]
79    struct Test;
80    tid!(Test);
81
82    #[test]
83    fn test_data_owned() {
84        let mut owned = Data::Owned(Box::new(Test));
85
86        let _ = owned.downcast_ref::<Test>().unwrap();
87        let _ = owned.downcast_mut::<Test>().unwrap();
88        assert!(matches!(owned.into_owned::<Test>(), Ok(Test)));
89
90        let owned = Data::Owned(Box::new(Test));
91        assert!(matches!(owned.try_take_owned::<Test>(), Ok(Test)));
92    }
93
94    #[test]
95    fn test_data_ref() {
96        let test = Test;
97        let mut borrowed = Data::Borrowed(&test);
98
99        let _ = borrowed.downcast_ref::<Test>().unwrap();
100        assert!(borrowed.downcast_mut::<Test>().is_none());
101        assert!(matches!(borrowed.into_owned::<Test>(), Ok(Test)));
102
103        let borrowed = Data::Borrowed(&test);
104        assert!(matches!(borrowed.try_take_owned::<Test>(), Err(_)));
105    }
106
107    #[test]
108    fn test_data_mut() {
109        let mut test = Test;
110        let mut mut_ref = Data::Mut(&mut test);
111
112        let _ = mut_ref.downcast_ref::<Test>().unwrap();
113        let _ = mut_ref.downcast_mut::<Test>().unwrap();
114        assert!(matches!(mut_ref.into_owned::<Test>(), Ok(Test)));
115
116        let mut_ref = Data::Mut(&mut test);
117        assert!(matches!(mut_ref.try_take_owned::<Test>(), Err(_)));
118    }
119
120    #[test]
121    fn test_into_owned_wrong_type() {
122        #[derive(Debug, Clone)]
123        struct Other;
124        tid!(Other);
125
126        let data = Data::Owned(Box::new(Test));
127        assert!(matches!(data.into_owned::<Other>(), Err(_)));
128    }
129}