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 path_template: Option<&'static str>,
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 pub(crate) fn path_template(&self) -> Option<&'static str> {
160 self.path_template
161 }
162
163 pub(crate) fn set_path_template(&mut self, v: &'static str) {
165 self.path_template = Some(v);
166 }
167}
168
169pub trait RequestOptionsBuilder: internal::RequestBuilder {
176 fn with_idempotency(self, v: bool) -> Self;
178
179 fn with_user_agent<V: Into<String>>(self, v: V) -> Self;
181
182 fn with_attempt_timeout<V: Into<std::time::Duration>>(self, v: V) -> Self;
187
188 fn with_retry_policy<V: Into<RetryPolicyArg>>(self, v: V) -> Self;
190
191 fn with_backoff_policy<V: Into<BackoffPolicyArg>>(self, v: V) -> Self;
193
194 fn with_retry_throttler<V: Into<RetryThrottlerArg>>(self, v: V) -> Self;
196
197 fn with_polling_error_policy<V: Into<PollingErrorPolicyArg>>(self, v: V) -> Self;
199
200 fn with_polling_backoff_policy<V: Into<PollingBackoffPolicyArg>>(self, v: V) -> Self;
202}
203
204#[cfg_attr(not(feature = "_internal-semver"), doc(hidden))]
205pub mod internal {
206 use super::RequestOptions;
211
212 pub trait RequestBuilder {
218 fn request_options(&mut self) -> &mut RequestOptions;
219 }
220
221 pub fn set_default_idempotency(mut options: RequestOptions, default: bool) -> RequestOptions {
222 options.set_default_idempotency(default);
223 options
224 }
225
226 pub fn set_path_template(
227 mut options: RequestOptions,
228 path_template: &'static str,
229 ) -> RequestOptions {
230 options.set_path_template(path_template);
231 options
232 }
233
234 pub fn get_path_template(options: &RequestOptions) -> Option<&'static str> {
235 options.path_template()
236 }
237}
238
239impl<T> RequestOptionsBuilder for T
241where
242 T: internal::RequestBuilder,
243{
244 fn with_idempotency(mut self, v: bool) -> Self {
245 self.request_options().set_idempotency(v);
246 self
247 }
248
249 fn with_user_agent<V: Into<String>>(mut self, v: V) -> Self {
250 self.request_options().set_user_agent(v);
251 self
252 }
253
254 fn with_attempt_timeout<V: Into<std::time::Duration>>(mut self, v: V) -> Self {
255 self.request_options().set_attempt_timeout(v);
256 self
257 }
258
259 fn with_retry_policy<V: Into<RetryPolicyArg>>(mut self, v: V) -> Self {
260 self.request_options().set_retry_policy(v);
261 self
262 }
263
264 fn with_backoff_policy<V: Into<BackoffPolicyArg>>(mut self, v: V) -> Self {
265 self.request_options().set_backoff_policy(v);
266 self
267 }
268
269 fn with_retry_throttler<V: Into<RetryThrottlerArg>>(mut self, v: V) -> Self {
270 self.request_options().set_retry_throttler(v);
271 self
272 }
273
274 fn with_polling_error_policy<V: Into<PollingErrorPolicyArg>>(mut self, v: V) -> Self {
275 self.request_options().set_polling_error_policy(v);
276 self
277 }
278
279 fn with_polling_backoff_policy<V: Into<PollingBackoffPolicyArg>>(mut self, v: V) -> Self {
280 self.request_options().set_polling_backoff_policy(v);
281 self
282 }
283}
284
285#[cfg(test)]
286mod tests {
287 use super::internal::*;
288 use super::*;
289 use crate::exponential_backoff::ExponentialBackoffBuilder;
290 use crate::polling_error_policy;
291 use crate::retry_policy::LimitedAttemptCount;
292 use crate::retry_throttler::AdaptiveThrottler;
293 use std::time::Duration;
294 type Result = std::result::Result<(), Box<dyn std::error::Error>>;
295
296 #[derive(Debug, Default)]
297 struct TestBuilder {
298 request_options: RequestOptions,
299 }
300 impl RequestBuilder for TestBuilder {
301 fn request_options(&mut self) -> &mut RequestOptions {
302 &mut self.request_options
303 }
304 }
305
306 #[test]
307 fn request_options() {
308 let mut opts = RequestOptions::default();
309
310 assert_eq!(opts.idempotent, None);
311 opts.set_idempotency(true);
312 assert_eq!(opts.idempotent(), Some(true));
313 opts.set_idempotency(false);
314 assert_eq!(opts.idempotent(), Some(false));
315
316 opts.set_user_agent("test-only");
317 assert_eq!(opts.user_agent().as_deref(), Some("test-only"));
318 assert_eq!(opts.attempt_timeout(), &None);
319
320 let d = Duration::from_secs(123);
321 opts.set_attempt_timeout(d);
322 assert_eq!(opts.user_agent().as_deref(), Some("test-only"));
323 assert_eq!(opts.attempt_timeout(), &Some(d));
324
325 opts.set_retry_policy(LimitedAttemptCount::new(3));
326 assert!(opts.retry_policy().is_some(), "{opts:?}");
327
328 opts.set_backoff_policy(ExponentialBackoffBuilder::new().clamp());
329 assert!(opts.backoff_policy().is_some(), "{opts:?}");
330
331 opts.set_retry_throttler(AdaptiveThrottler::default());
332 assert!(opts.retry_throttler().is_some(), "{opts:?}");
333
334 opts.set_polling_error_policy(polling_error_policy::Aip194Strict);
335 assert!(opts.polling_error_policy().is_some(), "{opts:?}");
336
337 opts.set_polling_backoff_policy(ExponentialBackoffBuilder::new().clamp());
338 assert!(opts.polling_backoff_policy().is_some(), "{opts:?}");
339
340 opts.set_path_template("test");
341 assert_eq!(opts.path_template(), Some("test"));
342 }
343
344 #[test]
345 fn request_options_idempotency() {
346 let opts = set_default_idempotency(RequestOptions::default(), true);
347 assert_eq!(opts.idempotent(), Some(true));
348 let opts = set_default_idempotency(opts, false);
349 assert_eq!(opts.idempotent(), Some(true));
350
351 let opts = set_default_idempotency(RequestOptions::default(), false);
352 assert_eq!(opts.idempotent(), Some(false));
353 let opts = set_default_idempotency(opts, true);
354 assert_eq!(opts.idempotent(), Some(false));
355 }
356
357 #[test]
358 fn request_options_path_template() {
359 let opts = RequestOptions::default();
360 assert_eq!(opts.path_template(), None);
361 let opts = set_path_template(opts, "test");
362 assert_eq!(opts.path_template(), Some("test"));
363 }
364
365 #[test]
366 fn request_options_builder() -> Result {
367 let mut builder = TestBuilder::default();
368 assert_eq!(builder.request_options().user_agent(), &None);
369 assert_eq!(builder.request_options().attempt_timeout(), &None);
370
371 let mut builder = TestBuilder::default().with_idempotency(true);
372 assert_eq!(builder.request_options().idempotent(), Some(true));
373 let mut builder = TestBuilder::default().with_idempotency(false);
374 assert_eq!(builder.request_options().idempotent(), Some(false));
375
376 let mut builder = TestBuilder::default().with_user_agent("test-only");
377 assert_eq!(
378 builder.request_options().user_agent().as_deref(),
379 Some("test-only")
380 );
381 assert_eq!(builder.request_options().attempt_timeout(), &None);
382
383 let d = Duration::from_secs(123);
384 let mut builder = TestBuilder::default().with_attempt_timeout(d);
385 assert_eq!(builder.request_options().user_agent(), &None);
386 assert_eq!(builder.request_options().attempt_timeout(), &Some(d));
387
388 let mut builder = TestBuilder::default().with_retry_policy(LimitedAttemptCount::new(3));
389 assert!(
390 builder.request_options().retry_policy().is_some(),
391 "{builder:?}"
392 );
393
394 let mut builder =
395 TestBuilder::default().with_backoff_policy(ExponentialBackoffBuilder::new().build()?);
396 assert!(
397 builder.request_options().backoff_policy().is_some(),
398 "{builder:?}"
399 );
400
401 let mut builder = TestBuilder::default().with_retry_throttler(AdaptiveThrottler::default());
402 assert!(
403 builder.request_options().retry_throttler().is_some(),
404 "{builder:?}"
405 );
406
407 let mut builder =
408 TestBuilder::default().with_polling_error_policy(polling_error_policy::Aip194Strict);
409 assert!(
410 builder.request_options().polling_error_policy().is_some(),
411 "{builder:?}"
412 );
413
414 let mut builder = TestBuilder::default()
415 .with_polling_backoff_policy(ExponentialBackoffBuilder::new().build()?);
416 assert!(
417 builder.request_options().polling_backoff_policy().is_some(),
418 "{builder:?}"
419 );
420
421 Ok(())
422 }
423}