1use std::fmt::Write;
2
3use reqwest::header::{
4 HeaderMap as Headers,
5 HeaderValue,
6 AUTHORIZATION,
7 CONTENT_LENGTH,
8 CONTENT_TYPE,
9 USER_AGENT,
10};
11use reqwest::{Client, RequestBuilder as ReqwestRequestBuilder, Url};
12use tracing::instrument;
13
14use super::multipart::Multipart;
15use super::routing::Route;
16use super::{HttpError, LightMethod};
17use crate::constants;
18use crate::internal::prelude::*;
19
20#[deprecated = "use Request directly now"]
21pub type RequestBuilder<'a> = Request<'a>;
22
23#[derive(Clone, Debug)]
24#[must_use]
25pub struct Request<'a> {
26 pub(super) body: Option<Vec<u8>>,
27 pub(super) multipart: Option<Multipart>,
28 pub(super) headers: Option<Headers>,
29 pub(super) method: LightMethod,
30 pub(super) route: Route<'a>,
31 pub(super) params: Option<Vec<(&'static str, String)>>,
32}
33
34impl<'a> Request<'a> {
35 pub const fn new(route: Route<'a>, method: LightMethod) -> Self {
36 Self {
37 body: None,
38 multipart: None,
39 headers: None,
40 method,
41 route,
42 params: None,
43 }
44 }
45
46 pub fn body(mut self, body: Option<Vec<u8>>) -> Self {
47 self.body = body;
48 self
49 }
50
51 pub fn multipart(mut self, multipart: Option<Multipart>) -> Self {
52 self.multipart = multipart;
53 self
54 }
55
56 pub fn headers(mut self, headers: Option<Headers>) -> Self {
57 self.headers = headers;
58 self
59 }
60
61 pub fn params(mut self, params: Option<Vec<(&'static str, String)>>) -> Self {
62 self.params = params;
63 self
64 }
65
66 #[allow(clippy::missing_errors_doc)]
67 #[instrument(skip(token))]
68 pub fn build(
69 self,
70 client: &Client,
71 token: &str,
72 proxy: Option<&str>,
73 ) -> Result<ReqwestRequestBuilder> {
74 let mut path = self.route.path().to_string();
75
76 if let Some(proxy) = proxy {
77 path = path.replace("https://discord.com", proxy.trim_end_matches('/'));
79 }
80
81 if let Some(params) = self.params {
82 path += "?";
83 for (param, value) in params {
84 write!(path, "&{param}={value}").expect("writing to a string should never fail");
85 }
86 }
87
88 let mut builder = client
89 .request(self.method.reqwest_method(), Url::parse(&path).map_err(HttpError::Url)?);
90
91 let mut headers = self.headers.unwrap_or_default();
92 headers.insert(USER_AGENT, HeaderValue::from_static(constants::USER_AGENT));
93 headers
94 .insert(AUTHORIZATION, HeaderValue::from_str(token).map_err(HttpError::InvalidHeader)?);
95
96 if let Some(multipart) = self.multipart {
97 builder = builder.multipart(multipart.build_form()?);
99 } else if let Some(bytes) = self.body {
100 headers.insert(CONTENT_LENGTH, bytes.len().into());
101 headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
102 builder = builder.body(bytes);
103 } else {
104 headers.insert(CONTENT_LENGTH, 0.into()); }
106
107 Ok(builder.headers(headers))
108 }
109
110 #[must_use]
111 pub fn body_ref(&self) -> Option<&[u8]> {
112 self.body.as_deref()
113 }
114
115 #[must_use]
116 pub fn body_mut(&mut self) -> Option<&mut [u8]> {
117 self.body.as_deref_mut()
118 }
119
120 #[must_use]
121 pub fn headers_ref(&self) -> &Option<Headers> {
122 &self.headers
123 }
124
125 #[must_use]
126 pub fn headers_mut(&mut self) -> &mut Option<Headers> {
127 &mut self.headers
128 }
129
130 #[must_use]
131 pub fn method_ref(&self) -> &LightMethod {
132 &self.method
133 }
134
135 #[must_use]
136 pub fn route_ref(&self) -> &Route<'_> {
137 &self.route
138 }
139
140 #[must_use]
141 pub fn params_ref(&self) -> Option<&[(&'static str, String)]> {
142 self.params.as_deref()
143 }
144
145 #[must_use]
146 pub fn params_mut(&mut self) -> Option<&mut [(&'static str, String)]> {
147 self.params.as_deref_mut()
148 }
149}