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 #[instrument(skip(token))]
67 pub fn build(
68 self,
69 client: &Client,
70 token: &str,
71 proxy: Option<&str>,
72 ) -> Result<ReqwestRequestBuilder> {
73 let mut path = self.route.path().to_string();
74
75 if let Some(proxy) = proxy {
76 path = path.replace("https://discord.com", proxy.trim_end_matches('/'));
78 }
79
80 if let Some(params) = self.params {
81 path += "?";
82 for (param, value) in params {
83 write!(path, "&{param}={value}").unwrap();
84 }
85 }
86
87 let mut builder = client
88 .request(self.method.reqwest_method(), Url::parse(&path).map_err(HttpError::Url)?);
89
90 let mut headers = self.headers.unwrap_or_default();
91 headers.insert(USER_AGENT, HeaderValue::from_static(constants::USER_AGENT));
92 headers
93 .insert(AUTHORIZATION, HeaderValue::from_str(token).map_err(HttpError::InvalidHeader)?);
94
95 if let Some(multipart) = self.multipart {
96 builder = builder.multipart(multipart.build_form()?);
98 } else if let Some(bytes) = self.body {
99 headers.insert(CONTENT_LENGTH, bytes.len().into());
100 headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
101 builder = builder.body(bytes);
102 } else {
103 headers.insert(CONTENT_LENGTH, 0.into()); }
105
106 Ok(builder.headers(headers))
107 }
108
109 #[must_use]
110 pub fn body_ref(&self) -> Option<&[u8]> {
111 self.body.as_deref()
112 }
113
114 #[must_use]
115 pub fn body_mut(&mut self) -> Option<&mut [u8]> {
116 self.body.as_deref_mut()
117 }
118
119 #[must_use]
120 pub fn headers_ref(&self) -> &Option<Headers> {
121 &self.headers
122 }
123
124 #[must_use]
125 pub fn headers_mut(&mut self) -> &mut Option<Headers> {
126 &mut self.headers
127 }
128
129 #[must_use]
130 pub fn method_ref(&self) -> &LightMethod {
131 &self.method
132 }
133
134 #[must_use]
135 pub fn route_ref(&self) -> &Route<'_> {
136 &self.route
137 }
138
139 #[must_use]
140 pub fn params_ref(&self) -> Option<&[(&'static str, String)]> {
141 self.params.as_deref()
142 }
143
144 #[must_use]
145 pub fn params_mut(&mut self) -> Option<&mut [(&'static str, String)]> {
146 self.params.as_deref_mut()
147 }
148}