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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
use std::{any::type_name, ops::Deref, rc::Rc};

use actix_utils::future::{err, ok, Ready};
use actix_web::{dev::Payload, error, Error, FromRequest, HttpRequest};
use tracing::debug;

/// A thread-local equivalent to [`SharedData`](crate::extract::SharedData).
#[doc(alias = "state")]
#[derive(Debug)]
pub struct LocalData<T: ?Sized>(Rc<T>);

impl<T> LocalData<T> {
    /// Constructs a new `LocalData` instance.
    pub fn new(item: T) -> LocalData<T> {
        LocalData(Rc::new(item))
    }
}

impl<T: ?Sized> Deref for LocalData<T> {
    type Target = T;

    fn deref(&self) -> &T {
        &*self.0
    }
}

impl<T: ?Sized> Clone for LocalData<T> {
    fn clone(&self) -> LocalData<T> {
        LocalData(Rc::clone(&self.0))
    }
}

impl<T: ?Sized> From<Rc<T>> for LocalData<T> {
    fn from(rc: Rc<T>) -> Self {
        LocalData(rc)
    }
}

impl<T: ?Sized + 'static> FromRequest for LocalData<T> {
    type Error = Error;
    type Future = Ready<Result<Self, Error>>;

    #[inline]
    fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
        if let Some(st) = req.app_data::<LocalData<T>>() {
            ok(st.clone())
        } else {
            debug!(
                "Failed to extract `LocalData<{}>` for `{}` handler. For the LocalData extractor \
                to work correctly, wrap the data with `LocalData::new()` and pass it to \
                `App::app_data()`. Ensure that types align in both the set and retrieve calls.",
                type_name::<T>(),
                req.match_name().unwrap_or_else(|| req.path())
            );

            err(error::ErrorInternalServerError(
                "Requested application data is not configured correctly. \
                View/enable debug logs for more details.",
            ))
        }
    }
}

#[cfg(test)]
mod tests {
    use actix_web::{
        dev::Service,
        http::StatusCode,
        test::{init_service, TestRequest},
        web, App, HttpResponse,
    };

    use super::*;

    trait TestTrait {
        fn get_num(&self) -> i32;
    }

    struct A {}

    impl TestTrait for A {
        fn get_num(&self) -> i32 {
            42
        }
    }

    #[actix_web::test]
    async fn test_app_data_extractor() {
        let srv = init_service(
            App::new()
                .app_data(LocalData::new(10usize))
                .service(web::resource("/").to(|_: LocalData<usize>| HttpResponse::Ok())),
        )
        .await;

        let req = TestRequest::default().to_request();
        let resp = srv.call(req).await.unwrap();
        assert_eq!(resp.status(), StatusCode::OK);

        let srv = init_service(
            App::new()
                .app_data(LocalData::new(10u32))
                .service(web::resource("/").to(|_: LocalData<usize>| HttpResponse::Ok())),
        )
        .await;
        let req = TestRequest::default().to_request();
        let resp = srv.call(req).await.unwrap();
        assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
    }

    #[actix_web::test]
    async fn test_override_data() {
        let srv = init_service(
            App::new().app_data(LocalData::new(1usize)).service(
                web::resource("/")
                    .app_data(LocalData::new(10usize))
                    .route(web::get().to(|data: LocalData<usize>| {
                        assert_eq!(*data, 10);
                        HttpResponse::Ok()
                    })),
            ),
        )
        .await;

        let req = TestRequest::default().to_request();
        let resp = srv.call(req).await.unwrap();
        assert_eq!(resp.status(), StatusCode::OK);
    }

    #[actix_web::test]
    async fn test_data_from_rc() {
        let data_new = LocalData::new(String::from("test-123"));
        let data_from_rc = LocalData::from(Rc::new(String::from("test-123")));
        assert_eq!(data_new.0, data_from_rc.0);
    }

    #[actix_web::test]
    async fn test_data_from_dyn_rc() {
        // This works when Sized is required
        let dyn_rc_box: Rc<Box<dyn TestTrait>> = Rc::new(Box::new(A {}));
        let data_arc_box = LocalData::from(dyn_rc_box);

        // This works when Data Sized Bound is removed
        let dyn_rc: Rc<dyn TestTrait> = Rc::new(A {});
        let data_arc = LocalData::from(dyn_rc);
        assert_eq!(data_arc_box.get_num(), data_arc.get_num())
    }

    #[actix_web::test]
    async fn test_get_ref_from_dyn_data() {
        let dyn_rc: Rc<dyn TestTrait> = Rc::new(A {});
        let data_arc = LocalData::from(dyn_rc);
        let ref_data: &dyn TestTrait = &*data_arc;
        assert_eq!(data_arc.get_num(), ref_data.get_num())
    }
}