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 attempt_timeout: Option<std::time::Duration>,
47 retry_policy: Option<Arc<dyn RetryPolicy>>,
48 backoff_policy: Option<Arc<dyn BackoffPolicy>>,
49 retry_throttler: Option<SharedRetryThrottler>,
50 polling_error_policy: Option<Arc<dyn PollingErrorPolicy>>,
51 polling_backoff_policy: Option<Arc<dyn PollingBackoffPolicy>>,
52 extensions: http::Extensions,
53}
54
55impl RequestOptions {
56 pub fn idempotent(&self) -> Option<bool> {
58 self.idempotent
59 }
60
61 pub fn set_idempotency(&mut self, value: bool) {
73 self.idempotent = Some(value);
74 }
75
76 pub(crate) fn set_default_idempotency(&mut self, default: bool) {
82 self.idempotent.get_or_insert(default);
83 }
84
85 pub fn set_user_agent<T: Into<String>>(&mut self, v: T) {
87 self.user_agent = Some(v.into());
88 }
89
90 pub fn user_agent(&self) -> &Option<String> {
92 &self.user_agent
93 }
94
95 pub fn set_attempt_timeout<T: Into<std::time::Duration>>(&mut self, v: T) {
100 self.attempt_timeout = Some(v.into());
101 }
102
103 pub fn attempt_timeout(&self) -> &Option<std::time::Duration> {
105 &self.attempt_timeout
106 }
107
108 pub fn retry_policy(&self) -> &Option<Arc<dyn RetryPolicy>> {
110 &self.retry_policy
111 }
112
113 pub fn set_retry_policy<V: Into<RetryPolicyArg>>(&mut self, v: V) {
115 self.retry_policy = Some(v.into().into());
116 }
117
118 pub fn backoff_policy(&self) -> &Option<Arc<dyn BackoffPolicy>> {
120 &self.backoff_policy
121 }
122
123 pub fn set_backoff_policy<V: Into<BackoffPolicyArg>>(&mut self, v: V) {
125 self.backoff_policy = Some(v.into().into());
126 }
127
128 pub fn retry_throttler(&self) -> &Option<SharedRetryThrottler> {
130 &self.retry_throttler
131 }
132
133 pub fn set_retry_throttler<V: Into<RetryThrottlerArg>>(&mut self, v: V) {
135 self.retry_throttler = Some(v.into().into());
136 }
137
138 pub fn polling_error_policy(&self) -> &Option<Arc<dyn PollingErrorPolicy>> {
140 &self.polling_error_policy
141 }
142
143 pub fn set_polling_error_policy<V: Into<PollingErrorPolicyArg>>(&mut self, v: V) {
145 self.polling_error_policy = Some(v.into().0);
146 }
147
148 pub fn polling_backoff_policy(&self) -> &Option<Arc<dyn PollingBackoffPolicy>> {
150 &self.polling_backoff_policy
151 }
152
153 pub fn set_polling_backoff_policy<V: Into<PollingBackoffPolicyArg>>(&mut self, v: V) {
155 self.polling_backoff_policy = Some(v.into().0);
156 }
157}
158
159pub trait RequestOptionsBuilder: internal::RequestBuilder {
166 fn with_idempotency(self, v: bool) -> Self;
168
169 fn with_user_agent<V: Into<String>>(self, v: V) -> Self;
171
172 fn with_attempt_timeout<V: Into<std::time::Duration>>(self, v: V) -> Self;
177
178 fn with_retry_policy<V: Into<RetryPolicyArg>>(self, v: V) -> Self;
180
181 fn with_backoff_policy<V: Into<BackoffPolicyArg>>(self, v: V) -> Self;
183
184 fn with_retry_throttler<V: Into<RetryThrottlerArg>>(self, v: V) -> Self;
186
187 fn with_polling_error_policy<V: Into<PollingErrorPolicyArg>>(self, v: V) -> Self;
189
190 fn with_polling_backoff_policy<V: Into<PollingBackoffPolicyArg>>(self, v: V) -> Self;
192}
193
194#[cfg_attr(not(feature = "_internal-semver"), doc(hidden))]
195pub mod internal {
196 use super::RequestOptions;
201
202 pub trait RequestBuilder {
208 fn request_options(&mut self) -> &mut RequestOptions;
209 }
210
211 pub fn set_default_idempotency(mut options: RequestOptions, default: bool) -> RequestOptions {
212 options.set_default_idempotency(default);
213 options
214 }
215
216 mod sealed {
217 pub trait OptionsExt {}
218 }
219
220 pub trait RequestOptionsExt: sealed::OptionsExt {
230 fn get_extension<T>(&self) -> Option<&T>
232 where
233 T: Send + Sync + 'static;
234
235 fn insert_extension<T>(self, value: T) -> Self
237 where
238 T: Clone + Send + Sync + 'static;
239 }
240
241 impl sealed::OptionsExt for RequestOptions {}
242 impl RequestOptionsExt for RequestOptions {
243 fn get_extension<T>(&self) -> Option<&T>
244 where
245 T: Send + Sync + 'static,
246 {
247 self.extensions.get::<T>()
248 }
249
250 fn insert_extension<T>(mut self, value: T) -> Self
251 where
252 T: Clone + Send + Sync + 'static,
253 {
254 let _ = self.extensions.insert(value);
255 self
256 }
257 }
258
259 #[derive(Debug, Clone, Default, PartialEq)]
260 pub struct PathTemplate(pub &'static str);
261
262 #[derive(Debug, Clone, Default, PartialEq)]
263 pub struct ResourceName(pub String);
264
265 #[deprecated]
268 pub fn set_path_template(
269 options: RequestOptions,
270 path_template: &'static str,
271 ) -> RequestOptions {
272 options.insert_extension(PathTemplate(path_template))
273 }
274
275 #[deprecated]
278 pub fn get_path_template(options: &RequestOptions) -> Option<&'static str> {
279 options.get_extension::<PathTemplate>().map(|e| e.0)
280 }
281}
282
283impl<T> RequestOptionsBuilder for T
285where
286 T: internal::RequestBuilder,
287{
288 fn with_idempotency(mut self, v: bool) -> Self {
289 self.request_options().set_idempotency(v);
290 self
291 }
292
293 fn with_user_agent<V: Into<String>>(mut self, v: V) -> Self {
294 self.request_options().set_user_agent(v);
295 self
296 }
297
298 fn with_attempt_timeout<V: Into<std::time::Duration>>(mut self, v: V) -> Self {
299 self.request_options().set_attempt_timeout(v);
300 self
301 }
302
303 fn with_retry_policy<V: Into<RetryPolicyArg>>(mut self, v: V) -> Self {
304 self.request_options().set_retry_policy(v);
305 self
306 }
307
308 fn with_backoff_policy<V: Into<BackoffPolicyArg>>(mut self, v: V) -> Self {
309 self.request_options().set_backoff_policy(v);
310 self
311 }
312
313 fn with_retry_throttler<V: Into<RetryThrottlerArg>>(mut self, v: V) -> Self {
314 self.request_options().set_retry_throttler(v);
315 self
316 }
317
318 fn with_polling_error_policy<V: Into<PollingErrorPolicyArg>>(mut self, v: V) -> Self {
319 self.request_options().set_polling_error_policy(v);
320 self
321 }
322
323 fn with_polling_backoff_policy<V: Into<PollingBackoffPolicyArg>>(mut self, v: V) -> Self {
324 self.request_options().set_polling_backoff_policy(v);
325 self
326 }
327}
328
329#[cfg(test)]
330mod tests {
331 use super::internal::*;
332 use super::*;
333 use crate::exponential_backoff::ExponentialBackoffBuilder;
334 use crate::polling_error_policy;
335 use crate::retry_policy::LimitedAttemptCount;
336 use crate::retry_throttler::AdaptiveThrottler;
337 use static_assertions::{assert_impl_all, assert_not_impl_all};
338 use std::panic::{RefUnwindSafe, UnwindSafe};
339 use std::time::Duration;
340
341 #[derive(Debug, Default)]
342 struct TestBuilder {
343 request_options: RequestOptions,
344 }
345 impl RequestBuilder for TestBuilder {
346 fn request_options(&mut self) -> &mut RequestOptions {
347 &mut self.request_options
348 }
349 }
350
351 #[test]
352 fn traits() {
353 assert_impl_all!(RequestOptions: Clone, Send, Sync, Unpin, std::fmt::Debug);
354 assert_not_impl_all!(RequestOptions: RefUnwindSafe, UnwindSafe);
355 }
356
357 #[test]
358 fn request_options() {
359 let mut opts = RequestOptions::default();
360
361 assert_eq!(opts.idempotent, None);
362 opts.set_idempotency(true);
363 assert_eq!(opts.idempotent(), Some(true));
364 opts.set_idempotency(false);
365 assert_eq!(opts.idempotent(), Some(false));
366
367 opts.set_user_agent("test-only");
368 assert_eq!(opts.user_agent().as_deref(), Some("test-only"));
369 assert_eq!(opts.attempt_timeout(), &None);
370
371 let d = Duration::from_secs(123);
372 opts.set_attempt_timeout(d);
373 assert_eq!(opts.user_agent().as_deref(), Some("test-only"));
374 assert_eq!(opts.attempt_timeout(), &Some(d));
375
376 opts.set_retry_policy(LimitedAttemptCount::new(3));
377 assert!(opts.retry_policy().is_some(), "{opts:?}");
378
379 opts.set_backoff_policy(ExponentialBackoffBuilder::new().clamp());
380 assert!(opts.backoff_policy().is_some(), "{opts:?}");
381
382 opts.set_retry_throttler(AdaptiveThrottler::default());
383 assert!(opts.retry_throttler().is_some(), "{opts:?}");
384
385 opts.set_polling_error_policy(polling_error_policy::Aip194Strict);
386 assert!(opts.polling_error_policy().is_some(), "{opts:?}");
387
388 opts.set_polling_backoff_policy(ExponentialBackoffBuilder::new().clamp());
389 assert!(opts.polling_backoff_policy().is_some(), "{opts:?}");
390 }
391
392 #[test]
393 fn request_options_idempotency() {
394 let opts = set_default_idempotency(RequestOptions::default(), true);
395 assert_eq!(opts.idempotent(), Some(true));
396 let opts = set_default_idempotency(opts, false);
397 assert_eq!(opts.idempotent(), Some(true));
398
399 let opts = set_default_idempotency(RequestOptions::default(), false);
400 assert_eq!(opts.idempotent(), Some(false));
401 let opts = set_default_idempotency(opts, true);
402 assert_eq!(opts.idempotent(), Some(false));
403 }
404
405 #[test]
406 fn request_options_ext() {
407 #[derive(Debug, Clone, PartialEq)]
408 struct TestA(&'static str);
409 #[derive(Debug, Clone, PartialEq)]
410 struct TestB(u32);
411
412 let opts = RequestOptions::default();
413 assert!(opts.get_extension::<TestA>().is_none(), "{opts:?}");
414 assert!(opts.get_extension::<TestB>().is_none(), "{opts:?}");
415 let opts = opts.insert_extension(TestA("1"));
416 assert_eq!(opts.get_extension::<TestA>(), Some(&TestA("1")), "{opts:?}");
417 assert!(opts.get_extension::<TestB>().is_none(), "{opts:?}");
418 let opts = opts
419 .insert_extension(TestA("2"))
420 .insert_extension(TestB(42));
421 assert_eq!(opts.get_extension::<TestA>(), Some(&TestA("2")), "{opts:?}");
422 assert_eq!(opts.get_extension::<TestB>(), Some(&TestB(42)), "{opts:?}");
423 }
424
425 #[test]
426 fn request_options_builder() -> anyhow::Result<()> {
427 let mut builder = TestBuilder::default();
428 assert_eq!(builder.request_options().user_agent(), &None);
429 assert_eq!(builder.request_options().attempt_timeout(), &None);
430
431 let mut builder = TestBuilder::default().with_idempotency(true);
432 assert_eq!(builder.request_options().idempotent(), Some(true));
433 let mut builder = TestBuilder::default().with_idempotency(false);
434 assert_eq!(builder.request_options().idempotent(), Some(false));
435
436 let mut builder = TestBuilder::default().with_user_agent("test-only");
437 assert_eq!(
438 builder.request_options().user_agent().as_deref(),
439 Some("test-only")
440 );
441 assert_eq!(builder.request_options().attempt_timeout(), &None);
442
443 let d = Duration::from_secs(123);
444 let mut builder = TestBuilder::default().with_attempt_timeout(d);
445 assert_eq!(builder.request_options().user_agent(), &None);
446 assert_eq!(builder.request_options().attempt_timeout(), &Some(d));
447
448 let mut builder = TestBuilder::default().with_retry_policy(LimitedAttemptCount::new(3));
449 assert!(
450 builder.request_options().retry_policy().is_some(),
451 "{builder:?}"
452 );
453
454 let mut builder =
455 TestBuilder::default().with_backoff_policy(ExponentialBackoffBuilder::new().build()?);
456 assert!(
457 builder.request_options().backoff_policy().is_some(),
458 "{builder:?}"
459 );
460
461 let mut builder = TestBuilder::default().with_retry_throttler(AdaptiveThrottler::default());
462 assert!(
463 builder.request_options().retry_throttler().is_some(),
464 "{builder:?}"
465 );
466
467 let mut builder =
468 TestBuilder::default().with_polling_error_policy(polling_error_policy::Aip194Strict);
469 assert!(
470 builder.request_options().polling_error_policy().is_some(),
471 "{builder:?}"
472 );
473
474 let mut builder = TestBuilder::default()
475 .with_polling_backoff_policy(ExponentialBackoffBuilder::new().build()?);
476 assert!(
477 builder.request_options().polling_backoff_policy().is_some(),
478 "{builder:?}"
479 );
480
481 Ok(())
482 }
483}