Skip to main content

caelix_core/
context.rs

1use std::{
2    any::{Any, TypeId},
3    collections::HashMap,
4    sync::{Arc, RwLock},
5};
6
7pub struct RequestContext {
8    method: String,
9    path: String,
10    headers: HashMap<String, String>,
11    extensions: RwLock<HashMap<TypeId, Arc<dyn Any + Send + Sync>>>,
12}
13
14impl RequestContext {
15    pub fn new(
16        method: impl Into<String>,
17        path: impl Into<String>,
18        headers: HashMap<String, String>,
19    ) -> Self {
20        Self {
21            method: method.into(),
22            path: path.into(),
23            headers: headers
24                .into_iter()
25                .map(|(name, value)| (name.to_ascii_lowercase(), value))
26                .collect(),
27            extensions: RwLock::new(HashMap::new()),
28        }
29    }
30
31    pub fn method(&self) -> &str {
32        &self.method
33    }
34
35    pub fn path(&self) -> &str {
36        &self.path
37    }
38
39    pub fn header(&self, name: &str) -> Option<&str> {
40        self.headers
41            .get(&name.to_ascii_lowercase())
42            .map(String::as_str)
43    }
44
45    pub fn bearer_token(&self) -> Option<&str> {
46        self.header("authorization")?.strip_prefix("Bearer ")
47    }
48
49    pub fn set<T: Send + Sync + 'static>(&self, value: T) -> crate::Result<()> {
50        self.extensions
51            .write()
52            .map_err(|_| {
53                crate::exception::startup_error("request context extensions lock poisoned")
54            })?
55            .insert(TypeId::of::<T>(), Arc::new(value));
56        Ok(())
57    }
58
59    pub fn get<T: Send + Sync + 'static>(&self) -> crate::Result<Option<Arc<T>>> {
60        let value = self
61            .extensions
62            .read()
63            .map_err(|_| {
64                crate::exception::startup_error("request context extensions lock poisoned")
65            })?
66            .get(&TypeId::of::<T>())
67            .cloned();
68
69        let Some(value) = value else {
70            return Ok(None);
71        };
72
73        value
74            .clone()
75            .downcast::<T>()
76            .map(Some)
77            .map_err(|_| crate::exception::startup_error("request context extension type mismatch"))
78    }
79}