1#[cfg(not(target_arch = "wasm32"))]
16use crate::blocking;
17use crate::client::ClientState;
18use crate::config::{ProxyConfig, SecurityConfig, ServiceConfig};
19use crate::weak_cache::Cached;
20use crate::{Client, HostMetricsRegistry, UserAgent};
21use arc_swap::ArcSwap;
22use conjure_error::Error;
23use conjure_http::client::ConjureRuntime;
24use std::sync::Arc;
25use std::time::Duration;
26#[cfg(not(target_arch = "wasm32"))]
27use tokio::runtime::Handle;
28use url::Url;
29use witchcraft_metrics::MetricRegistry;
30
31pub struct Builder<T = Complete>(T);
33
34pub struct ServiceStage(());
36
37pub struct UserAgentStage {
39 service: String,
40}
41
42#[derive(Clone, PartialEq, Eq, Hash)]
43pub(crate) struct CachedConfig {
44 service: String,
45 user_agent: UserAgent,
46 uris: Vec<Url>,
47 security: SecurityConfig,
48 proxy: ProxyConfig,
49 connect_timeout: Duration,
50 read_timeout: Duration,
51 write_timeout: Duration,
52 backoff_slot_size: Duration,
53 max_num_retries: u32,
54 client_qos: ClientQos,
55 server_qos: ServerQos,
56 service_error: ServiceError,
57 idempotency: Idempotency,
58 node_selection_strategy: NodeSelectionStrategy,
59 override_host_index: Option<usize>,
60}
61
62#[derive(Clone)]
63pub(crate) struct UncachedConfig {
64 pub(crate) metrics: Option<Arc<MetricRegistry>>,
65 pub(crate) host_metrics: Option<Arc<HostMetricsRegistry>>,
66 #[cfg(not(target_arch = "wasm32"))]
67 pub(crate) blocking_handle: Option<Handle>,
68 pub(crate) conjure_runtime: Arc<ConjureRuntime>,
69}
70
71pub struct Complete {
73 cached: CachedConfig,
74 uncached: UncachedConfig,
75}
76
77impl Default for Builder<ServiceStage> {
78 #[inline]
79 fn default() -> Self {
80 Builder::new()
81 }
82}
83
84impl Builder<ServiceStage> {
85 #[inline]
87 pub fn new() -> Self {
88 Builder(ServiceStage(()))
89 }
90
91 #[inline]
95 pub fn service(self, service: &str) -> Builder<UserAgentStage> {
96 Builder(UserAgentStage {
97 service: service.to_string(),
98 })
99 }
100}
101
102impl Builder<UserAgentStage> {
103 #[inline]
105 pub fn user_agent(self, user_agent: UserAgent) -> Builder {
106 Builder(Complete {
107 cached: CachedConfig {
108 service: self.0.service,
109 user_agent,
110 uris: vec![],
111 security: SecurityConfig::builder().build(),
112 proxy: ProxyConfig::Direct,
113 connect_timeout: Duration::from_secs(10),
114 read_timeout: Duration::from_secs(5 * 60),
115 write_timeout: Duration::from_secs(5 * 60),
116 backoff_slot_size: Duration::from_millis(250),
117 max_num_retries: 4,
118 client_qos: ClientQos::Enabled,
119 server_qos: ServerQos::AutomaticRetry,
120 service_error: ServiceError::WrapInNewError,
121 idempotency: Idempotency::ByMethod,
122 node_selection_strategy: NodeSelectionStrategy::PinUntilError,
123 override_host_index: None,
124 },
125 uncached: UncachedConfig {
126 metrics: None,
127 host_metrics: None,
128 #[cfg(not(target_arch = "wasm32"))]
129 blocking_handle: None,
130 conjure_runtime: Arc::new(ConjureRuntime::new()),
131 },
132 })
133 }
134}
135
136#[cfg(test)]
137impl Builder {
138 pub(crate) fn for_test() -> Self {
139 use crate::Agent;
140
141 Builder::new()
142 .service("test")
143 .user_agent(UserAgent::new(Agent::new("test", "0.0.0")))
144 }
145}
146
147impl Builder<Complete> {
148 pub(crate) fn cached_config(&self) -> &CachedConfig {
149 &self.0.cached
150 }
151
152 #[inline]
154 pub fn from_config(mut self, config: &ServiceConfig) -> Self {
155 self = self.uris(config.uris().to_vec());
156
157 if let Some(security) = config.security() {
158 self = self.security(security.clone());
159 }
160
161 if let Some(proxy) = config.proxy() {
162 self = self.proxy(proxy.clone());
163 }
164
165 if let Some(connect_timeout) = config.connect_timeout() {
166 self = self.connect_timeout(connect_timeout);
167 }
168
169 if let Some(read_timeout) = config.read_timeout() {
170 self = self.read_timeout(read_timeout);
171 }
172
173 if let Some(write_timeout) = config.write_timeout() {
174 self = self.write_timeout(write_timeout);
175 }
176
177 if let Some(backoff_slot_size) = config.backoff_slot_size() {
178 self = self.backoff_slot_size(backoff_slot_size);
179 }
180
181 if let Some(max_num_retries) = config.max_num_retries() {
182 self = self.max_num_retries(max_num_retries);
183 }
184
185 self
186 }
187
188 #[inline]
190 pub fn get_service(&self) -> &str {
191 &self.0.cached.service
192 }
193
194 #[inline]
196 pub fn get_user_agent(&self) -> &UserAgent {
197 &self.0.cached.user_agent
198 }
199
200 #[inline]
204 pub fn uri(mut self, uri: Url) -> Self {
205 self.0.cached.uris.push(uri);
206 self
207 }
208
209 #[inline]
213 pub fn uris(mut self, uris: Vec<Url>) -> Self {
214 self.0.cached.uris = uris;
215 self
216 }
217
218 #[inline]
220 pub fn get_uris(&self) -> &[Url] {
221 &self.0.cached.uris
222 }
223
224 #[inline]
228 pub fn security(mut self, security: SecurityConfig) -> Self {
229 self.0.cached.security = security;
230 self
231 }
232
233 #[inline]
235 pub fn get_security(&self) -> &SecurityConfig {
236 &self.0.cached.security
237 }
238
239 #[inline]
243 pub fn proxy(mut self, proxy: ProxyConfig) -> Self {
244 self.0.cached.proxy = proxy;
245 self
246 }
247
248 #[inline]
250 pub fn get_proxy(&self) -> &ProxyConfig {
251 &self.0.cached.proxy
252 }
253
254 #[inline]
258 pub fn connect_timeout(mut self, connect_timeout: Duration) -> Self {
259 self.0.cached.connect_timeout = connect_timeout;
260 self
261 }
262
263 #[inline]
265 pub fn get_connect_timeout(&self) -> Duration {
266 self.0.cached.connect_timeout
267 }
268
269 #[inline]
275 pub fn read_timeout(mut self, read_timeout: Duration) -> Self {
276 self.0.cached.read_timeout = read_timeout;
277 self
278 }
279
280 #[inline]
282 pub fn get_read_timeout(&self) -> Duration {
283 self.0.cached.read_timeout
284 }
285
286 #[inline]
292 pub fn write_timeout(mut self, write_timeout: Duration) -> Self {
293 self.0.cached.write_timeout = write_timeout;
294 self
295 }
296
297 #[inline]
299 pub fn get_write_timeout(&self) -> Duration {
300 self.0.cached.write_timeout
301 }
302
303 #[inline]
310 pub fn backoff_slot_size(mut self, backoff_slot_size: Duration) -> Self {
311 self.0.cached.backoff_slot_size = backoff_slot_size;
312 self
313 }
314
315 #[inline]
317 pub fn get_backoff_slot_size(&self) -> Duration {
318 self.0.cached.backoff_slot_size
319 }
320
321 #[inline]
325 pub fn max_num_retries(mut self, max_num_retries: u32) -> Self {
326 self.0.cached.max_num_retries = max_num_retries;
327 self
328 }
329
330 #[inline]
332 pub fn get_max_num_retries(&self) -> u32 {
333 self.0.cached.max_num_retries
334 }
335
336 #[inline]
340 pub fn client_qos(mut self, client_qos: ClientQos) -> Self {
341 self.0.cached.client_qos = client_qos;
342 self
343 }
344
345 #[inline]
347 pub fn get_client_qos(&self) -> ClientQos {
348 self.0.cached.client_qos
349 }
350
351 #[inline]
355 pub fn server_qos(mut self, server_qos: ServerQos) -> Self {
356 self.0.cached.server_qos = server_qos;
357 self
358 }
359
360 #[inline]
362 pub fn get_server_qos(&self) -> ServerQos {
363 self.0.cached.server_qos
364 }
365
366 #[inline]
370 pub fn service_error(mut self, service_error: ServiceError) -> Self {
371 self.0.cached.service_error = service_error;
372 self
373 }
374
375 #[inline]
377 pub fn get_service_error(&self) -> ServiceError {
378 self.0.cached.service_error
379 }
380
381 #[inline]
387 pub fn idempotency(mut self, idempotency: Idempotency) -> Self {
388 self.0.cached.idempotency = idempotency;
389 self
390 }
391
392 #[inline]
394 pub fn get_idempotency(&self) -> Idempotency {
395 self.0.cached.idempotency
396 }
397
398 #[inline]
402 pub fn node_selection_strategy(
403 mut self,
404 node_selection_strategy: NodeSelectionStrategy,
405 ) -> Self {
406 self.0.cached.node_selection_strategy = node_selection_strategy;
407 self
408 }
409
410 #[inline]
412 pub fn get_node_selection_strategy(&self) -> NodeSelectionStrategy {
413 self.0.cached.node_selection_strategy
414 }
415
416 #[inline]
420 pub fn metrics(mut self, metrics: Arc<MetricRegistry>) -> Self {
421 self.0.uncached.metrics = Some(metrics);
422 self
423 }
424
425 #[inline]
427 pub fn get_metrics(&self) -> Option<&Arc<MetricRegistry>> {
428 self.0.uncached.metrics.as_ref()
429 }
430
431 #[inline]
435 pub fn host_metrics(mut self, host_metrics: Arc<HostMetricsRegistry>) -> Self {
436 self.0.uncached.host_metrics = Some(host_metrics);
437 self
438 }
439
440 #[inline]
442 pub fn get_host_metrics(&self) -> Option<&Arc<HostMetricsRegistry>> {
443 self.0.uncached.host_metrics.as_ref()
444 }
445
446 #[inline]
450 pub fn conjure_runtime(mut self, conjure_runtime: Arc<ConjureRuntime>) -> Self {
451 self.0.uncached.conjure_runtime = conjure_runtime;
452 self
453 }
454
455 pub fn get_conjure_runtime(&self) -> &Arc<ConjureRuntime> {
457 &self.0.uncached.conjure_runtime
458 }
459
460 #[inline]
462 pub fn override_host_index(mut self, override_host_index: usize) -> Self {
463 self.0.cached.override_host_index = Some(override_host_index);
464 self
465 }
466
467 #[inline]
469 pub fn get_override_host_index(&self) -> Option<usize> {
470 self.0.cached.override_host_index
471 }
472
473 pub fn build(&self) -> Result<Client, Error> {
475 let state = ClientState::new(self)?;
476 Ok(Client::new(
477 Arc::new(ArcSwap::new(Arc::new(Cached::uncached(state)))),
478 None,
479 ))
480 }
481}
482
483#[cfg(not(target_arch = "wasm32"))]
484impl Builder<Complete> {
485 #[inline]
491 pub fn blocking_handle(mut self, blocking_handle: Handle) -> Self {
492 self.0.uncached.blocking_handle = Some(blocking_handle);
493 self
494 }
495
496 #[inline]
498 pub fn get_blocking_handle(&self) -> Option<&Handle> {
499 self.0.uncached.blocking_handle.as_ref()
500 }
501
502 pub fn build_blocking(&self) -> Result<blocking::Client, Error> {
504 self.build().map(|client| blocking::Client {
505 client,
506 handle: self.0.uncached.blocking_handle.clone(),
507 })
508 }
509}
510
511#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
513#[non_exhaustive]
514pub enum ClientQos {
515 Enabled,
519
520 DangerousDisableSympatheticClientQos,
525}
526
527#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
531#[non_exhaustive]
532pub enum ServerQos {
533 AutomaticRetry,
537
538 Propagate429And503ToCaller,
543}
544
545#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
549#[non_exhaustive]
550pub enum ServiceError {
551 WrapInNewError,
558
559 PropagateToCaller,
564}
565
566#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
568#[non_exhaustive]
569pub enum Idempotency {
570 Always,
572
573 ByMethod,
578
579 Never,
581}
582
583#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
585#[non_exhaustive]
586pub enum NodeSelectionStrategy {
587 PinUntilError,
595
596 PinUntilErrorWithoutReshuffle,
598
599 Balanced,
601}