google_cloud_gax/options/
mod.rs1use crate::backoff_policy::{BackoffPolicy, BackoffPolicyArg};
29use crate::polling_backoff_policy::{PollingBackoffPolicy, PollingBackoffPolicyArg};
30use crate::polling_policy::{PollingPolicy, PollingPolicyArg};
31use crate::retry_policy::{RetryPolicy, RetryPolicyArg};
32use crate::retry_throttler::{RetryThrottlerArg, RetryThrottlerWrapped};
33use auth::credentials::Credential;
34use std::sync::Arc;
35
36#[derive(Clone, Debug, Default)]
44pub struct RequestOptions {
45 pub(crate) idempotent: Option<bool>,
46 user_agent: Option<String>,
47 attempt_timeout: Option<std::time::Duration>,
48 pub(crate) retry_policy: Option<Arc<dyn RetryPolicy>>,
49 pub(crate) backoff_policy: Option<Arc<dyn BackoffPolicy>>,
50 pub(crate) retry_throttler: Option<RetryThrottlerWrapped>,
51 pub(crate) polling_policy: Option<Arc<dyn PollingPolicy>>,
52 pub(crate) polling_backoff_policy: Option<Arc<dyn PollingBackoffPolicy>>,
53}
54
55impl RequestOptions {
56 pub fn set_idempotency(&mut self, value: bool) {
68 self.idempotent = Some(value);
69 }
70
71 pub fn set_default_idempotency(mut self, default: bool) -> Self {
77 self.idempotent.get_or_insert(default);
78 self
79 }
80
81 pub fn set_user_agent<T: Into<String>>(&mut self, v: T) {
83 self.user_agent = Some(v.into());
84 }
85
86 pub fn user_agent(&self) -> &Option<String> {
88 &self.user_agent
89 }
90
91 pub fn set_attempt_timeout<T: Into<std::time::Duration>>(&mut self, v: T) {
96 self.attempt_timeout = Some(v.into());
97 }
98
99 pub fn attempt_timeout(&self) -> &Option<std::time::Duration> {
101 &self.attempt_timeout
102 }
103
104 pub fn set_retry_policy<V: Into<RetryPolicyArg>>(&mut self, v: V) {
106 self.retry_policy = Some(v.into().0);
107 }
108
109 pub fn set_backoff_policy<V: Into<BackoffPolicyArg>>(&mut self, v: V) {
111 self.backoff_policy = Some(v.into().0);
112 }
113
114 pub fn set_retry_throttler<V: Into<RetryThrottlerArg>>(&mut self, v: V) {
116 self.retry_throttler = Some(v.into().0);
117 }
118
119 pub fn set_polling_policy<V: Into<PollingPolicyArg>>(&mut self, v: V) {
121 self.polling_policy = Some(v.into().0);
122 }
123
124 pub fn set_polling_backoff_policy<V: Into<PollingBackoffPolicyArg>>(&mut self, v: V) {
126 self.polling_backoff_policy = Some(v.into().0);
127 }
128}
129
130pub trait RequestOptionsBuilder {
137 fn with_idempotency(self, v: bool) -> Self;
139
140 fn with_user_agent<V: Into<String>>(self, v: V) -> Self;
142
143 fn with_attempt_timeout<V: Into<std::time::Duration>>(self, v: V) -> Self;
148
149 fn with_retry_policy<V: Into<RetryPolicyArg>>(self, v: V) -> Self;
151
152 fn with_backoff_policy<V: Into<BackoffPolicyArg>>(self, v: V) -> Self;
154
155 fn with_retry_throttler<V: Into<RetryThrottlerArg>>(self, v: V) -> Self;
157
158 fn with_polling_policy<V: Into<PollingPolicyArg>>(self, v: V) -> Self;
160
161 fn with_polling_backoff_policy<V: Into<PollingBackoffPolicyArg>>(self, v: V) -> Self;
163}
164
165pub trait RequestBuilder {
171 fn request_options(&mut self) -> &mut RequestOptions;
172}
173
174impl<T> RequestOptionsBuilder for T
177where
178 T: RequestBuilder,
179{
180 fn with_idempotency(mut self, v: bool) -> Self {
181 self.request_options().set_idempotency(v);
182 self
183 }
184
185 fn with_user_agent<V: Into<String>>(mut self, v: V) -> Self {
186 self.request_options().set_user_agent(v);
187 self
188 }
189
190 fn with_attempt_timeout<V: Into<std::time::Duration>>(mut self, v: V) -> Self {
191 self.request_options().set_attempt_timeout(v);
192 self
193 }
194
195 fn with_retry_policy<V: Into<RetryPolicyArg>>(mut self, v: V) -> Self {
196 self.request_options().set_retry_policy(v);
197 self
198 }
199
200 fn with_backoff_policy<V: Into<BackoffPolicyArg>>(mut self, v: V) -> Self {
201 self.request_options().set_backoff_policy(v);
202 self
203 }
204
205 fn with_retry_throttler<V: Into<RetryThrottlerArg>>(mut self, v: V) -> Self {
206 self.request_options().set_retry_throttler(v);
207 self
208 }
209
210 fn with_polling_policy<V: Into<PollingPolicyArg>>(mut self, v: V) -> Self {
211 self.request_options().set_polling_policy(v);
212 self
213 }
214
215 fn with_polling_backoff_policy<V: Into<PollingBackoffPolicyArg>>(mut self, v: V) -> Self {
216 self.request_options().set_polling_backoff_policy(v);
217 self
218 }
219}
220
221pub struct ClientConfig {
229 pub(crate) endpoint: Option<String>,
230 pub(crate) cred: Option<Credential>,
231 pub(crate) tracing: bool,
232 pub(crate) retry_policy: Option<Arc<dyn RetryPolicy>>,
233 pub(crate) backoff_policy: Option<Arc<dyn BackoffPolicy>>,
234 pub(crate) retry_throttler: RetryThrottlerWrapped,
235 pub(crate) polling_policy: Option<Arc<dyn PollingPolicy>>,
236 pub(crate) polling_backoff_policy: Option<Arc<dyn PollingBackoffPolicy>>,
237}
238
239const LOGGING_VAR: &str = "GOOGLE_CLOUD_RUST_LOGGING";
240
241impl ClientConfig {
242 pub fn new() -> Self {
244 Self::default()
245 }
246
247 pub fn tracing_enabled(&self) -> bool {
248 if self.tracing {
249 return true;
250 }
251 std::env::var(LOGGING_VAR)
252 .map(|v| v == "true")
253 .unwrap_or(false)
254 }
255
256 pub fn set_endpoint<T: Into<String>>(mut self, v: T) -> Self {
258 self.endpoint = Some(v.into());
259 self
260 }
261
262 pub fn enable_tracing(mut self) -> Self {
264 self.tracing = true;
265 self
266 }
267
268 pub fn disable_tracing(mut self) -> Self {
270 self.tracing = false;
271 self
272 }
273
274 pub fn set_credential<T: Into<Option<Credential>>>(mut self, v: T) -> Self {
276 self.cred = v.into();
277 self
278 }
279
280 pub fn set_retry_policy<V: Into<RetryPolicyArg>>(mut self, v: V) -> Self {
282 self.retry_policy = Some(v.into().0);
283 self
284 }
285
286 pub fn set_backoff_policy<V: Into<BackoffPolicyArg>>(mut self, v: V) -> Self {
288 self.backoff_policy = Some(v.into().0);
289 self
290 }
291
292 pub fn set_retry_throttler<V: Into<RetryThrottlerArg>>(mut self, v: V) -> Self {
294 self.retry_throttler = v.into().0;
295 self
296 }
297
298 pub fn set_polling_policy<V: Into<PollingPolicyArg>>(mut self, v: V) -> Self {
300 self.polling_policy = Some(v.into().0);
301 self
302 }
303
304 pub fn set_polling_backoff_policy<V: Into<PollingBackoffPolicyArg>>(mut self, v: V) -> Self {
306 self.polling_backoff_policy = Some(v.into().0);
307 self
308 }
309}
310
311impl std::default::Default for ClientConfig {
312 fn default() -> Self {
313 use crate::retry_throttler::AdaptiveThrottler;
314 use std::sync::{Arc, Mutex};
315 Self {
316 endpoint: None,
317 cred: None,
318 tracing: false,
319 retry_policy: None,
320 backoff_policy: None,
321 retry_throttler: Arc::new(Mutex::new(AdaptiveThrottler::default())),
322 polling_policy: None,
323 polling_backoff_policy: None,
324 }
325 }
326}
327
328#[cfg(test)]
329mod test {
330 use super::*;
331 use crate::exponential_backoff::ExponentialBackoffBuilder;
332 use crate::polling_policy;
333 use crate::retry_policy::LimitedAttemptCount;
334 use crate::retry_throttler::AdaptiveThrottler;
335 use std::time::Duration;
336 type Result = std::result::Result<(), Box<dyn std::error::Error>>;
337
338 #[derive(Debug, Default)]
339 struct TestBuilder {
340 request_options: RequestOptions,
341 }
342 impl RequestBuilder for TestBuilder {
343 fn request_options(&mut self) -> &mut RequestOptions {
344 &mut self.request_options
345 }
346 }
347
348 #[test]
349 fn request_options() {
350 let mut opts = RequestOptions::default();
351
352 assert_eq!(opts.idempotent, None);
353 opts.set_idempotency(true);
354 assert_eq!(opts.idempotent, Some(true));
355 opts.set_idempotency(false);
356 assert_eq!(opts.idempotent, Some(false));
357
358 opts.set_user_agent("test-only");
359 assert_eq!(opts.user_agent().as_deref(), Some("test-only"));
360 assert_eq!(opts.attempt_timeout(), &None);
361
362 let d = Duration::from_secs(123);
363 opts.set_attempt_timeout(d);
364 assert_eq!(opts.user_agent().as_deref(), Some("test-only"));
365 assert_eq!(opts.attempt_timeout(), &Some(d));
366
367 opts.set_retry_policy(LimitedAttemptCount::new(3));
368 assert!(opts.retry_policy.is_some(), "{opts:?}");
369
370 opts.set_backoff_policy(ExponentialBackoffBuilder::new().clamp());
371 assert!(opts.backoff_policy.is_some(), "{opts:?}");
372
373 opts.set_retry_throttler(AdaptiveThrottler::default());
374 assert!(opts.retry_throttler.is_some(), "{opts:?}");
375
376 opts.set_polling_policy(polling_policy::Aip194Strict);
377 assert!(opts.polling_policy.is_some(), "{opts:?}");
378
379 opts.set_polling_backoff_policy(ExponentialBackoffBuilder::new().clamp());
380 assert!(opts.polling_backoff_policy.is_some(), "{opts:?}");
381 }
382
383 #[test]
384 fn request_options_idempotency() {
385 let opts = RequestOptions::default().set_default_idempotency(true);
386 assert_eq!(opts.idempotent, Some(true));
387 let opts = opts.set_default_idempotency(false);
388 assert_eq!(opts.idempotent, Some(true));
389
390 let opts = RequestOptions::default().set_default_idempotency(false);
391 assert_eq!(opts.idempotent, Some(false));
392 let opts = opts.set_default_idempotency(true);
393 assert_eq!(opts.idempotent, Some(false));
394 }
395
396 #[test]
397 fn request_options_builder() -> Result {
398 let mut builder = TestBuilder::default();
399 assert_eq!(builder.request_options().user_agent(), &None);
400 assert_eq!(builder.request_options().attempt_timeout(), &None);
401
402 let mut builder = TestBuilder::default().with_idempotency(true);
403 assert_eq!(builder.request_options().idempotent, Some(true));
404 let mut builder = TestBuilder::default().with_idempotency(false);
405 assert_eq!(builder.request_options().idempotent, Some(false));
406
407 let mut builder = TestBuilder::default().with_user_agent("test-only");
408 assert_eq!(
409 builder.request_options().user_agent().as_deref(),
410 Some("test-only")
411 );
412 assert_eq!(builder.request_options().attempt_timeout(), &None);
413
414 let d = Duration::from_secs(123);
415 let mut builder = TestBuilder::default().with_attempt_timeout(d);
416 assert_eq!(builder.request_options().user_agent(), &None);
417 assert_eq!(builder.request_options().attempt_timeout(), &Some(d));
418
419 let mut builder = TestBuilder::default().with_retry_policy(LimitedAttemptCount::new(3));
420 assert!(
421 builder.request_options().retry_policy.is_some(),
422 "{builder:?}"
423 );
424
425 let mut builder =
426 TestBuilder::default().with_backoff_policy(ExponentialBackoffBuilder::new().build()?);
427 assert!(
428 builder.request_options().backoff_policy.is_some(),
429 "{builder:?}"
430 );
431
432 let mut builder = TestBuilder::default().with_retry_throttler(AdaptiveThrottler::default());
433 assert!(
434 builder.request_options().retry_throttler.is_some(),
435 "{builder:?}"
436 );
437
438 let mut builder = TestBuilder::default().with_polling_policy(polling_policy::Aip194Strict);
439 assert!(
440 builder.request_options().polling_policy.is_some(),
441 "{builder:?}"
442 );
443
444 let mut builder = TestBuilder::default()
445 .with_polling_backoff_policy(ExponentialBackoffBuilder::new().build()?);
446 assert!(
447 builder.request_options().polling_backoff_policy.is_some(),
448 "{builder:?}"
449 );
450
451 Ok(())
452 }
453
454 #[test]
457 #[serial_test::serial]
458 fn config_tracing() {
459 unsafe {
460 std::env::remove_var(LOGGING_VAR);
461 }
462 let config = ClientConfig::new();
463 assert!(!config.tracing_enabled(), "expected tracing to be disabled");
464 let config = ClientConfig::new().enable_tracing();
465 assert!(config.tracing_enabled(), "expected tracing to be enabled");
466 let config = config.disable_tracing();
467 assert!(
468 !config.tracing_enabled(),
469 "expected tracing to be disaabled"
470 );
471
472 unsafe {
473 std::env::set_var(LOGGING_VAR, "true");
474 }
475 let config = ClientConfig::new();
476 assert!(config.tracing_enabled(), "expected tracing to be enabled");
477
478 unsafe {
479 std::env::set_var(LOGGING_VAR, "not-true");
480 }
481 let config = ClientConfig::new();
482 assert!(!config.tracing_enabled(), "expected tracing to be disabled");
483 }
484
485 #[test]
486 fn config_endpoint() {
487 let config = ClientConfig::new().set_endpoint("http://storage.googleapis.com");
488 assert_eq!(
489 config.endpoint,
490 Some("http://storage.googleapis.com".to_string())
491 );
492 }
493
494 #[tokio::test]
495 async fn config_credentials() -> Result {
496 let config =
497 ClientConfig::new().set_credential(auth::credentials::testing::test_credentials());
498 let cred = config.cred.unwrap();
499 let token = cred.get_token().await?;
500 assert_eq!(token.token, "test-only-token");
501 Ok(())
502 }
503
504 #[test]
505 fn config_retry_policy() {
506 let config = ClientConfig::new().set_retry_policy(LimitedAttemptCount::new(5));
507 assert!(config.retry_policy.is_some());
508 }
509
510 #[test]
511 fn config_backoff() {
512 let config =
513 ClientConfig::new().set_backoff_policy(ExponentialBackoffBuilder::new().clamp());
514 assert!(config.backoff_policy.is_some());
515 }
516
517 fn map_lock_err<T>(e: std::sync::PoisonError<T>) -> Box<dyn std::error::Error> {
518 format!("cannot acquire lock {e}").into()
519 }
520
521 #[test]
522 fn config_retry_throttler() -> Result {
523 use crate::retry_throttler::CircuitBreaker;
524 let config = ClientConfig::new();
525 let throttler = config.retry_throttler.lock().map_err(map_lock_err)?;
526 assert!(!throttler.throttle_retry_attempt());
527
528 let config = ClientConfig::new().set_retry_throttler(CircuitBreaker::default());
529 let throttler = config.retry_throttler.lock().map_err(map_lock_err)?;
530 assert!(!throttler.throttle_retry_attempt());
531
532 Ok(())
533 }
534
535 #[test]
536 fn config_polling() {
537 let config = ClientConfig::new().set_polling_policy(polling_policy::AlwaysContinue);
538 assert!(config.polling_policy.is_some());
539 }
540
541 #[test]
542 fn config_polling_backoff() {
543 let config = ClientConfig::new()
544 .set_polling_backoff_policy(ExponentialBackoffBuilder::new().clamp());
545 assert!(config.polling_backoff_policy.is_some());
546 }
547}