pkarr_client/client/
builder.rs1use std::sync::Arc;
2
3#[cfg(relays)]
4use std::time::Duration;
5
6#[cfg(feature = "relays")]
7use url::Url;
8
9#[cfg(dht)]
10use crate::mainline;
11use crate::{errors::BuildError, Client};
12use crate::{Cache, DEFAULT_CACHE_SIZE, DEFAULT_MAXIMUM_TTL, DEFAULT_MINIMUM_TTL};
13
14#[cfg(feature = "endpoints")]
15pub const DEFAULT_MAX_RECURSION_DEPTH: u8 = 7;
16
17#[cfg(relays)]
19pub const DEFAULT_REQUEST_TIMEOUT: Duration = Duration::from_secs(2);
20
21#[derive(Clone)]
23pub(crate) struct Config {
24 pub cache_size: usize,
28 pub minimum_ttl: u32,
32 pub maximum_ttl: u32,
36 pub cache: Option<Arc<dyn Cache>>,
38
39 #[cfg(dht)]
40 pub dht: Option<mainline::DhtBuilder>,
41
42 #[cfg(feature = "relays")]
44 pub relays: Option<Vec<Url>>,
45
46 #[cfg(feature = "relays")]
52 pub request_timeout: Duration,
53
54 #[cfg(feature = "endpoints")]
55 pub max_recursion_depth: u8,
56}
57
58impl Default for Config {
59 fn default() -> Self {
60 Self {
61 cache_size: DEFAULT_CACHE_SIZE,
62 minimum_ttl: DEFAULT_MINIMUM_TTL,
63 maximum_ttl: DEFAULT_MAXIMUM_TTL,
64 cache: None,
65
66 #[cfg(dht)]
67 dht: Some(mainline::Dht::builder()),
68
69 #[cfg(feature = "relays")]
70 relays: Some(
71 crate::DEFAULT_RELAYS
72 .iter()
73 .map(|s| {
74 Url::parse(s).expect("DEFAULT_RELAYS should be parsed to Url successfully.")
75 })
76 .collect(),
77 ),
78
79 #[cfg(relays)]
80 request_timeout: DEFAULT_REQUEST_TIMEOUT,
81
82 #[cfg(feature = "endpoints")]
83 max_recursion_depth: DEFAULT_MAX_RECURSION_DEPTH,
84 }
85 }
86}
87
88impl std::fmt::Debug for Config {
89 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
90 let mut debug_struct = f.debug_struct("Config");
91
92 debug_struct.field("cache_size", &self.cache_size);
93 debug_struct.field("minimum_ttl", &self.minimum_ttl);
94 debug_struct.field("maximum_ttl", &self.maximum_ttl);
95 debug_struct.field("cache", &self.cache);
96
97 #[cfg(dht)]
98 debug_struct.field("dht", &self.dht);
99 #[cfg(dht)]
100 #[cfg(feature = "relays")]
101 debug_struct.field(
102 "relays",
103 &self
104 .relays
105 .as_ref()
106 .map(|urls| urls.iter().map(|url| url.as_str()).collect::<Vec<_>>()),
107 );
108
109 #[cfg(feature = "relays")]
110 debug_struct.field("request_timeout", &self.request_timeout);
111
112 debug_struct.finish()
113 }
114}
115
116#[derive(Debug, Default, Clone)]
118pub struct ClientBuilder(Config);
119
120impl ClientBuilder {
121 pub fn no_default_network(&mut self) -> &mut Self {
128 self.no_dht();
129 self.no_relays();
130
131 self
132 }
133
134 pub fn no_dht(&mut self) -> &mut Self {
136 #[cfg(dht)]
137 {
138 self.0.dht = None;
139 }
140
141 self
142 }
143
144 #[cfg(dht)]
145 pub fn dht<F>(&mut self, f: F) -> &mut Self
147 where
148 F: FnOnce(&mut mainline::DhtBuilder) -> &mut mainline::DhtBuilder,
149 {
150 if self.0.dht.is_none() {
151 self.0.dht = Some(Default::default());
152 }
153
154 if let Some(ref mut builder) = self.0.dht {
155 f(builder);
156 };
157
158 self
159 }
160
161 #[cfg(dht)]
168 pub fn bootstrap<T: ToString>(&mut self, bootstrap: &[T]) -> &mut Self {
169 self.dht(|b| b.bootstrap(bootstrap));
170
171 self
172 }
173
174 #[cfg(dht)]
175 pub fn extra_bootstrap<T: ToString>(&mut self, bootstrap: &[T]) -> &mut Self {
180 self.dht(|b| b.extra_bootstrap(bootstrap));
181
182 self
183 }
184
185 pub fn no_relays(&mut self) -> &mut Self {
187 #[cfg(feature = "relays")]
188 {
189 self.0.relays = None;
190 }
191
192 self
193 }
194
195 #[cfg(feature = "relays")]
199 pub fn relays<T: reqwest::IntoUrl + Clone>(
200 &mut self,
201 relays: &[T],
202 ) -> Result<&mut Self, InvalidRelayUrl> {
203 self.0.relays = Some(into_urls(relays)?);
204
205 Ok(self)
206 }
207
208 #[cfg(feature = "relays")]
209 pub fn extra_relays<T: reqwest::IntoUrl + Clone>(
213 &mut self,
214 relays: &[T],
215 ) -> Result<&mut Self, InvalidRelayUrl> {
216 if let Some(ref mut existing) = self.0.relays {
217 for relay in into_urls(relays)? {
218 if !existing.contains(&relay) {
219 existing.push(relay)
220 }
221 }
222 }
223
224 Ok(self)
225 }
226
227 pub fn cache_size(&mut self, cache_size: usize) -> &mut Self {
231 self.0.cache_size = cache_size;
232
233 self
234 }
235
236 pub fn minimum_ttl(&mut self, ttl: u32) -> &mut Self {
240 self.0.minimum_ttl = ttl;
241 self.0.maximum_ttl = self.0.maximum_ttl.max(ttl);
242
243 self
244 }
245
246 pub fn maximum_ttl(&mut self, ttl: u32) -> &mut Self {
250 self.0.maximum_ttl = ttl;
251 self.0.minimum_ttl = self.0.minimum_ttl.min(ttl);
252
253 self
254 }
255
256 pub fn cache(&mut self, cache: Arc<dyn Cache>) -> &mut Self {
258 self.0.cache = Some(cache);
259
260 self
261 }
262
263 #[cfg(feature = "relays")]
268 pub fn request_timeout(&mut self, timeout: Duration) -> &mut Self {
269 self.0.request_timeout = timeout;
270
271 self
272 }
273
274 #[cfg(feature = "endpoints")]
275 pub fn max_recursion_depth(&mut self, max_recursion_depth: u8) -> &mut Self {
281 self.0.max_recursion_depth = max_recursion_depth;
282
283 self
284 }
285
286 pub fn build(&self) -> Result<Client, BuildError> {
288 Client::new(self.0.clone())
289 }
290
291 #[cfg(dht)]
295 pub async fn build_async(&self) -> Result<Client, BuildError> {
296 Client::new_async(self.0.clone()).await
297 }
298}
299
300#[cfg(relays)]
301fn into_urls<T: reqwest::IntoUrl + Clone>(relays: &[T]) -> Result<Vec<Url>, InvalidRelayUrl> {
302 relays
303 .iter()
304 .map(|url| match url.clone().into_url() {
305 Err(e) => Err(InvalidRelayUrl::Parse(e)),
306 Ok(url) => {
307 if url.scheme() != "http" && url.scheme() != "https" {
308 Err(InvalidRelayUrl::NotHttp(url.to_string()))
309 } else {
310 Ok(url)
311 }
312 }
313 })
314 .collect::<Result<Vec<Url>, InvalidRelayUrl>>()
315}
316
317#[cfg(relays)]
318#[derive(thiserror::Error, Debug)]
319pub enum InvalidRelayUrl {
321 #[error("Failed to parse into a Url: {0}")]
322 Parse(reqwest::Error),
324
325 #[error("Relays Urls should have `http` or `https`: {0}")]
326 NotHttp(String),
328}