1use crate::backoff_policy::{BackoffPolicy, BackoffPolicyArg};
29use crate::polling_backoff_policy::{PollingBackoffPolicy, PollingBackoffPolicyArg};
30use crate::polling_error_policy::{PollingErrorPolicy, PollingErrorPolicyArg};
31use crate::retry_policy::{RetryPolicy, RetryPolicyArg};
32use crate::retry_throttler::{RetryThrottlerArg, SharedRetryThrottler};
33use std::sync::Arc;
34
35#[derive(Clone, Debug, Default)]
43pub struct RequestOptions {
44 idempotent: Option<bool>,
45 user_agent: Option<String>,
46 quota_project: Option<String>,
47 attempt_timeout: Option<std::time::Duration>,
48 retry_policy: Option<Arc<dyn RetryPolicy>>,
49 backoff_policy: Option<Arc<dyn BackoffPolicy>>,
50 retry_throttler: Option<SharedRetryThrottler>,
51 polling_error_policy: Option<Arc<dyn PollingErrorPolicy>>,
52 polling_backoff_policy: Option<Arc<dyn PollingBackoffPolicy>>,
53 #[cfg(google_cloud_unstable_gapic_streaming)]
54 request_stream_channel_capacity: Option<usize>,
55 extensions: http::Extensions,
56}
57
58impl RequestOptions {
59 pub fn idempotent(&self) -> Option<bool> {
61 self.idempotent
62 }
63
64 pub fn set_idempotency(&mut self, value: bool) {
76 self.idempotent = Some(value);
77 }
78
79 pub(crate) fn set_default_idempotency(&mut self, default: bool) {
85 self.idempotent.get_or_insert(default);
86 }
87
88 pub fn set_user_agent<T: Into<String>>(&mut self, v: T) {
90 self.user_agent = Some(v.into());
91 }
92
93 pub fn user_agent(&self) -> &Option<String> {
95 &self.user_agent
96 }
97
98 pub fn set_quota_project<T: Into<String>>(&mut self, v: T) {
105 self.quota_project = Some(v.into());
106 }
107
108 pub fn quota_project(&self) -> &Option<String> {
110 &self.quota_project
111 }
112
113 pub fn set_attempt_timeout<T: Into<std::time::Duration>>(&mut self, v: T) {
118 self.attempt_timeout = Some(v.into());
119 }
120
121 pub fn attempt_timeout(&self) -> &Option<std::time::Duration> {
123 &self.attempt_timeout
124 }
125
126 pub fn retry_policy(&self) -> &Option<Arc<dyn RetryPolicy>> {
128 &self.retry_policy
129 }
130
131 pub fn set_retry_policy<V: Into<RetryPolicyArg>>(&mut self, v: V) {
133 self.retry_policy = Some(v.into().into());
134 }
135
136 pub fn backoff_policy(&self) -> &Option<Arc<dyn BackoffPolicy>> {
138 &self.backoff_policy
139 }
140
141 pub fn set_backoff_policy<V: Into<BackoffPolicyArg>>(&mut self, v: V) {
143 self.backoff_policy = Some(v.into().into());
144 }
145
146 pub fn retry_throttler(&self) -> &Option<SharedRetryThrottler> {
148 &self.retry_throttler
149 }
150
151 pub fn set_retry_throttler<V: Into<RetryThrottlerArg>>(&mut self, v: V) {
153 self.retry_throttler = Some(v.into().into());
154 }
155
156 pub fn polling_error_policy(&self) -> &Option<Arc<dyn PollingErrorPolicy>> {
158 &self.polling_error_policy
159 }
160
161 pub fn set_polling_error_policy<V: Into<PollingErrorPolicyArg>>(&mut self, v: V) {
163 self.polling_error_policy = Some(v.into().0);
164 }
165
166 pub fn polling_backoff_policy(&self) -> &Option<Arc<dyn PollingBackoffPolicy>> {
168 &self.polling_backoff_policy
169 }
170
171 pub fn set_polling_backoff_policy<V: Into<PollingBackoffPolicyArg>>(&mut self, v: V) {
173 self.polling_backoff_policy = Some(v.into().0);
174 }
175
176 #[cfg(google_cloud_unstable_gapic_streaming)]
178 pub fn request_stream_channel_capacity(&self) -> Option<usize> {
179 self.request_stream_channel_capacity
180 }
181
182 #[cfg(google_cloud_unstable_gapic_streaming)]
186 pub fn set_request_stream_channel_capacity(&mut self, capacity: usize) {
187 self.request_stream_channel_capacity =
188 Some(capacity.clamp(1, MAX_REQUEST_CHANNEL_CAPACITY));
189 }
190}
191
192#[cfg(google_cloud_unstable_gapic_streaming)]
197const MAX_REQUEST_CHANNEL_CAPACITY: usize = usize::MAX >> 3;
198
199pub trait RequestOptionsBuilder: internal::RequestBuilder {
206 fn with_idempotency(self, v: bool) -> Self;
208
209 fn with_user_agent<V: Into<String>>(self, v: V) -> Self;
211
212 fn with_attempt_timeout<V: Into<std::time::Duration>>(self, v: V) -> Self;
217
218 fn with_retry_policy<V: Into<RetryPolicyArg>>(self, v: V) -> Self;
220
221 fn with_backoff_policy<V: Into<BackoffPolicyArg>>(self, v: V) -> Self;
223
224 fn with_retry_throttler<V: Into<RetryThrottlerArg>>(self, v: V) -> Self;
226
227 fn with_polling_error_policy<V: Into<PollingErrorPolicyArg>>(self, v: V) -> Self;
229
230 fn with_polling_backoff_policy<V: Into<PollingBackoffPolicyArg>>(self, v: V) -> Self;
232
233 fn with_quota_project<V: Into<String>>(self, _v: V) -> Self
243 where
244 Self: Sized,
245 {
246 unimplemented!();
247 }
248
249 fn with_custom_header(
283 mut self,
284 name: http::header::HeaderName,
285 value: http::header::HeaderValue,
286 ) -> Self
287 where
288 Self: Sized,
289 {
290 use internal::RequestOptionsExt;
291 let mut headers = self
292 .request_options()
293 .get_extension::<http::HeaderMap>()
294 .cloned()
295 .unwrap_or_default();
296 headers.insert(name, value);
297 let mut options = std::mem::take(self.request_options());
298 options = options.insert_extension(headers);
299 *self.request_options() = options;
300 self
301 }
302
303 #[cfg(google_cloud_unstable_gapic_streaming)]
308 fn with_request_stream_channel_capacity(self, _capacity: usize) -> Self
309 where
310 Self: Sized,
311 {
312 unimplemented!();
313 }
314}
315
316#[cfg_attr(not(feature = "_internal-semver"), doc(hidden))]
317#[allow(missing_docs)]
318pub mod internal {
319 use super::RequestOptions;
324
325 pub trait RequestBuilder {
331 fn request_options(&mut self) -> &mut RequestOptions;
332 }
333
334 pub fn set_default_idempotency(mut options: RequestOptions, default: bool) -> RequestOptions {
335 options.set_default_idempotency(default);
336 options
337 }
338
339 mod sealed {
340 pub trait OptionsExt {}
341 }
342
343 pub trait RequestOptionsExt: sealed::OptionsExt {
353 fn get_extension<T>(&self) -> Option<&T>
355 where
356 T: Send + Sync + 'static;
357
358 fn get_extension_mut<T>(&mut self) -> Option<&mut T>
360 where
361 T: Send + Sync + 'static;
362
363 fn get_extension_or_default_mut<T>(&mut self) -> &mut T
365 where
366 T: Default + Clone + Send + Sync + 'static;
367
368 fn insert_extension<T>(self, value: T) -> Self
370 where
371 T: Clone + Send + Sync + 'static;
372 }
373
374 impl sealed::OptionsExt for RequestOptions {}
375 impl RequestOptionsExt for RequestOptions {
376 fn get_extension<T>(&self) -> Option<&T>
377 where
378 T: Send + Sync + 'static,
379 {
380 self.extensions.get::<T>()
381 }
382
383 fn get_extension_mut<T>(&mut self) -> Option<&mut T>
384 where
385 T: Send + Sync + 'static,
386 {
387 self.extensions.get_mut::<T>()
388 }
389
390 fn get_extension_or_default_mut<T>(&mut self) -> &mut T
391 where
392 T: Default + Clone + Send + Sync + 'static,
393 {
394 if self.extensions.get::<T>().is_none() {
395 let _ = self.extensions.insert(T::default());
396 }
397 self.extensions
398 .get_mut::<T>()
399 .expect("value was just inserted if missing")
400 }
401
402 fn insert_extension<T>(mut self, value: T) -> Self
403 where
404 T: Clone + Send + Sync + 'static,
405 {
406 let _ = self.extensions.insert(value);
407 self
408 }
409 }
410
411 #[derive(Debug, Clone, Default, PartialEq)]
412 pub struct PathTemplate(pub &'static str);
413
414 #[derive(Debug, Clone, Default, PartialEq)]
415 pub struct ResourceName(pub String);
416
417 #[deprecated]
420 pub fn set_path_template(
421 options: RequestOptions,
422 path_template: &'static str,
423 ) -> RequestOptions {
424 options.insert_extension(PathTemplate(path_template))
425 }
426
427 #[deprecated]
430 pub fn get_path_template(options: &RequestOptions) -> Option<&'static str> {
431 options.get_extension::<PathTemplate>().map(|e| e.0)
432 }
433}
434
435impl<T> RequestOptionsBuilder for T
437where
438 T: internal::RequestBuilder,
439{
440 fn with_idempotency(mut self, v: bool) -> Self {
441 self.request_options().set_idempotency(v);
442 self
443 }
444
445 fn with_user_agent<V: Into<String>>(mut self, v: V) -> Self {
446 self.request_options().set_user_agent(v);
447 self
448 }
449
450 fn with_quota_project<V: Into<String>>(mut self, v: V) -> Self {
451 self.request_options().set_quota_project(v);
452 self
453 }
454
455 fn with_attempt_timeout<V: Into<std::time::Duration>>(mut self, v: V) -> Self {
456 self.request_options().set_attempt_timeout(v);
457 self
458 }
459
460 fn with_retry_policy<V: Into<RetryPolicyArg>>(mut self, v: V) -> Self {
461 self.request_options().set_retry_policy(v);
462 self
463 }
464
465 fn with_backoff_policy<V: Into<BackoffPolicyArg>>(mut self, v: V) -> Self {
466 self.request_options().set_backoff_policy(v);
467 self
468 }
469
470 fn with_retry_throttler<V: Into<RetryThrottlerArg>>(mut self, v: V) -> Self {
471 self.request_options().set_retry_throttler(v);
472 self
473 }
474
475 fn with_polling_error_policy<V: Into<PollingErrorPolicyArg>>(mut self, v: V) -> Self {
476 self.request_options().set_polling_error_policy(v);
477 self
478 }
479
480 fn with_polling_backoff_policy<V: Into<PollingBackoffPolicyArg>>(mut self, v: V) -> Self {
481 self.request_options().set_polling_backoff_policy(v);
482 self
483 }
484
485 #[cfg(google_cloud_unstable_gapic_streaming)]
486 fn with_request_stream_channel_capacity(mut self, capacity: usize) -> Self {
487 self.request_options()
488 .set_request_stream_channel_capacity(capacity);
489 self
490 }
491}
492
493#[cfg(test)]
494mod tests {
495 use super::internal::*;
496 use super::*;
497 use crate::exponential_backoff::ExponentialBackoffBuilder;
498 use crate::polling_error_policy;
499 use crate::retry_policy::LimitedAttemptCount;
500 use crate::retry_throttler::AdaptiveThrottler;
501 use static_assertions::{assert_impl_all, assert_not_impl_all};
502 use std::panic::{RefUnwindSafe, UnwindSafe};
503 use std::time::Duration;
504
505 #[derive(Debug, Default)]
506 struct TestBuilder {
507 request_options: RequestOptions,
508 }
509 impl RequestBuilder for TestBuilder {
510 fn request_options(&mut self) -> &mut RequestOptions {
511 &mut self.request_options
512 }
513 }
514
515 #[test]
516 fn traits() {
517 assert_impl_all!(RequestOptions: Clone, Send, Sync, Unpin, std::fmt::Debug);
518 assert_not_impl_all!(RequestOptions: RefUnwindSafe, UnwindSafe);
519 }
520
521 #[test]
522 fn request_options() {
523 const USER_AGENT: &str = "test-only";
524 const USER_PROJECT: &str = "test-project";
525
526 let mut opts = RequestOptions::default();
527
528 assert_eq!(opts.idempotent, None);
529 opts.set_idempotency(true);
530 assert_eq!(opts.idempotent(), Some(true));
531 opts.set_idempotency(false);
532 assert_eq!(opts.idempotent(), Some(false));
533
534 opts.set_user_agent(USER_AGENT);
535 assert_eq!(opts.user_agent().as_deref(), Some(USER_AGENT));
536 assert_eq!(opts.attempt_timeout(), &None);
537
538 opts.set_quota_project(USER_PROJECT);
539 assert_eq!(opts.quota_project().as_deref(), Some(USER_PROJECT));
540
541 let d = Duration::from_secs(123);
542 opts.set_attempt_timeout(d);
543 assert_eq!(opts.user_agent().as_deref(), Some(USER_AGENT));
544 assert_eq!(opts.attempt_timeout(), &Some(d));
545
546 opts.set_retry_policy(LimitedAttemptCount::new(3));
547 assert!(opts.retry_policy().is_some(), "{opts:?}");
548
549 opts.set_backoff_policy(ExponentialBackoffBuilder::new().clamp());
550 assert!(opts.backoff_policy().is_some(), "{opts:?}");
551
552 opts.set_retry_throttler(AdaptiveThrottler::default());
553 assert!(opts.retry_throttler().is_some(), "{opts:?}");
554
555 opts.set_polling_error_policy(polling_error_policy::Aip194Strict);
556 assert!(opts.polling_error_policy().is_some(), "{opts:?}");
557
558 opts.set_polling_backoff_policy(ExponentialBackoffBuilder::new().clamp());
559 assert!(opts.polling_backoff_policy().is_some(), "{opts:?}");
560 }
561
562 #[test]
563 fn request_options_idempotency() {
564 let opts = set_default_idempotency(RequestOptions::default(), true);
565 assert_eq!(opts.idempotent(), Some(true));
566 let opts = set_default_idempotency(opts, false);
567 assert_eq!(opts.idempotent(), Some(true));
568
569 let opts = set_default_idempotency(RequestOptions::default(), false);
570 assert_eq!(opts.idempotent(), Some(false));
571 let opts = set_default_idempotency(opts, true);
572 assert_eq!(opts.idempotent(), Some(false));
573 }
574
575 #[test]
576 fn request_options_ext() {
577 #[derive(Debug, Clone, PartialEq)]
578 struct TestA(&'static str);
579 #[derive(Debug, Clone, PartialEq)]
580 struct TestB(u32);
581
582 let opts = RequestOptions::default();
583 assert!(opts.get_extension::<TestA>().is_none(), "{opts:?}");
584 assert!(opts.get_extension::<TestB>().is_none(), "{opts:?}");
585 let opts = opts.insert_extension(TestA("1"));
586 assert_eq!(opts.get_extension::<TestA>(), Some(&TestA("1")), "{opts:?}");
587 assert!(opts.get_extension::<TestB>().is_none(), "{opts:?}");
588 let opts = opts
589 .insert_extension(TestA("2"))
590 .insert_extension(TestB(42));
591 assert_eq!(opts.get_extension::<TestA>(), Some(&TestA("2")), "{opts:?}");
592 assert_eq!(opts.get_extension::<TestB>(), Some(&TestB(42)), "{opts:?}");
593 }
594
595 #[test]
596 fn request_options_ext_mut() {
597 #[derive(Debug, Clone, Default, PartialEq)]
598 struct TestCounter(u32);
599
600 let mut opts = RequestOptions::default();
601
602 assert!(opts.get_extension_mut::<TestCounter>().is_none());
604
605 let counter = opts.get_extension_or_default_mut::<TestCounter>();
607 assert_eq!(counter, &mut TestCounter(0));
608 counter.0 += 10;
609
610 let counter = opts
612 .get_extension_mut::<TestCounter>()
613 .expect("counter extension should be present after insertion");
614 assert_eq!(counter, &mut TestCounter(10));
615 counter.0 += 10;
616
617 let counter2 = opts.get_extension_or_default_mut::<TestCounter>();
619 assert_eq!(counter2, &mut TestCounter(20));
620 counter2.0 += 5;
621
622 assert_eq!(opts.get_extension::<TestCounter>(), Some(&TestCounter(25)));
623 }
624
625 #[test]
626 fn request_options_builder() -> anyhow::Result<()> {
627 const USER_AGENT: &str = "test-only";
628 const USER_PROJECT: &str = "test-project";
629
630 let mut builder = TestBuilder::default();
631 assert_eq!(builder.request_options().user_agent(), &None);
632 assert_eq!(builder.request_options().quota_project(), &None);
633 assert_eq!(builder.request_options().attempt_timeout(), &None);
634
635 let mut builder = TestBuilder::default().with_idempotency(true);
636 assert_eq!(builder.request_options().idempotent(), Some(true));
637 let mut builder = TestBuilder::default().with_idempotency(false);
638 assert_eq!(builder.request_options().idempotent(), Some(false));
639
640 let mut builder = TestBuilder::default().with_user_agent(USER_AGENT);
641 assert_eq!(
642 builder.request_options().user_agent().as_deref(),
643 Some(USER_AGENT)
644 );
645 assert_eq!(builder.request_options().attempt_timeout(), &None);
646
647 let mut builder = TestBuilder::default().with_quota_project(USER_PROJECT);
648 assert_eq!(
649 builder.request_options().quota_project().as_deref(),
650 Some(USER_PROJECT)
651 );
652
653 let d = Duration::from_secs(123);
654 let mut builder = TestBuilder::default().with_attempt_timeout(d);
655 assert_eq!(builder.request_options().user_agent(), &None);
656 assert_eq!(builder.request_options().attempt_timeout(), &Some(d));
657
658 let mut builder = TestBuilder::default().with_retry_policy(LimitedAttemptCount::new(3));
659 assert!(
660 builder.request_options().retry_policy().is_some(),
661 "{builder:?}"
662 );
663
664 let mut builder =
665 TestBuilder::default().with_backoff_policy(ExponentialBackoffBuilder::new().build()?);
666 assert!(
667 builder.request_options().backoff_policy().is_some(),
668 "{builder:?}"
669 );
670
671 let mut builder = TestBuilder::default().with_retry_throttler(AdaptiveThrottler::default());
672 assert!(
673 builder.request_options().retry_throttler().is_some(),
674 "{builder:?}"
675 );
676
677 let mut builder =
678 TestBuilder::default().with_polling_error_policy(polling_error_policy::Aip194Strict);
679 assert!(
680 builder.request_options().polling_error_policy().is_some(),
681 "{builder:?}"
682 );
683
684 let mut builder = TestBuilder::default()
685 .with_polling_backoff_policy(ExponentialBackoffBuilder::new().build()?);
686 assert!(
687 builder.request_options().polling_backoff_policy().is_some(),
688 "{builder:?}"
689 );
690
691 Ok(())
692 }
693
694 #[test]
695 fn request_options_builder_custom_headers() {
696 let mut builder = TestBuilder::default()
697 .with_custom_header(
698 http::header::HeaderName::from_static("x-custom-1"),
699 http::header::HeaderValue::from_static("value1"),
700 )
701 .with_custom_header(
702 http::header::HeaderName::from_static("x-custom-2"),
703 http::header::HeaderValue::from_static("value2"),
704 );
705
706 let headers = builder
707 .request_options()
708 .get_extension::<http::HeaderMap>()
709 .expect("headers extension should be present");
710
711 assert_eq!(
712 headers.get("x-custom-1").and_then(|v| v.to_str().ok()),
713 Some("value1")
714 );
715 assert_eq!(
716 headers.get("x-custom-2").and_then(|v| v.to_str().ok()),
717 Some("value2")
718 );
719 }
720
721 #[cfg(google_cloud_unstable_gapic_streaming)]
722 #[test]
723 fn request_options_builder_request_stream_channel_capacity() {
724 let builder = TestBuilder::default();
725 assert_eq!(
726 builder.request_options.request_stream_channel_capacity(),
727 None
728 );
729
730 let builder = TestBuilder::default().with_request_stream_channel_capacity(32);
731 assert_eq!(
732 builder.request_options.request_stream_channel_capacity(),
733 Some(32)
734 );
735
736 let builder = TestBuilder::default().with_request_stream_channel_capacity(0);
738 assert_eq!(
739 builder.request_options.request_stream_channel_capacity(),
740 Some(1)
741 );
742
743 let builder = TestBuilder::default().with_request_stream_channel_capacity(usize::MAX);
744 assert_eq!(
745 builder.request_options.request_stream_channel_capacity(),
746 Some(MAX_REQUEST_CHANNEL_CAPACITY)
747 );
748 }
749}