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