actix_web_lab/
local_data.rs1use std::{
2 any::type_name,
3 future::{Ready, ready},
4 ops::Deref,
5 rc::Rc,
6};
7
8use actix_web::{Error, FromRequest, HttpRequest, dev::Payload, error};
9use tracing::debug;
10
11#[doc(alias = "state")]
13#[derive(Debug)]
14pub struct LocalData<T: ?Sized>(Rc<T>);
15
16impl<T> LocalData<T> {
17 pub fn new(item: T) -> LocalData<T> {
19 LocalData(Rc::new(item))
20 }
21}
22
23impl<T: ?Sized> Deref for LocalData<T> {
24 type Target = T;
25
26 fn deref(&self) -> &T {
27 &self.0
28 }
29}
30
31impl<T: ?Sized> Clone for LocalData<T> {
32 fn clone(&self) -> LocalData<T> {
33 LocalData(Rc::clone(&self.0))
34 }
35}
36
37impl<T: ?Sized> From<Rc<T>> for LocalData<T> {
38 fn from(rc: Rc<T>) -> Self {
39 LocalData(rc)
40 }
41}
42
43impl<T: ?Sized + 'static> FromRequest for LocalData<T> {
44 type Error = Error;
45 type Future = Ready<Result<Self, Error>>;
46
47 #[inline]
48 fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
49 if let Some(st) = req.app_data::<LocalData<T>>() {
50 ready(Ok(st.clone()))
51 } else {
52 debug!(
53 "Failed to extract `LocalData<{}>` for `{}` handler. For the LocalData extractor \
54 to work correctly, wrap the data with `LocalData::new()` and pass it to \
55 `App::app_data()`. Ensure that types align in both the set and retrieve calls.",
56 type_name::<T>(),
57 req.match_name().unwrap_or_else(|| req.path())
58 );
59
60 ready(Err(error::ErrorInternalServerError(
61 "Requested application data is not configured correctly. \
62 View/enable debug logs for more details.",
63 )))
64 }
65 }
66}
67
68#[cfg(test)]
69mod tests {
70 use actix_web::{
71 App, HttpResponse,
72 dev::Service,
73 http::StatusCode,
74 test::{TestRequest, init_service},
75 web,
76 };
77
78 use super::*;
79
80 trait TestTrait {
81 fn get_num(&self) -> i32;
82 }
83
84 struct A {}
85
86 impl TestTrait for A {
87 fn get_num(&self) -> i32 {
88 42
89 }
90 }
91
92 #[actix_web::test]
93 async fn test_app_data_extractor() {
94 let srv = init_service(
95 App::new()
96 .app_data(LocalData::new(10usize))
97 .service(web::resource("/").to(|_: LocalData<usize>| HttpResponse::Ok())),
98 )
99 .await;
100
101 let req = TestRequest::default().to_request();
102 let resp = srv.call(req).await.unwrap();
103 assert_eq!(resp.status(), StatusCode::OK);
104
105 let srv = init_service(
106 App::new()
107 .app_data(LocalData::new(10u32))
108 .service(web::resource("/").to(|_: LocalData<usize>| HttpResponse::Ok())),
109 )
110 .await;
111 let req = TestRequest::default().to_request();
112 let resp = srv.call(req).await.unwrap();
113 assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
114 }
115
116 #[actix_web::test]
117 async fn test_override_data() {
118 let srv = init_service(
119 App::new().app_data(LocalData::new(1usize)).service(
120 web::resource("/")
121 .app_data(LocalData::new(10usize))
122 .route(web::get().to(|data: LocalData<usize>| {
123 assert_eq!(*data, 10);
124 HttpResponse::Ok()
125 })),
126 ),
127 )
128 .await;
129
130 let req = TestRequest::default().to_request();
131 let resp = srv.call(req).await.unwrap();
132 assert_eq!(resp.status(), StatusCode::OK);
133 }
134
135 #[actix_web::test]
136 async fn test_data_from_rc() {
137 let data_new = LocalData::new(String::from("test-123"));
138 let data_from_rc = LocalData::from(Rc::new(String::from("test-123")));
139 assert_eq!(data_new.0, data_from_rc.0);
140 }
141
142 #[actix_web::test]
143 async fn test_data_from_dyn_rc() {
144 let dyn_rc_box: Rc<Box<dyn TestTrait>> = Rc::new(Box::new(A {}));
146 let data_arc_box = LocalData::from(dyn_rc_box);
147
148 let dyn_rc: Rc<dyn TestTrait> = Rc::new(A {});
150 let data_arc = LocalData::from(dyn_rc);
151 assert_eq!(data_arc_box.get_num(), data_arc.get_num())
152 }
153
154 #[actix_web::test]
155 async fn test_get_ref_from_dyn_data() {
156 let dyn_rc: Rc<dyn TestTrait> = Rc::new(A {});
157 let data_arc = LocalData::from(dyn_rc);
158 let ref_data: &dyn TestTrait = &*data_arc;
159 assert_eq!(data_arc.get_num(), ref_data.get_num())
160 }
161}