http_request_derive/
http_request.rs1use bytes::Bytes;
6use http::{HeaderMap, Uri};
7use snafu::{OptionExt, ResultExt};
8use url::Url;
9
10use crate::{
11 Error, FromHttpResponse, HttpRequestBody, HttpRequestQueryParams,
12 error::{BuildRequestSnafu, ParseUriSnafu, UrlCannotBeABaseSnafu},
13};
14
15pub trait HttpRequest {
17 type Response: FromHttpResponse;
19
20 type Query: HttpRequestQueryParams;
22
23 type Body: HttpRequestBody;
25
26 const METHOD: http::Method;
28
29 fn path(&self) -> String;
31
32 fn query(&self) -> Option<&Self::Query> {
34 None
35 }
36
37 fn body(&self) -> Option<&Self::Body> {
39 None
40 }
41
42 fn apply_headers(&self, headers: &mut HeaderMap) {
44 if let Some(body) = self.body() {
45 body.apply_headers(headers);
46 }
47 }
48
49 fn to_http_request(&self, base_url: &Url) -> Result<http::request::Request<Vec<u8>>, Error> {
51 let body = self
52 .body()
53 .map(HttpRequestBody::to_vec)
54 .transpose()?
55 .unwrap_or_default();
56
57 let uri = {
58 let mut url = base_url.clone();
59 {
60 let mut segments =
61 url.path_segments_mut()
62 .ok()
63 .with_context(|| UrlCannotBeABaseSnafu {
64 url: base_url.clone(),
65 })?;
66 let _ = segments
67 .pop_if_empty()
68 .extend(self.path().split('/').skip_while(|s| s.is_empty()));
69 }
70
71 let query = self
72 .query()
73 .map(HttpRequestQueryParams::http_request_query_string)
74 .transpose()?;
75 if let Some(query) = query {
76 let query = query.as_deref();
77 url.set_query(query);
78 }
79
80 url.as_str().parse::<Uri>().context(ParseUriSnafu)?
81 };
82
83 let mut headers = HeaderMap::new();
84 self.apply_headers(&mut headers);
85
86 let mut builder = http::request::Request::builder()
87 .method(Self::METHOD)
88 .uri(uri);
89
90 for (name, value) in &headers {
91 builder = builder.header(name, value);
92 }
93
94 builder.body(body).context(BuildRequestSnafu)
95 }
96
97 fn read_response(response: http::Response<Bytes>) -> Result<Self::Response, Error> {
99 Self::Response::from_http_response(response)
100 }
101}