roa_core/context/
storage.rs

1use std::any::{Any, TypeId};
2use std::borrow::Cow;
3use std::collections::HashMap;
4use std::fmt::Display;
5use std::ops::Deref;
6use std::str::FromStr;
7use std::sync::Arc;
8
9use http::StatusCode;
10
11use crate::Status;
12
13pub trait Value: Any + Send + Sync {}
14
15impl<V> Value for V where V: Any + Send + Sync {}
16
17/// A context scoped storage.
18#[derive(Clone)]
19pub struct Storage(HashMap<TypeId, HashMap<Cow<'static, str>, Arc<dyn Any + Send + Sync>>>);
20
21/// A wrapper of Arc.
22///
23/// ### Deref
24///
25/// ```rust
26/// use roa_core::Variable;
27///
28/// fn consume<V>(var: Variable<V>) {
29///     let value: &V = &var;
30/// }
31/// ```
32///
33/// ### Parse
34///
35/// ```rust
36/// use roa_core::{Variable, Result};
37///
38/// fn consume<V: AsRef<str>>(var: Variable<V>) -> Result {
39///     let value: i32 = var.parse()?;
40///     Ok(())
41/// }
42/// ```
43#[derive(Debug, Clone)]
44pub struct Variable<'a, V> {
45    key: &'a str,
46    value: Arc<V>,
47}
48
49impl<V> Deref for Variable<'_, V> {
50    type Target = V;
51    #[inline]
52    fn deref(&self) -> &Self::Target {
53        &self.value
54    }
55}
56
57impl<'a, V> Variable<'a, V> {
58    /// Construct a variable from name and value.
59    #[inline]
60    fn new(key: &'a str, value: Arc<V>) -> Self {
61        Self { key, value }
62    }
63
64    /// Consume self and get inner Arc<T>.
65    #[inline]
66    pub fn value(self) -> Arc<V> {
67        self.value
68    }
69}
70
71impl<V> Variable<'_, V>
72where
73    V: AsRef<str>,
74{
75    /// A wrapper of `str::parse`. Converts `T::FromStr::Err` to `roa_core::Error` automatically.
76    #[inline]
77    pub fn parse<T>(&self) -> Result<T, Status>
78    where
79        T: FromStr,
80        T::Err: Display,
81    {
82        self.as_ref().parse().map_err(|err| {
83            Status::new(
84                StatusCode::BAD_REQUEST,
85                format!(
86                    "{}\ntype of variable `{}` should be {}",
87                    err,
88                    self.key,
89                    std::any::type_name::<T>()
90                ),
91                true,
92            )
93        })
94    }
95}
96
97impl Storage {
98    /// Construct an empty Bucket.
99    #[inline]
100    pub fn new() -> Self {
101        Self(HashMap::new())
102    }
103
104    /// Inserts a key-value pair into the storage.
105    ///
106    /// If the storage did not have this key present, [`None`] is returned.
107    ///
108    /// If the storage did have this key present, the value is updated, and the old
109    /// value is returned.
110    pub fn insert<S, K, V>(&mut self, scope: S, key: K, value: V) -> Option<Arc<V>>
111    where
112        S: Any,
113        K: Into<Cow<'static, str>>,
114        V: Value,
115    {
116        let id = TypeId::of::<S>();
117        match self.0.get_mut(&id) {
118            Some(bucket) => bucket
119                .insert(key.into(), Arc::new(value))
120                .and_then(|value| value.downcast().ok()),
121            None => {
122                self.0.insert(id, HashMap::new());
123                self.insert(scope, key, value)
124            }
125        }
126    }
127
128    /// If the storage did not have this key present, [`None`] is returned.
129    ///
130    /// If the storage did have this key present, the key-value pair will be returned as a `Variable`
131    #[inline]
132    pub fn get<'a, S, V>(&self, key: &'a str) -> Option<Variable<'a, V>>
133    where
134        S: Any,
135        V: Value,
136    {
137        let value = self.0.get(&TypeId::of::<S>())?.get(key)?.clone();
138        Some(Variable::new(key, value.clone().downcast().ok()?))
139    }
140}
141
142impl Default for Storage {
143    #[inline]
144    fn default() -> Self {
145        Self::new()
146    }
147}
148
149#[cfg(test)]
150mod tests {
151    use std::sync::Arc;
152
153    use http::StatusCode;
154
155    use super::{Storage, Variable};
156
157    #[test]
158    fn storage() {
159        struct Scope;
160
161        let mut storage = Storage::default();
162        assert!(storage.get::<Scope, &'static str>("id").is_none());
163        assert!(storage.insert(Scope, "id", "1").is_none());
164        let id: i32 = storage
165            .get::<Scope, &'static str>("id")
166            .unwrap()
167            .parse()
168            .unwrap();
169        assert_eq!(1, id);
170        assert_eq!(
171            1,
172            storage
173                .insert(Scope, "id", "2")
174                .unwrap()
175                .parse::<i32>()
176                .unwrap()
177        );
178    }
179
180    #[test]
181    fn variable() {
182        assert_eq!(
183            1,
184            Variable::new("id", Arc::new("1")).parse::<i32>().unwrap()
185        );
186        let result = Variable::new("id", Arc::new("x")).parse::<usize>();
187        assert!(result.is_err());
188        let status = result.unwrap_err();
189        assert_eq!(StatusCode::BAD_REQUEST, status.status_code);
190        assert!(status
191            .message
192            .ends_with("type of variable `id` should be usize"));
193    }
194}