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