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 extensions: http::Extensions,
54}
55
56impl RequestOptions {
57 pub fn idempotent(&self) -> Option<bool> {
59 self.idempotent
60 }
61
62 pub fn set_idempotency(&mut self, value: bool) {
74 self.idempotent = Some(value);
75 }
76
77 pub(crate) fn set_default_idempotency(&mut self, default: bool) {
83 self.idempotent.get_or_insert(default);
84 }
85
86 pub fn set_user_agent<T: Into<String>>(&mut self, v: T) {
88 self.user_agent = Some(v.into());
89 }
90
91 pub fn user_agent(&self) -> &Option<String> {
93 &self.user_agent
94 }
95
96 pub fn set_quota_project<T: Into<String>>(&mut self, v: T) {
103 self.quota_project = Some(v.into());
104 }
105
106 pub fn quota_project(&self) -> &Option<String> {
108 &self.quota_project
109 }
110
111 pub fn set_attempt_timeout<T: Into<std::time::Duration>>(&mut self, v: T) {
116 self.attempt_timeout = Some(v.into());
117 }
118
119 pub fn attempt_timeout(&self) -> &Option<std::time::Duration> {
121 &self.attempt_timeout
122 }
123
124 pub fn retry_policy(&self) -> &Option<Arc<dyn RetryPolicy>> {
126 &self.retry_policy
127 }
128
129 pub fn set_retry_policy<V: Into<RetryPolicyArg>>(&mut self, v: V) {
131 self.retry_policy = Some(v.into().into());
132 }
133
134 pub fn backoff_policy(&self) -> &Option<Arc<dyn BackoffPolicy>> {
136 &self.backoff_policy
137 }
138
139 pub fn set_backoff_policy<V: Into<BackoffPolicyArg>>(&mut self, v: V) {
141 self.backoff_policy = Some(v.into().into());
142 }
143
144 pub fn retry_throttler(&self) -> &Option<SharedRetryThrottler> {
146 &self.retry_throttler
147 }
148
149 pub fn set_retry_throttler<V: Into<RetryThrottlerArg>>(&mut self, v: V) {
151 self.retry_throttler = Some(v.into().into());
152 }
153
154 pub fn polling_error_policy(&self) -> &Option<Arc<dyn PollingErrorPolicy>> {
156 &self.polling_error_policy
157 }
158
159 pub fn set_polling_error_policy<V: Into<PollingErrorPolicyArg>>(&mut self, v: V) {
161 self.polling_error_policy = Some(v.into().0);
162 }
163
164 pub fn polling_backoff_policy(&self) -> &Option<Arc<dyn PollingBackoffPolicy>> {
166 &self.polling_backoff_policy
167 }
168
169 pub fn set_polling_backoff_policy<V: Into<PollingBackoffPolicyArg>>(&mut self, v: V) {
171 self.polling_backoff_policy = Some(v.into().0);
172 }
173}
174
175pub trait RequestOptionsBuilder: internal::RequestBuilder {
182 fn with_idempotency(self, v: bool) -> Self;
184
185 fn with_user_agent<V: Into<String>>(self, v: V) -> Self;
187
188 fn with_attempt_timeout<V: Into<std::time::Duration>>(self, v: V) -> Self;
193
194 fn with_retry_policy<V: Into<RetryPolicyArg>>(self, v: V) -> Self;
196
197 fn with_backoff_policy<V: Into<BackoffPolicyArg>>(self, v: V) -> Self;
199
200 fn with_retry_throttler<V: Into<RetryThrottlerArg>>(self, v: V) -> Self;
202
203 fn with_polling_error_policy<V: Into<PollingErrorPolicyArg>>(self, v: V) -> Self;
205
206 fn with_polling_backoff_policy<V: Into<PollingBackoffPolicyArg>>(self, v: V) -> Self;
208
209 fn with_quota_project<V: Into<String>>(self, _v: V) -> Self
219 where
220 Self: Sized,
221 {
222 unimplemented!();
223 }
224}
225
226#[cfg_attr(not(feature = "_internal-semver"), doc(hidden))]
227#[allow(missing_docs)]
228pub mod internal {
229 use super::RequestOptions;
234
235 pub trait RequestBuilder {
241 fn request_options(&mut self) -> &mut RequestOptions;
242 }
243
244 pub fn set_default_idempotency(mut options: RequestOptions, default: bool) -> RequestOptions {
245 options.set_default_idempotency(default);
246 options
247 }
248
249 mod sealed {
250 pub trait OptionsExt {}
251 }
252
253 pub trait RequestOptionsExt: sealed::OptionsExt {
263 fn get_extension<T>(&self) -> Option<&T>
265 where
266 T: Send + Sync + 'static;
267
268 fn insert_extension<T>(self, value: T) -> Self
270 where
271 T: Clone + Send + Sync + 'static;
272 }
273
274 impl sealed::OptionsExt for RequestOptions {}
275 impl RequestOptionsExt for RequestOptions {
276 fn get_extension<T>(&self) -> Option<&T>
277 where
278 T: Send + Sync + 'static,
279 {
280 self.extensions.get::<T>()
281 }
282
283 fn insert_extension<T>(mut self, value: T) -> Self
284 where
285 T: Clone + Send + Sync + 'static,
286 {
287 let _ = self.extensions.insert(value);
288 self
289 }
290 }
291
292 #[derive(Debug, Clone, Default, PartialEq)]
293 pub struct PathTemplate(pub &'static str);
294
295 #[derive(Debug, Clone, Default, PartialEq)]
296 pub struct ResourceName(pub String);
297
298 #[deprecated]
301 pub fn set_path_template(
302 options: RequestOptions,
303 path_template: &'static str,
304 ) -> RequestOptions {
305 options.insert_extension(PathTemplate(path_template))
306 }
307
308 #[deprecated]
311 pub fn get_path_template(options: &RequestOptions) -> Option<&'static str> {
312 options.get_extension::<PathTemplate>().map(|e| e.0)
313 }
314}
315
316impl<T> RequestOptionsBuilder for T
318where
319 T: internal::RequestBuilder,
320{
321 fn with_idempotency(mut self, v: bool) -> Self {
322 self.request_options().set_idempotency(v);
323 self
324 }
325
326 fn with_user_agent<V: Into<String>>(mut self, v: V) -> Self {
327 self.request_options().set_user_agent(v);
328 self
329 }
330
331 fn with_quota_project<V: Into<String>>(mut self, v: V) -> Self {
332 self.request_options().set_quota_project(v);
333 self
334 }
335
336 fn with_attempt_timeout<V: Into<std::time::Duration>>(mut self, v: V) -> Self {
337 self.request_options().set_attempt_timeout(v);
338 self
339 }
340
341 fn with_retry_policy<V: Into<RetryPolicyArg>>(mut self, v: V) -> Self {
342 self.request_options().set_retry_policy(v);
343 self
344 }
345
346 fn with_backoff_policy<V: Into<BackoffPolicyArg>>(mut self, v: V) -> Self {
347 self.request_options().set_backoff_policy(v);
348 self
349 }
350
351 fn with_retry_throttler<V: Into<RetryThrottlerArg>>(mut self, v: V) -> Self {
352 self.request_options().set_retry_throttler(v);
353 self
354 }
355
356 fn with_polling_error_policy<V: Into<PollingErrorPolicyArg>>(mut self, v: V) -> Self {
357 self.request_options().set_polling_error_policy(v);
358 self
359 }
360
361 fn with_polling_backoff_policy<V: Into<PollingBackoffPolicyArg>>(mut self, v: V) -> Self {
362 self.request_options().set_polling_backoff_policy(v);
363 self
364 }
365}
366
367#[cfg(test)]
368mod tests {
369 use super::internal::*;
370 use super::*;
371 use crate::exponential_backoff::ExponentialBackoffBuilder;
372 use crate::polling_error_policy;
373 use crate::retry_policy::LimitedAttemptCount;
374 use crate::retry_throttler::AdaptiveThrottler;
375 use static_assertions::{assert_impl_all, assert_not_impl_all};
376 use std::panic::{RefUnwindSafe, UnwindSafe};
377 use std::time::Duration;
378
379 #[derive(Debug, Default)]
380 struct TestBuilder {
381 request_options: RequestOptions,
382 }
383 impl RequestBuilder for TestBuilder {
384 fn request_options(&mut self) -> &mut RequestOptions {
385 &mut self.request_options
386 }
387 }
388
389 #[test]
390 fn traits() {
391 assert_impl_all!(RequestOptions: Clone, Send, Sync, Unpin, std::fmt::Debug);
392 assert_not_impl_all!(RequestOptions: RefUnwindSafe, UnwindSafe);
393 }
394
395 #[test]
396 fn request_options() {
397 const USER_AGENT: &str = "test-only";
398 const USER_PROJECT: &str = "test-project";
399
400 let mut opts = RequestOptions::default();
401
402 assert_eq!(opts.idempotent, None);
403 opts.set_idempotency(true);
404 assert_eq!(opts.idempotent(), Some(true));
405 opts.set_idempotency(false);
406 assert_eq!(opts.idempotent(), Some(false));
407
408 opts.set_user_agent(USER_AGENT);
409 assert_eq!(opts.user_agent().as_deref(), Some(USER_AGENT));
410 assert_eq!(opts.attempt_timeout(), &None);
411
412 opts.set_quota_project(USER_PROJECT);
413 assert_eq!(opts.quota_project().as_deref(), Some(USER_PROJECT));
414
415 let d = Duration::from_secs(123);
416 opts.set_attempt_timeout(d);
417 assert_eq!(opts.user_agent().as_deref(), Some(USER_AGENT));
418 assert_eq!(opts.attempt_timeout(), &Some(d));
419
420 opts.set_retry_policy(LimitedAttemptCount::new(3));
421 assert!(opts.retry_policy().is_some(), "{opts:?}");
422
423 opts.set_backoff_policy(ExponentialBackoffBuilder::new().clamp());
424 assert!(opts.backoff_policy().is_some(), "{opts:?}");
425
426 opts.set_retry_throttler(AdaptiveThrottler::default());
427 assert!(opts.retry_throttler().is_some(), "{opts:?}");
428
429 opts.set_polling_error_policy(polling_error_policy::Aip194Strict);
430 assert!(opts.polling_error_policy().is_some(), "{opts:?}");
431
432 opts.set_polling_backoff_policy(ExponentialBackoffBuilder::new().clamp());
433 assert!(opts.polling_backoff_policy().is_some(), "{opts:?}");
434 }
435
436 #[test]
437 fn request_options_idempotency() {
438 let opts = set_default_idempotency(RequestOptions::default(), true);
439 assert_eq!(opts.idempotent(), Some(true));
440 let opts = set_default_idempotency(opts, false);
441 assert_eq!(opts.idempotent(), Some(true));
442
443 let opts = set_default_idempotency(RequestOptions::default(), false);
444 assert_eq!(opts.idempotent(), Some(false));
445 let opts = set_default_idempotency(opts, true);
446 assert_eq!(opts.idempotent(), Some(false));
447 }
448
449 #[test]
450 fn request_options_ext() {
451 #[derive(Debug, Clone, PartialEq)]
452 struct TestA(&'static str);
453 #[derive(Debug, Clone, PartialEq)]
454 struct TestB(u32);
455
456 let opts = RequestOptions::default();
457 assert!(opts.get_extension::<TestA>().is_none(), "{opts:?}");
458 assert!(opts.get_extension::<TestB>().is_none(), "{opts:?}");
459 let opts = opts.insert_extension(TestA("1"));
460 assert_eq!(opts.get_extension::<TestA>(), Some(&TestA("1")), "{opts:?}");
461 assert!(opts.get_extension::<TestB>().is_none(), "{opts:?}");
462 let opts = opts
463 .insert_extension(TestA("2"))
464 .insert_extension(TestB(42));
465 assert_eq!(opts.get_extension::<TestA>(), Some(&TestA("2")), "{opts:?}");
466 assert_eq!(opts.get_extension::<TestB>(), Some(&TestB(42)), "{opts:?}");
467 }
468
469 #[test]
470 fn request_options_builder() -> anyhow::Result<()> {
471 const USER_AGENT: &str = "test-only";
472 const USER_PROJECT: &str = "test-project";
473
474 let mut builder = TestBuilder::default();
475 assert_eq!(builder.request_options().user_agent(), &None);
476 assert_eq!(builder.request_options().quota_project(), &None);
477 assert_eq!(builder.request_options().attempt_timeout(), &None);
478
479 let mut builder = TestBuilder::default().with_idempotency(true);
480 assert_eq!(builder.request_options().idempotent(), Some(true));
481 let mut builder = TestBuilder::default().with_idempotency(false);
482 assert_eq!(builder.request_options().idempotent(), Some(false));
483
484 let mut builder = TestBuilder::default().with_user_agent(USER_AGENT);
485 assert_eq!(
486 builder.request_options().user_agent().as_deref(),
487 Some(USER_AGENT)
488 );
489 assert_eq!(builder.request_options().attempt_timeout(), &None);
490
491 let mut builder = TestBuilder::default().with_quota_project(USER_PROJECT);
492 assert_eq!(
493 builder.request_options().quota_project().as_deref(),
494 Some(USER_PROJECT)
495 );
496
497 let d = Duration::from_secs(123);
498 let mut builder = TestBuilder::default().with_attempt_timeout(d);
499 assert_eq!(builder.request_options().user_agent(), &None);
500 assert_eq!(builder.request_options().attempt_timeout(), &Some(d));
501
502 let mut builder = TestBuilder::default().with_retry_policy(LimitedAttemptCount::new(3));
503 assert!(
504 builder.request_options().retry_policy().is_some(),
505 "{builder:?}"
506 );
507
508 let mut builder =
509 TestBuilder::default().with_backoff_policy(ExponentialBackoffBuilder::new().build()?);
510 assert!(
511 builder.request_options().backoff_policy().is_some(),
512 "{builder:?}"
513 );
514
515 let mut builder = TestBuilder::default().with_retry_throttler(AdaptiveThrottler::default());
516 assert!(
517 builder.request_options().retry_throttler().is_some(),
518 "{builder:?}"
519 );
520
521 let mut builder =
522 TestBuilder::default().with_polling_error_policy(polling_error_policy::Aip194Strict);
523 assert!(
524 builder.request_options().polling_error_policy().is_some(),
525 "{builder:?}"
526 );
527
528 let mut builder = TestBuilder::default()
529 .with_polling_backoff_policy(ExponentialBackoffBuilder::new().build()?);
530 assert!(
531 builder.request_options().polling_backoff_policy().is_some(),
532 "{builder:?}"
533 );
534
535 Ok(())
536 }
537}