volo_http/server/
param.rs1use std::{convert::Infallible, error::Error, fmt, ops::Deref, str::FromStr};
9
10use ahash::AHashMap;
11use bytes::{BufMut, BytesMut};
12use faststr::FastStr;
13use http::{StatusCode, request::Parts};
14use matchit::Params;
15
16use super::{IntoResponse, extract::FromContext};
17use crate::{
18 context::ServerContext, error::BoxError, response::Response, utils::macros::all_the_tuples,
19};
20
21#[derive(Clone, Debug, Default)]
42pub struct PathParamsVec {
43 inner: Vec<(FastStr, FastStr)>,
44}
45
46impl PathParamsVec {
47 pub(crate) fn extend(&mut self, params: Params) {
48 self.inner.reserve(params.len());
49
50 let cap = params.iter().map(|(k, v)| k.len() + v.len()).sum();
51 let mut buf = BytesMut::with_capacity(cap);
52
53 for (k, v) in params.iter() {
54 buf.put(k.as_bytes());
55 let k = unsafe { FastStr::from_bytes_unchecked(buf.split().freeze()) };
57
58 buf.put(v.as_bytes());
59 let v = unsafe { FastStr::from_bytes_unchecked(buf.split().freeze()) };
61
62 self.inner.push((k, v));
63 }
64 }
65
66 pub(crate) fn pop(&mut self) -> Option<(FastStr, FastStr)> {
67 self.inner.pop()
68 }
69}
70
71impl IntoIterator for PathParamsVec {
72 type Item = (FastStr, FastStr);
73 type IntoIter = std::vec::IntoIter<(FastStr, FastStr)>;
74
75 fn into_iter(self) -> Self::IntoIter {
76 self.inner.into_iter()
77 }
78}
79
80impl Deref for PathParamsVec {
81 type Target = [(FastStr, FastStr)];
82
83 fn deref(&self) -> &Self::Target {
84 &self.inner
85 }
86}
87
88impl FromContext for PathParamsVec {
89 type Rejection = Infallible;
90
91 async fn from_context(cx: &mut ServerContext, _: &mut Parts) -> Result<Self, Self::Rejection> {
92 Ok(cx.params().clone())
93 }
94}
95
96#[derive(Debug, Default, Clone)]
115pub struct PathParamsMap {
116 inner: AHashMap<FastStr, FastStr>,
117}
118
119impl Deref for PathParamsMap {
120 type Target = AHashMap<FastStr, FastStr>;
121
122 fn deref(&self) -> &Self::Target {
123 &self.inner
124 }
125}
126
127impl IntoIterator for PathParamsMap {
128 type Item = (FastStr, FastStr);
129 type IntoIter = std::collections::hash_map::IntoIter<FastStr, FastStr>;
130
131 fn into_iter(self) -> Self::IntoIter {
132 self.inner.into_iter()
133 }
134}
135
136impl From<PathParamsVec> for PathParamsMap {
137 fn from(value: PathParamsVec) -> Self {
138 let mut inner = AHashMap::with_capacity(value.inner.len());
139
140 for (k, v) in value.inner.into_iter() {
141 inner.insert(k, v);
142 }
143
144 Self { inner }
145 }
146}
147
148impl FromContext for PathParamsMap {
149 type Rejection = Infallible;
150
151 async fn from_context(cx: &mut ServerContext, _: &mut Parts) -> Result<Self, Self::Rejection> {
152 let params = cx.params();
153 let mut inner = AHashMap::with_capacity(params.len());
154
155 for (k, v) in params.iter() {
156 inner.insert(k.clone(), v.clone());
157 }
158
159 Ok(Self { inner })
160 }
161}
162
163trait FromPathParam: Sized {
164 fn from_path_param(param: &str) -> Result<Self, PathParamsRejection>;
165}
166
167macro_rules! impl_from_path_param {
168 ($ty:ty) => {
169 impl FromPathParam for $ty {
170 fn from_path_param(param: &str) -> Result<Self, PathParamsRejection> {
171 FromStr::from_str(param)
172 .map_err(Into::into)
173 .map_err(PathParamsRejection::ParseError)
174 }
175 }
176 };
177}
178
179impl_from_path_param!(bool);
180impl_from_path_param!(u8);
181impl_from_path_param!(u16);
182impl_from_path_param!(u32);
183impl_from_path_param!(u64);
184impl_from_path_param!(usize);
185impl_from_path_param!(i8);
186impl_from_path_param!(i16);
187impl_from_path_param!(i32);
188impl_from_path_param!(i64);
189impl_from_path_param!(isize);
190impl_from_path_param!(char);
191impl_from_path_param!(String);
192impl_from_path_param!(FastStr);
193
194#[derive(Debug, Default, Clone)]
211pub struct PathParams<T>(pub T);
212
213impl<T> FromContext for PathParams<T>
214where
215 T: FromPathParam,
216{
217 type Rejection = PathParamsRejection;
218
219 async fn from_context(cx: &mut ServerContext, _: &mut Parts) -> Result<Self, Self::Rejection> {
220 let mut param_iter = cx.params().iter();
221 let t = T::from_path_param(
222 param_iter
223 .next()
224 .ok_or(PathParamsRejection::LengthMismatch)?
225 .1
226 .as_str(),
227 )?;
228 Ok(PathParams(t))
229 }
230}
231
232macro_rules! impl_path_params_extractor {
233 (
234 $($ty:ident),+ $(,)?
235 ) => {
236 #[allow(non_snake_case)]
237 impl<$($ty,)+> FromContext for PathParams<($($ty,)+)>
238 where
239 $(
240 $ty: FromPathParam,
241 )+
242 {
243 type Rejection = PathParamsRejection;
244
245 async fn from_context(
246 cx: &mut ServerContext,
247 _: &mut Parts,
248 ) -> Result<Self, Self::Rejection> {
249 let mut param_iter = cx.params().iter();
250 $(
251 let $ty = $ty::from_path_param(
252 param_iter.next().ok_or(PathParamsRejection::LengthMismatch)?.1.as_str(),
253 )?;
254 )+
255 Ok(PathParams(($($ty,)+)))
256 }
257 }
258 };
259}
260
261all_the_tuples!(impl_path_params_extractor);
262
263#[derive(Debug)]
265pub enum PathParamsRejection {
266 LengthMismatch,
268 ParseError(BoxError),
270}
271
272impl fmt::Display for PathParamsRejection {
273 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
274 match self {
275 Self::LengthMismatch => write!(
276 f,
277 "the number of path params does not match number of types in `PathParams`"
278 ),
279 Self::ParseError(e) => write!(f, "path param parse error: {e}"),
280 }
281 }
282}
283
284impl Error for PathParamsRejection {
285 fn source(&self) -> Option<&(dyn Error + 'static)> {
286 match self {
287 Self::LengthMismatch => None,
288 Self::ParseError(e) => Some(e.as_ref()),
289 }
290 }
291}
292
293impl IntoResponse for PathParamsRejection {
294 fn into_response(self) -> Response {
295 StatusCode::BAD_REQUEST.into_response()
296 }
297}