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