use crate::policies::{ExponentialRetryPolicy, FixedRetryPolicy, NoRetryPolicy, Policy};
use crate::{http_client, TimeoutPolicy};
use crate::{HttpClient, RetryPolicy};
use std::fmt::Debug;
use std::sync::Arc;
use std::time::Duration;
#[derive(Clone, Debug, Default)]
pub struct ClientOptions {
pub(crate) per_call_policies: Vec<Arc<dyn Policy>>,
pub(crate) per_retry_policies: Vec<Arc<dyn Policy>>,
pub(crate) retry: RetryOptions,
pub(crate) telemetry: TelemetryOptions,
pub(crate) transport: TransportOptions,
pub timeout: TimeoutPolicy,
}
impl ClientOptions {
pub fn new(transport: TransportOptions) -> Self {
Self {
per_call_policies: Vec::new(),
per_retry_policies: Vec::new(),
retry: RetryOptions::default(),
telemetry: TelemetryOptions::default(),
transport,
timeout: TimeoutPolicy::default(),
}
}
pub fn per_call_policies_mut(&mut self) -> &mut Vec<Arc<dyn Policy>> {
&mut self.per_call_policies
}
pub fn per_retry_policies_mut(&mut self) -> &mut Vec<Arc<dyn Policy>> {
&mut self.per_retry_policies
}
setters! {
per_call_policies: Vec<Arc<dyn Policy>> => per_call_policies,
per_retry_policies: Vec<Arc<dyn Policy>> => per_retry_policies,
retry: RetryOptions => retry,
telemetry: TelemetryOptions => telemetry,
transport: TransportOptions => transport,
timeout: TimeoutPolicy => timeout,
}
}
#[derive(Clone)]
enum RetryMode {
Exponential(ExponentialRetryOptions),
Fixed(FixedRetryOptions),
Custom(Arc<dyn Policy>),
None,
}
impl Debug for RetryMode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
RetryMode::Exponential(o) => write!(f, "Exponetial({o:?})"),
RetryMode::Fixed(o) => write!(f, "Fixed({o:?})"),
RetryMode::Custom(_) => write!(f, "Custom"),
RetryMode::None => write!(f, "None"),
}
}
}
impl Default for RetryMode {
fn default() -> Self {
RetryMode::Exponential(ExponentialRetryOptions::default())
}
}
#[derive(Clone, Debug, Default)]
pub struct RetryOptions {
mode: RetryMode,
}
impl RetryOptions {
pub fn exponential(options: ExponentialRetryOptions) -> Self {
Self {
mode: RetryMode::Exponential(options),
}
}
pub fn fixed(options: FixedRetryOptions) -> Self {
Self {
mode: RetryMode::Fixed(options),
}
}
pub fn custom<T: RetryPolicy + 'static>(policy: Arc<T>) -> Self {
Self {
mode: RetryMode::Custom(policy),
}
}
pub fn none() -> Self {
Self {
mode: RetryMode::None,
}
}
pub(crate) fn to_policy(&self) -> Arc<dyn Policy> {
match &self.mode {
RetryMode::Exponential(options) => Arc::new(ExponentialRetryPolicy::new(
options.initial_delay,
options.max_retries,
options.max_total_elapsed,
options.max_delay,
)),
RetryMode::Fixed(options) => Arc::new(FixedRetryPolicy::new(
options.delay,
options.max_retries,
options.max_total_elapsed,
)),
RetryMode::Custom(c) => c.clone(),
RetryMode::None => Arc::new(NoRetryPolicy::default()),
}
}
}
#[derive(Clone, Debug)]
pub struct ExponentialRetryOptions {
pub initial_delay: Duration,
pub max_retries: u32,
pub max_total_elapsed: Duration,
pub max_delay: Duration,
}
impl ExponentialRetryOptions {
setters! {
initial_delay: Duration => initial_delay,
max_retries: u32 => max_retries,
max_total_elapsed: Duration => max_total_elapsed,
max_delay: Duration => max_delay,
}
}
impl Default for ExponentialRetryOptions {
fn default() -> Self {
Self {
initial_delay: Duration::from_millis(200),
max_retries: 8,
max_total_elapsed: Duration::from_secs(60),
max_delay: Duration::from_secs(30),
}
}
}
#[derive(Clone, Debug)]
pub struct FixedRetryOptions {
pub delay: Duration,
pub max_retries: u32,
pub max_total_elapsed: Duration,
}
impl FixedRetryOptions {
setters! {
#[doc = "Set the delay between retry attempts."]
delay: Duration => delay,
#[doc = "Set the maximum number of retry attempts before giving up."]
max_retries: u32 => max_retries,
#[doc = "Set the maximum permissible elapsed time since starting to retry."]
max_total_elapsed: Duration => max_total_elapsed,
}
}
impl Default for FixedRetryOptions {
fn default() -> Self {
Self {
delay: Duration::from_millis(200),
max_retries: 8,
max_total_elapsed: Duration::from_secs(60),
}
}
}
#[derive(Clone, Debug, Default)]
pub struct TelemetryOptions {
pub(crate) application_id: Option<String>,
}
impl TelemetryOptions {
setters! {
#[doc = "Set the application ID to telemetry."]
application_id: String => Some(application_id),
}
}
#[derive(Clone, Debug)]
pub struct TransportOptions {
inner: TransportOptionsImpl,
}
#[derive(Clone, Debug)]
enum TransportOptionsImpl {
Http {
http_client: Arc<dyn HttpClient>,
},
Custom(Arc<dyn Policy>),
}
impl TransportOptions {
pub fn new(http_client: Arc<dyn HttpClient>) -> Self {
let inner = TransportOptionsImpl::Http { http_client };
Self { inner }
}
pub fn new_custom_policy(policy: Arc<dyn Policy>) -> Self {
let inner = TransportOptionsImpl::Custom(policy);
Self { inner }
}
pub async fn send(
&self,
ctx: &crate::Context,
request: &mut crate::Request,
) -> crate::Result<crate::Response> {
use TransportOptionsImpl as I;
match &self.inner {
I::Http { http_client } => http_client.execute_request(request).await,
I::Custom(s) => s.send(ctx, request, &[]).await,
}
}
}
impl Default for TransportOptions {
fn default() -> Self {
Self::new(http_client::new_http_client())
}
}