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