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#[derive(Clone, Debug, Default)]
21pub struct ClientOptions {
22    pub(crate) per_call_policies: Vec<Arc<dyn Policy>>,
24    pub(crate) per_retry_policies: Vec<Arc<dyn Policy>>,
26    pub(crate) retry: RetryOptions,
28    pub(crate) telemetry: TelemetryOptions,
30    pub(crate) transport: TransportOptions,
32    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    pub fn per_call_policies_mut(&mut self) -> &mut Vec<Arc<dyn Policy>> {
50        &mut self.per_call_policies
51    }
52
53    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#[derive(Clone)]
70enum RetryMode {
71    Exponential(ExponentialRetryOptions),
76
77    Fixed(FixedRetryOptions),
79
80    Custom(Arc<dyn Policy>),
82
83    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#[derive(Clone, Debug, Default)]
111pub struct RetryOptions {
112    mode: RetryMode,
114}
115
116impl RetryOptions {
117    pub fn exponential(options: ExponentialRetryOptions) -> Self {
119        Self {
120            mode: RetryMode::Exponential(options),
121        }
122    }
123
124    pub fn fixed(options: FixedRetryOptions) -> Self {
126        Self {
127            mode: RetryMode::Fixed(options),
128        }
129    }
130
131    pub fn custom<T: RetryPolicy + 'static>(policy: Arc<T>) -> Self {
133        Self {
134            mode: RetryMode::Custom(policy),
135        }
136    }
137
138    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#[derive(Clone, Debug)]
178pub struct ExponentialRetryOptions {
179    pub initial_delay: Duration,
183
184    pub max_retries: u32,
188
189    pub max_total_elapsed: Duration,
193
194    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#[derive(Clone, Debug)]
233pub struct FixedRetryOptions {
234    pub delay: Duration,
238
239    pub max_retries: u32,
243
244    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#[derive(Clone, Debug, Default)]
273pub struct TelemetryOptions {
274    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#[derive(Clone, Debug)]
287pub struct TransportOptions {
288    inner: TransportOptionsImpl,
289}
290
291#[derive(Clone, Debug)]
292enum TransportOptionsImpl {
293    Http {
294        http_client: Arc<dyn HttpClient>,
296    },
297    Custom(Arc<dyn Policy>),
298}
299
300impl TransportOptions {
301    pub fn new(http_client: Arc<dyn HttpClient>) -> Self {
303        let inner = TransportOptionsImpl::Http { http_client };
304        Self { inner }
305    }
306
307    pub fn new_custom_policy(policy: Arc<dyn Policy>) -> Self {
311        let inner = TransportOptionsImpl::Custom(policy);
312        Self { inner }
313    }
314
315    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    fn default() -> Self {
332        Self::new(http_client::new_http_client())
333    }
334}