twilight_http/client/
builder.rs1use super::Token;
2use crate::{Client, client::connector};
3use http::header::HeaderMap;
4use hyper_util::rt::TokioExecutor;
5use std::{
6 sync::{Arc, atomic::AtomicBool},
7 time::Duration,
8};
9use twilight_http_ratelimiting::RateLimiter;
10use twilight_model::channel::message::AllowedMentions;
11
12#[derive(Debug)]
14#[must_use = "has no effect if not built into a Client"]
15pub struct ClientBuilder {
16 pub(crate) default_allowed_mentions: Option<AllowedMentions>,
17 pub(crate) proxy: Option<Box<str>>,
18 ratelimiter: Option<RateLimiter>,
19 remember_invalid_token: bool,
20 pub(crate) default_headers: Option<HeaderMap>,
21 pub(crate) timeout: Duration,
22 pub(super) token: Option<Token>,
23 pub(crate) use_http: bool,
24}
25
26impl ClientBuilder {
27 pub fn new() -> Self {
29 Self::default()
30 }
31
32 pub fn build(self) -> Client {
34 let connector = connector::create();
35
36 let http =
37 hyper_util::client::legacy::Client::builder(TokioExecutor::new()).build(connector);
38
39 let token_invalidated = if self.remember_invalid_token {
40 Some(Arc::new(AtomicBool::new(false)))
41 } else {
42 None
43 };
44
45 Client {
46 http,
47 default_headers: self.default_headers,
48 proxy: self.proxy,
49 ratelimiter: self.ratelimiter,
50 timeout: self.timeout,
51 token_invalidated,
52 token: self.token,
53 default_allowed_mentions: self.default_allowed_mentions,
54 use_http: self.use_http,
55 }
56 }
57
58 pub fn default_allowed_mentions(mut self, allowed_mentions: AllowedMentions) -> Self {
61 self.default_allowed_mentions.replace(allowed_mentions);
62
63 self
64 }
65
66 pub fn proxy(mut self, proxy_url: String, use_http: bool) -> Self {
87 self.proxy.replace(proxy_url.into_boxed_str());
88 self.use_http = use_http;
89
90 self
91 }
92
93 #[allow(clippy::missing_const_for_fn)]
101 pub fn ratelimiter(mut self, ratelimiter: Option<RateLimiter>) -> Self {
102 self.ratelimiter = ratelimiter;
103
104 self
105 }
106
107 pub const fn timeout(mut self, duration: Duration) -> Self {
111 self.timeout = duration;
112
113 self
114 }
115
116 pub fn default_headers(mut self, headers: HeaderMap) -> Self {
118 self.default_headers.replace(headers);
119
120 self
121 }
122
123 pub const fn remember_invalid_token(mut self, remember: bool) -> Self {
131 self.remember_invalid_token = remember;
132
133 self
134 }
135
136 pub fn token(mut self, mut token: String) -> Self {
138 let is_bot = token.starts_with("Bot ");
139 let is_bearer = token.starts_with("Bearer ");
140
141 if !is_bot && !is_bearer {
144 token.insert_str(0, "Bot ");
145 }
146
147 self.token.replace(Token::new(token.into_boxed_str()));
148
149 self
150 }
151}
152
153impl Default for ClientBuilder {
154 fn default() -> Self {
155 Self {
156 default_allowed_mentions: None,
157 default_headers: None,
158 proxy: None,
159 ratelimiter: (!cfg!(test)).then(RateLimiter::default),
160 remember_invalid_token: true,
161 timeout: Duration::from_secs(10),
162 token: None,
163 use_http: false,
164 }
165 }
166}
167
168#[cfg(test)]
169mod tests {
170 use super::ClientBuilder;
171 use static_assertions::assert_impl_all;
172 use std::fmt::Debug;
173
174 assert_impl_all!(ClientBuilder: Debug, Default, Send, Sync);
175
176 #[test]
177 fn client_debug() {
178 assert!(
179 format!("{:?}", ClientBuilder::new().token("Bot foo".to_owned()))
180 .contains("token: Some(<redacted>)")
181 );
182 assert!(format!("{:?}", ClientBuilder::new()).contains("token: None"));
183 }
184}