azure_core/options/
mod.rs

1use crate::policies::{ExponentialRetryPolicy, FixedRetryPolicy, NoRetryPolicy, Policy};
2use crate::{http_client, TimeoutPolicy};
3use crate::{HttpClient, RetryPolicy};
4use std::fmt::Debug;
5use std::sync::Arc;
6use std::time::Duration;
7
8/// Client options allow customization of policies, retry options, and more.
9///
10/// # Examples
11///
12/// You can override default options and even add your own per-call or per-retry policies:
13///
14/// ```
15/// use azure_core::{ClientOptions, ExponentialRetryOptions, RetryOptions, TelemetryOptions};
16/// let options: ClientOptions = ClientOptions::default()
17///     .retry(RetryOptions::exponential(ExponentialRetryOptions::default().max_retries(10u32)))
18///     .telemetry(TelemetryOptions::default().application_id("my-application"));
19/// ```
20#[derive(Clone, Debug, Default)]
21pub struct ClientOptions {
22    /// Policies called per call.
23    pub(crate) per_call_policies: Vec<Arc<dyn Policy>>,
24    /// Policies called per retry.
25    pub(crate) per_retry_policies: Vec<Arc<dyn Policy>>,
26    /// Retry options.
27    pub(crate) retry: RetryOptions,
28    /// Telemetry options.
29    pub(crate) telemetry: TelemetryOptions,
30    /// Transport options.
31    pub(crate) transport: TransportOptions,
32    /// Transport options.
33    pub timeout: TimeoutPolicy,
34}
35
36impl ClientOptions {
37    pub fn new(transport: TransportOptions) -> Self {
38        Self {
39            per_call_policies: Vec::new(),
40            per_retry_policies: Vec::new(),
41            retry: RetryOptions::default(),
42            telemetry: TelemetryOptions::default(),
43            transport,
44            timeout: TimeoutPolicy::default(),
45        }
46    }
47
48    /// A mutable reference to per-call policies.
49    pub fn per_call_policies_mut(&mut self) -> &mut Vec<Arc<dyn Policy>> {
50        &mut self.per_call_policies
51    }
52
53    /// A mutable reference to per-retry policies.
54    pub fn per_retry_policies_mut(&mut self) -> &mut Vec<Arc<dyn Policy>> {
55        &mut self.per_retry_policies
56    }
57
58    setters! {
59        per_call_policies: Vec<Arc<dyn Policy>> => per_call_policies,
60        per_retry_policies: Vec<Arc<dyn Policy>> => per_retry_policies,
61        retry: RetryOptions => retry,
62        telemetry: TelemetryOptions => telemetry,
63        transport: TransportOptions => transport,
64        timeout: TimeoutPolicy => timeout,
65    }
66}
67
68/// The algorithm to apply when calculating the delay between retry attempts.
69#[derive(Clone)]
70enum RetryMode {
71    /// Retry attempts will delay based on a back-off strategy,
72    /// where each attempt will increase the duration that it waits before retrying.
73    ///
74    /// This is the default.
75    Exponential(ExponentialRetryOptions),
76
77    /// Retry attempts happen at fixed intervals; each delay is a consistent duration.
78    Fixed(FixedRetryOptions),
79
80    /// A custom retry policy
81    Custom(Arc<dyn Policy>),
82
83    /// Do not retry attempts.
84    None,
85}
86
87impl Debug for RetryMode {
88    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
89        match self {
90            RetryMode::Exponential(o) => write!(f, "Exponetial({o:?})"),
91            RetryMode::Fixed(o) => write!(f, "Fixed({o:?})"),
92            RetryMode::Custom(_) => write!(f, "Custom"),
93            RetryMode::None => write!(f, "None"),
94        }
95    }
96}
97
98impl Default for RetryMode {
99    fn default() -> Self {
100        RetryMode::Exponential(ExponentialRetryOptions::default())
101    }
102}
103
104/// Specify how retries should behave.
105///
106/// Note that not all requests can be retried. These options will only be used
107/// when a retry is attempted.
108///
109/// The default is an exponential retry policy using the default `ExponentialRetryOptions`.
110#[derive(Clone, Debug, Default)]
111pub struct RetryOptions {
112    /// The algorithm to use for calculating retry delays.
113    mode: RetryMode,
114}
115
116impl RetryOptions {
117    /// A retry strategy where attempts happen at intervals that get exponentially longer with each retry.
118    pub fn exponential(options: ExponentialRetryOptions) -> Self {
119        Self {
120            mode: RetryMode::Exponential(options),
121        }
122    }
123
124    /// A retry strategy where attempts happen at fixed intervals; each delay is a consistent duration.
125    pub fn fixed(options: FixedRetryOptions) -> Self {
126        Self {
127            mode: RetryMode::Fixed(options),
128        }
129    }
130
131    /// A custom retry using the supplied retry policy.
132    pub fn custom<T: RetryPolicy + 'static>(policy: Arc<T>) -> Self {
133        Self {
134            mode: RetryMode::Custom(policy),
135        }
136    }
137
138    /// No retries will be attempted.
139    pub fn none() -> Self {
140        Self {
141            mode: RetryMode::None,
142        }
143    }
144
145    pub(crate) fn to_policy(&self) -> Arc<dyn Policy> {
146        match &self.mode {
147            RetryMode::Exponential(options) => Arc::new(ExponentialRetryPolicy::new(
148                options.initial_delay,
149                options.max_retries,
150                options.max_total_elapsed,
151                options.max_delay,
152            )),
153            RetryMode::Fixed(options) => Arc::new(FixedRetryPolicy::new(
154                options.delay,
155                options.max_retries,
156                options.max_total_elapsed,
157            )),
158            RetryMode::Custom(c) => c.clone(),
159            RetryMode::None => Arc::new(NoRetryPolicy::default()),
160        }
161    }
162}
163
164/// Options for how an exponential retry strategy should behave.
165///
166/// # Example
167///
168/// Configuring retry to be exponential with 10 retries max and an initial delay of 1 second.
169/// ```
170/// # use core::time::Duration; use azure_core::RetryOptions; use azure_core::ExponentialRetryOptions;
171/// RetryOptions::exponential(
172///    ExponentialRetryOptions::default()
173///        .max_retries(10u32)
174///        .initial_delay(Duration::from_secs(1)),
175/// );
176/// ```
177#[derive(Clone, Debug)]
178pub struct ExponentialRetryOptions {
179    /// The initial delay between retry attempts. The delay will increase with each retry.
180    ///
181    /// The default is 200 milliseconds.
182    pub initial_delay: Duration,
183
184    /// The maximum number of retry attempts before giving up.
185    ///
186    /// The default is 8.
187    pub max_retries: u32,
188
189    /// The maximum permissible elapsed time since starting to retry before giving up.
190    ///
191    /// The default is 1 minute.
192    pub max_total_elapsed: Duration,
193
194    /// The maximum permissible time between retries.
195    ///
196    /// The default is 30 seconds. For SRE reasons, this is only respected when above 1 second.
197    pub max_delay: Duration,
198}
199
200impl ExponentialRetryOptions {
201    setters! {
202        initial_delay: Duration => initial_delay,
203        max_retries: u32 => max_retries,
204        max_total_elapsed: Duration => max_total_elapsed,
205        max_delay: Duration => max_delay,
206    }
207}
208
209impl Default for ExponentialRetryOptions {
210    fn default() -> Self {
211        Self {
212            initial_delay: Duration::from_millis(200),
213            max_retries: 8,
214            max_total_elapsed: Duration::from_secs(60),
215            max_delay: Duration::from_secs(30),
216        }
217    }
218}
219
220/// Options for how a fixed retry strategy should behave.
221///
222/// # Example
223///
224/// Configuring retry to be fixed with 10 retries max.
225/// ```
226/// # use azure_core::RetryOptions; use azure_core::FixedRetryOptions;
227/// RetryOptions::fixed(
228///    FixedRetryOptions::default()
229///        .max_retries(10u32)
230/// );
231/// ```
232#[derive(Clone, Debug)]
233pub struct FixedRetryOptions {
234    /// The delay between retry attempts.
235    ///
236    /// The default is 200 milliseconds.
237    pub delay: Duration,
238
239    /// The maximum number of retry attempts before giving up.
240    ///
241    /// The default is 8.
242    pub max_retries: u32,
243
244    /// The maximum permissible elapsed time since starting to retry.
245    ///
246    /// The default is 1 minute.
247    pub max_total_elapsed: Duration,
248}
249
250impl FixedRetryOptions {
251    setters! {
252        #[doc = "Set the delay between retry attempts."]
253        delay: Duration => delay,
254        #[doc = "Set the maximum number of retry attempts before giving up."]
255        max_retries: u32 => max_retries,
256        #[doc = "Set the maximum permissible elapsed time since starting to retry."]
257        max_total_elapsed: Duration => max_total_elapsed,
258    }
259}
260
261impl Default for FixedRetryOptions {
262    fn default() -> Self {
263        Self {
264            delay: Duration::from_millis(200),
265            max_retries: 8,
266            max_total_elapsed: Duration::from_secs(60),
267        }
268    }
269}
270
271/// Telemetry options.
272#[derive(Clone, Debug, Default)]
273pub struct TelemetryOptions {
274    /// Optional application ID to telemetry.
275    pub(crate) application_id: Option<String>,
276}
277
278impl TelemetryOptions {
279    setters! {
280        #[doc = "Set the application ID to telemetry."]
281        application_id: String => Some(application_id),
282    }
283}
284
285/// Transport options.
286#[derive(Clone, Debug)]
287pub struct TransportOptions {
288    inner: TransportOptionsImpl,
289}
290
291#[derive(Clone, Debug)]
292enum TransportOptionsImpl {
293    Http {
294        /// The HTTP client implementation to use for requests.
295        http_client: Arc<dyn HttpClient>,
296    },
297    Custom(Arc<dyn Policy>),
298}
299
300impl TransportOptions {
301    /// Creates a new `TransportOptions` using the given `HttpClient`.
302    pub fn new(http_client: Arc<dyn HttpClient>) -> Self {
303        let inner = TransportOptionsImpl::Http { http_client };
304        Self { inner }
305    }
306
307    /// Creates a new `TransportOptions` using the custom policy.
308    ///
309    /// This policy is expected to be the last policy in the pipeline.
310    pub fn new_custom_policy(policy: Arc<dyn Policy>) -> Self {
311        let inner = TransportOptionsImpl::Custom(policy);
312        Self { inner }
313    }
314
315    /// Use these options to send a request.
316    pub async fn send(
317        &self,
318        ctx: &crate::Context,
319        request: &mut crate::Request,
320    ) -> crate::Result<crate::Response> {
321        use TransportOptionsImpl as I;
322        match &self.inner {
323            I::Http { http_client } => http_client.execute_request(request).await,
324            I::Custom(s) => s.send(ctx, request, &[]).await,
325        }
326    }
327}
328
329impl Default for TransportOptions {
330    /// Creates an instance of the `TransportOptions` using the default `HttpClient`.
331    fn default() -> Self {
332        Self::new(http_client::new_http_client())
333    }
334}