1use std::any::type_name;
2
3use actix_utils::future::{ready, Ready};
4
5use crate::{dev::Payload, error, FromRequest, HttpRequest};
6
7#[derive(Debug, Clone)]
47pub struct ThinData<T>(pub T);
48
49impl_more::impl_as_ref!(ThinData<T> => T);
50impl_more::impl_as_mut!(ThinData<T> => T);
51impl_more::impl_deref_and_mut!(<T> in ThinData<T> => T);
52
53impl<T: Clone + 'static> FromRequest for ThinData<T> {
54 type Error = crate::Error;
55 type Future = Ready<Result<Self, Self::Error>>;
56
57 #[inline]
58 fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
59 ready(req.app_data::<Self>().cloned().ok_or_else(|| {
60 log::debug!(
61 "Failed to extract `ThinData<{}>` for `{}` handler. For the ThinData extractor to work \
62 correctly, wrap the data with `ThinData()` and pass it to `App::app_data()`. \
63 Ensure that types align in both the set and retrieve calls.",
64 type_name::<T>(),
65 req.match_name().unwrap_or(req.path())
66 );
67
68 error::ErrorInternalServerError(
69 "Requested application data is not configured correctly. \
70 View/enable debug logs for more details.",
71 )
72 }))
73 }
74}
75
76#[cfg(test)]
77mod tests {
78 use std::sync::{Arc, Mutex};
79
80 use super::*;
81 use crate::{
82 http::StatusCode,
83 test::{call_service, init_service, TestRequest},
84 web, App, HttpResponse,
85 };
86
87 type TestT = Arc<Mutex<u32>>;
88
89 #[actix_rt::test]
90 async fn thin_data() {
91 let test_data = TestT::default();
92
93 let app = init_service(App::new().app_data(ThinData(test_data.clone())).service(
94 web::resource("/").to(|td: ThinData<TestT>| {
95 *td.lock().unwrap() += 1;
96 HttpResponse::Ok()
97 }),
98 ))
99 .await;
100
101 for _ in 0..3 {
102 let req = TestRequest::default().to_request();
103 let resp = call_service(&app, req).await;
104 assert_eq!(resp.status(), StatusCode::OK);
105 }
106
107 assert_eq!(*test_data.lock().unwrap(), 3);
108 }
109
110 #[actix_rt::test]
111 async fn thin_data_missing() {
112 let app = init_service(
113 App::new().service(web::resource("/").to(|_: ThinData<u32>| HttpResponse::Ok())),
114 )
115 .await;
116
117 let req = TestRequest::default().to_request();
118 let resp = call_service(&app, req).await;
119 assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
120 }
121}