tower_http_client/client/
request_ext.rs

1//! Extensions for the `http::request::Builder`.
2
3use private::Sealed;
4use thiserror::Error;
5
6/// Set body errors.
7#[derive(Debug, Error)]
8#[error(transparent)]
9pub enum SetBodyError<S> {
10    /// An error occurred while setting the body.
11    Body(http::Error),
12    /// An error occurred while encoding the body.
13    Encode(S),
14}
15
16/// Extension trait for the [`http::request::Builder`].
17pub trait RequestBuilderExt: Sized + Sealed {
18    /// Sets a JSON body for this request.
19    ///
20    /// Additionally this method adds a `CONTENT_TYPE` header for JSON body.
21    /// If you decide to override the request body, keep this in mind.
22    ///
23    /// # Errors
24    ///
25    /// If the given value's implementation of [`serde::Serialize`] decides to fail.
26    #[cfg(feature = "json")]
27    #[cfg_attr(docsrs, doc(cfg(feature = "json")))]
28    fn json<T: serde::Serialize + ?Sized>(
29        self,
30        value: &T,
31    ) -> Result<http::Request<bytes::Bytes>, SetBodyError<serde_json::Error>>;
32
33    /// Sets a form body for this request.
34    ///
35    /// Additionally this method adds a `CONTENT_TYPE` header for form body.
36    /// If you decide to override the request body, keep this in mind.
37    ///
38    /// # Errors
39    ///
40    /// If the given value's implementation of [`serde::Serialize`] decides to fail.
41    #[cfg(feature = "form")]
42    #[cfg_attr(docsrs, doc(cfg(feature = "form")))]
43    fn form<T: serde::Serialize + ?Sized>(
44        self,
45        form: &T,
46    ) -> Result<http::Request<String>, SetBodyError<serde_urlencoded::ser::Error>>;
47
48    /// Appends a typed header to this request.
49    ///
50    /// This function will append the provided header as a header to the
51    /// internal [`http::HeaderMap`] being constructed.  Essentially this is
52    /// equivalent to calling [`headers::HeaderMapExt::typed_insert`].
53    #[must_use]
54    #[cfg(feature = "typed-header")]
55    #[cfg_attr(docsrs, doc(cfg(feature = "typed_header")))]
56    fn typed_header<T>(self, header: T) -> Self
57    where
58        T: headers::Header;
59}
60
61impl RequestBuilderExt for http::request::Builder {
62    #[cfg(feature = "json")]
63    #[cfg_attr(docsrs, doc(cfg(feature = "json")))]
64    fn json<T: serde::Serialize + ?Sized>(
65        mut self,
66        value: &T,
67    ) -> Result<http::Request<bytes::Bytes>, SetBodyError<serde_json::Error>> {
68        use http::{HeaderValue, header::CONTENT_TYPE};
69
70        if let Some(headers) = self.headers_mut() {
71            headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
72        }
73
74        let bytes = bytes::Bytes::from(serde_json::to_vec(value).map_err(SetBodyError::Encode)?);
75        self.body(bytes).map_err(SetBodyError::Body)
76    }
77
78    #[cfg(feature = "form")]
79    #[cfg_attr(docsrs, doc(cfg(feature = "form")))]
80    fn form<T: serde::Serialize + ?Sized>(
81        mut self,
82        form: &T,
83    ) -> Result<http::Request<String>, SetBodyError<serde_urlencoded::ser::Error>> {
84        use http::{HeaderValue, header::CONTENT_TYPE};
85
86        let string = serde_urlencoded::to_string(form).map_err(SetBodyError::Encode)?;
87        if let Some(headers) = self.headers_mut() {
88            headers.insert(
89                CONTENT_TYPE,
90                HeaderValue::from_static("application/x-www-form-urlencoded"),
91            );
92        }
93        self.body(string).map_err(SetBodyError::Body)
94    }
95
96    #[cfg(feature = "typed-header")]
97    #[cfg_attr(docsrs, doc(cfg(feature = "typed_header")))]
98    fn typed_header<T>(mut self, header: T) -> Self
99    where
100        T: headers::Header,
101    {
102        use headers::HeaderMapExt;
103
104        if let Some(headers) = self.headers_mut() {
105            headers.typed_insert(header);
106        }
107        self
108    }
109}
110
111mod private {
112    pub trait Sealed {}
113
114    impl Sealed for http::request::Builder {}
115}