Line data Source code
1 : use core::fmt;
2 : use std::borrow::Cow;
3 : use std::str::FromStr;
4 :
5 : use anyhow::anyhow;
6 : use hyper::body::HttpBody;
7 : use hyper::{Body, Request};
8 : use routerify::ext::RequestExt;
9 :
10 : use super::error::ApiError;
11 :
12 0 : pub fn get_request_param<'a>(
13 0 : request: &'a Request<Body>,
14 0 : param_name: &str,
15 0 : ) -> Result<&'a str, ApiError> {
16 0 : match request.param(param_name) {
17 0 : Some(arg) => Ok(arg),
18 0 : None => Err(ApiError::BadRequest(anyhow!(
19 0 : "no {param_name} specified in path param",
20 0 : ))),
21 : }
22 0 : }
23 :
24 0 : pub fn parse_request_param<T: FromStr>(
25 0 : request: &Request<Body>,
26 0 : param_name: &str,
27 0 : ) -> Result<T, ApiError> {
28 0 : match get_request_param(request, param_name)?.parse() {
29 0 : Ok(v) => Ok(v),
30 0 : Err(_) => Err(ApiError::BadRequest(anyhow!(
31 0 : "failed to parse {param_name}",
32 0 : ))),
33 : }
34 0 : }
35 :
36 0 : pub fn get_query_param<'a>(
37 0 : request: &'a Request<Body>,
38 0 : param_name: &str,
39 0 : ) -> Result<Option<Cow<'a, str>>, ApiError> {
40 0 : let query = match request.uri().query() {
41 0 : Some(q) => q,
42 0 : None => return Ok(None),
43 : };
44 0 : let mut values = url::form_urlencoded::parse(query.as_bytes())
45 0 : .filter_map(|(k, v)| if k == param_name { Some(v) } else { None })
46 0 : // we call .next() twice below. If it's None the first time, .fuse() ensures it's None afterwards
47 0 : .fuse();
48 0 :
49 0 : let value1 = values.next();
50 0 : if values.next().is_some() {
51 0 : return Err(ApiError::BadRequest(anyhow!(
52 0 : "param {param_name} specified more than once"
53 0 : )));
54 0 : }
55 0 : Ok(value1)
56 0 : }
57 :
58 0 : pub fn must_get_query_param<'a>(
59 0 : request: &'a Request<Body>,
60 0 : param_name: &str,
61 0 : ) -> Result<Cow<'a, str>, ApiError> {
62 0 : get_query_param(request, param_name)?.ok_or_else(|| {
63 0 : ApiError::BadRequest(anyhow!("no {param_name} specified in query parameters"))
64 0 : })
65 0 : }
66 :
67 0 : pub fn parse_query_param<E: fmt::Display, T: FromStr<Err = E>>(
68 0 : request: &Request<Body>,
69 0 : param_name: &str,
70 0 : ) -> Result<Option<T>, ApiError> {
71 0 : get_query_param(request, param_name)?
72 0 : .map(|v| {
73 0 : v.parse().map_err(|e| {
74 0 : ApiError::BadRequest(anyhow!("cannot parse query param {param_name}: {e}"))
75 0 : })
76 0 : })
77 0 : .transpose()
78 0 : }
79 :
80 0 : pub fn must_parse_query_param<E: fmt::Display, T: FromStr<Err = E>>(
81 0 : request: &Request<Body>,
82 0 : param_name: &str,
83 0 : ) -> Result<T, ApiError> {
84 0 : parse_query_param(request, param_name)?.ok_or_else(|| {
85 0 : ApiError::BadRequest(anyhow!("no {param_name} specified in query parameters"))
86 0 : })
87 0 : }
88 :
89 0 : pub async fn ensure_no_body(request: &mut Request<Body>) -> Result<(), ApiError> {
90 0 : match request.body_mut().data().await {
91 0 : Some(_) => Err(ApiError::BadRequest(anyhow!("Unexpected request body"))),
92 0 : None => Ok(()),
93 : }
94 0 : }
|