poem_openapi/param/
cookie.rs

1use std::ops::{Deref, DerefMut};
2
3use poem::{
4    Request, RequestBody, Result,
5    web::cookie::{CookieJar, PrivateCookieJar, SignedCookieJar},
6};
7
8use crate::{
9    ApiExtractor, ApiExtractorType, ExtractParamOptions,
10    error::ParseParamError,
11    registry::{MetaParamIn, MetaSchemaRef, Registry},
12    types::ParseFromParameter,
13};
14
15/// Represents the parameters passed by the cookie.
16pub struct Cookie<T>(pub T);
17
18impl<T> Deref for Cookie<T> {
19    type Target = T;
20
21    fn deref(&self) -> &Self::Target {
22        &self.0
23    }
24}
25
26impl<T> DerefMut for Cookie<T> {
27    fn deref_mut(&mut self) -> &mut Self::Target {
28        &mut self.0
29    }
30}
31
32impl<'a, T: ParseFromParameter> ApiExtractor<'a> for Cookie<T> {
33    const TYPES: &'static [ApiExtractorType] = &[ApiExtractorType::Parameter];
34    const PARAM_IS_REQUIRED: bool = T::IS_REQUIRED;
35
36    type ParamType = T;
37    type ParamRawType = T::RawValueType;
38
39    fn register(registry: &mut Registry) {
40        T::register(registry);
41    }
42
43    fn param_in() -> Option<MetaParamIn> {
44        Some(MetaParamIn::Cookie)
45    }
46
47    fn param_schema_ref() -> Option<MetaSchemaRef> {
48        Some(T::schema_ref())
49    }
50
51    fn param_raw_type(&self) -> Option<&Self::ParamRawType> {
52        self.0.as_raw_value()
53    }
54
55    async fn from_request(
56        request: &'a Request,
57        _body: &mut RequestBody,
58        param_opts: ExtractParamOptions<Self::ParamType>,
59    ) -> Result<Self> {
60        let value = request
61            .cookie()
62            .get_value(param_opts.name, param_opts.ignore_case);
63        let value = match (value, &param_opts.default_value) {
64            (Some(value), _) => Some(value),
65            (None, Some(default_value)) => return Ok(Self(default_value())),
66            (None, _) => None,
67        };
68
69        ParseFromParameter::parse_from_parameters(value.as_deref())
70            .map(Self)
71            .map_err(|err| {
72                ParseParamError {
73                    name: param_opts.name,
74                    reason: err.into_message(),
75                }
76                .into()
77            })
78    }
79}
80
81/// Represents the parameters passed by the private cookie.
82pub struct CookiePrivate<T>(pub T);
83
84impl<T> Deref for CookiePrivate<T> {
85    type Target = T;
86
87    fn deref(&self) -> &Self::Target {
88        &self.0
89    }
90}
91
92impl<T> DerefMut for CookiePrivate<T> {
93    fn deref_mut(&mut self) -> &mut Self::Target {
94        &mut self.0
95    }
96}
97
98impl<'a, T: ParseFromParameter> ApiExtractor<'a> for CookiePrivate<T> {
99    const TYPES: &'static [ApiExtractorType] = &[ApiExtractorType::Parameter];
100    const PARAM_IS_REQUIRED: bool = T::IS_REQUIRED;
101
102    type ParamType = T;
103    type ParamRawType = T::RawValueType;
104
105    fn register(registry: &mut Registry) {
106        T::register(registry);
107    }
108
109    fn param_in() -> Option<MetaParamIn> {
110        Some(MetaParamIn::Cookie)
111    }
112
113    fn param_schema_ref() -> Option<MetaSchemaRef> {
114        Some(T::schema_ref())
115    }
116
117    fn param_raw_type(&self) -> Option<&Self::ParamRawType> {
118        self.0.as_raw_value()
119    }
120
121    async fn from_request(
122        request: &'a Request,
123        _body: &mut RequestBody,
124        param_opts: ExtractParamOptions<Self::ParamType>,
125    ) -> Result<Self> {
126        let value = request
127            .cookie()
128            .private()
129            .get_value(param_opts.name, param_opts.ignore_case);
130        let value = match (value, &param_opts.default_value) {
131            (Some(value), _) => Some(value),
132            (None, Some(default_value)) => return Ok(Self(default_value())),
133            (None, _) => None,
134        };
135
136        ParseFromParameter::parse_from_parameters(value.as_deref())
137            .map(Self)
138            .map_err(|err| {
139                dbg!(ParseParamError {
140                    name: param_opts.name,
141                    reason: err.into_message(),
142                })
143                .into()
144            })
145    }
146}
147
148/// Represents the parameters passed by the signed cookie.
149pub struct CookieSigned<T>(pub T);
150
151impl<T> Deref for CookieSigned<T> {
152    type Target = T;
153
154    fn deref(&self) -> &Self::Target {
155        &self.0
156    }
157}
158
159impl<T> DerefMut for CookieSigned<T> {
160    fn deref_mut(&mut self) -> &mut Self::Target {
161        &mut self.0
162    }
163}
164
165impl<'a, T: ParseFromParameter> ApiExtractor<'a> for CookieSigned<T> {
166    const TYPES: &'static [ApiExtractorType] = &[ApiExtractorType::Parameter];
167    const PARAM_IS_REQUIRED: bool = T::IS_REQUIRED;
168
169    type ParamType = T;
170    type ParamRawType = T::RawValueType;
171
172    fn register(registry: &mut Registry) {
173        T::register(registry);
174    }
175
176    fn param_in() -> Option<MetaParamIn> {
177        Some(MetaParamIn::Cookie)
178    }
179
180    fn param_schema_ref() -> Option<MetaSchemaRef> {
181        Some(T::schema_ref())
182    }
183
184    fn param_raw_type(&self) -> Option<&Self::ParamRawType> {
185        self.0.as_raw_value()
186    }
187
188    async fn from_request(
189        request: &'a Request,
190        _body: &mut RequestBody,
191        param_opts: ExtractParamOptions<Self::ParamType>,
192    ) -> Result<Self> {
193        let value = request
194            .cookie()
195            .signed()
196            .get_value(param_opts.name, param_opts.ignore_case);
197        let value = match (value, &param_opts.default_value) {
198            (Some(value), _) => Some(value),
199            (None, Some(default_value)) => return Ok(Self(default_value())),
200            (None, _) => None,
201        };
202
203        ParseFromParameter::parse_from_parameters(value.as_deref())
204            .map(Self)
205            .map_err(|err| {
206                ParseParamError {
207                    name: param_opts.name,
208                    reason: err.into_message(),
209                }
210                .into()
211            })
212    }
213}
214
215trait GetValueFromCookie {
216    fn get_value(&self, name: &str, ignore_case: bool) -> Option<String>;
217}
218
219impl GetValueFromCookie for CookieJar {
220    fn get_value(&self, name: &str, ignore_case: bool) -> Option<String> {
221        if !ignore_case {
222            self.get(name)
223        } else {
224            self.get_ignore_ascii_case(name)
225        }
226        .as_ref()
227        .map(|cookie| cookie.value_str().to_string())
228    }
229}
230
231impl GetValueFromCookie for PrivateCookieJar<'_> {
232    fn get_value(&self, name: &str, ignore_case: bool) -> Option<String> {
233        if !ignore_case {
234            self.get(name)
235        } else {
236            self.get_ignore_ascii_case(name)
237        }
238        .as_ref()
239        .map(|cookie| cookie.value_str().to_string())
240    }
241}
242
243impl GetValueFromCookie for SignedCookieJar<'_> {
244    fn get_value(&self, name: &str, ignore_case: bool) -> Option<String> {
245        if !ignore_case {
246            self.get(name)
247        } else {
248            self.get_ignore_ascii_case(name)
249        }
250        .as_ref()
251        .map(|cookie| cookie.value_str().to_string())
252    }
253}