actix_web_lab/
swap_data.rs1use std::{
2 future::{Ready, ready},
3 sync::Arc,
4};
5
6use actix_web::{Error, FromRequest, HttpRequest, dev, error};
7use arc_swap::{ArcSwap, Guard};
8use tracing::debug;
9
10#[derive(Debug)]
16pub struct SwapData<T> {
17 swap: Arc<ArcSwap<T>>,
18}
19
20impl<T: Send + Sync> SwapData<T> {
21 pub fn new(item: T) -> Self {
23 Self {
24 swap: Arc::new(ArcSwap::new(Arc::new(item))),
25 }
26 }
27
28 pub fn load(&self) -> Guard<Arc<T>> {
32 self.swap.load()
33 }
34
35 pub fn store(&self, item: T) {
39 self.swap.store(Arc::new(item))
40 }
41}
42
43impl<T> Clone for SwapData<T> {
44 fn clone(&self) -> Self {
45 Self {
46 swap: Arc::clone(&self.swap),
47 }
48 }
49}
50
51impl<T: 'static> FromRequest for SwapData<T> {
52 type Error = Error;
53 type Future = Ready<Result<Self, Self::Error>>;
54
55 fn from_request(req: &HttpRequest, _pl: &mut dev::Payload) -> Self::Future {
56 if let Some(data) = req.app_data::<SwapData<T>>() {
57 ready(Ok(SwapData {
58 swap: Arc::clone(&data.swap),
59 }))
60 } else {
61 debug!(
62 "Failed to extract `SwapData<{}>` for `{}` handler. For the Data extractor to work \
63 correctly, wrap the data with `SwapData::new()` and pass it to `App::app_data()`. \
64 Ensure that types align in both the set and retrieve calls.",
65 core::any::type_name::<T>(),
66 req.match_name().unwrap_or_else(|| req.path())
67 );
68
69 ready(Err(error::ErrorInternalServerError(
70 "Requested application data is not configured correctly. \
71 View/enable debug logs for more details.",
72 )))
73 }
74 }
75}
76
77#[cfg(test)]
78mod tests {
79 use actix_web::test::TestRequest;
80
81 use super::*;
82
83 #[derive(Debug, Clone, PartialEq, Eq)]
84 struct NonCopy(u32);
85
86 #[actix_web::test]
87 async fn deref() {
88 let data = SwapData::new(NonCopy(42));
89 let inner_data = data.load();
90 let _inner_data: &NonCopy = &inner_data;
91 }
92
93 #[actix_web::test]
94 async fn extract_success() {
95 let data = SwapData::new(NonCopy(42));
96
97 let req = TestRequest::default().app_data(data).to_http_request();
98 let extracted_data = SwapData::<NonCopy>::extract(&req).await.unwrap();
99
100 assert_eq!(**extracted_data.load(), NonCopy(42));
101 }
102
103 #[actix_web::test]
104 async fn extract_fail() {
105 let req = TestRequest::default().to_http_request();
106 SwapData::<()>::extract(&req).await.unwrap_err();
107 }
108
109 #[actix_web::test]
110 async fn store_and_reload() {
111 let data = SwapData::new(NonCopy(42));
112 let initial_data = Guard::into_inner(data.load());
113
114 let req = TestRequest::default().app_data(data).to_http_request();
115
116 let extracted_data = SwapData::<NonCopy>::extract(&req).await.unwrap();
118 assert_eq!(**extracted_data.load(), NonCopy(42));
119
120 extracted_data.store(NonCopy(80));
122
123 let extracted_data = SwapData::<NonCopy>::extract(&req).await.unwrap();
125 assert_eq!(**extracted_data.load(), NonCopy(80));
126
127 assert_eq!(*initial_data, NonCopy(42));
129 }
130}