wreq_util/tower/delay/
layer.rs

1use std::time::Duration;
2
3use tower::Layer;
4
5use super::service::{Delay, DelayWith, JitterDelay, JitterDelayWith};
6
7/// A Tower [`Layer`] that introduces a fixed delay before each request.
8#[derive(Clone, Debug)]
9pub struct DelayLayer {
10    delay: Duration,
11}
12
13/// Conditional delay [`Layer`], applies delay based on a predicate.
14///
15/// Created via [`DelayLayer::when`].
16#[derive(Clone, Debug)]
17pub struct DelayLayerWith<P> {
18    delay: Duration,
19    predicate: P,
20}
21
22/// A Tower [`Layer`] that introduces a jittered delay before each request.
23///
24/// The actual delay for each request will be randomly chosen within the range
25/// `[base - base*pct, base + base*pct]`.
26///
27/// # Example
28///
29/// ```no_run
30/// use std::time::Duration;
31/// use wreq::Client;
32/// use wreq_util::middleware::delay::JitterDelayLayer;
33///
34/// // Creates delays in range [0.8s, 1.2s] (1s ± 20%)
35/// let client = Client::builder()
36///     .layer(JitterDelayLayer::new(Duration::from_secs(1), 0.2))
37///     .build()?;
38/// # Ok::<(), wreq::Error>(())
39/// ```
40#[derive(Clone, Debug)]
41pub struct JitterDelayLayer {
42    base: Duration,
43    pct: f64,
44}
45
46/// Conditional jitter delay [`Layer`], applies delay based on a predicate.
47///
48/// Created via [`JitterDelayLayer::when`].
49#[derive(Clone, Debug)]
50pub struct JitterDelayLayerWith<P> {
51    base: Duration,
52    pct: f64,
53    predicate: P,
54}
55
56// ===== impl DelayLayer =====
57
58impl DelayLayer {
59    /// Create a new [`DelayLayer`] with the given delay duration.
60    #[inline]
61    pub const fn new(delay: Duration) -> Self {
62        DelayLayer { delay }
63    }
64
65    /// Apply delay only to requests that satisfy a predicate.
66    ///
67    /// Requests that don't match the predicate will pass through without delay.
68    ///
69    /// # Example
70    ///
71    /// ```ignore
72    /// use std::time::Duration;
73    /// use http::Request;
74    /// use wreq_util::middleware::delay::DelayLayer;
75    ///
76    /// // Only delay POST requests
77    /// let layer = DelayLayer::new(Duration::from_secs(1))
78    ///     .when(|req: &Request<_>| req.method() == http::Method::POST);
79    /// ```
80    pub fn when<P, Req>(self, predicate: P) -> DelayLayerWith<P>
81    where
82        P: Fn(&Req) -> bool + Clone,
83    {
84        DelayLayerWith::new(self.delay, predicate)
85    }
86}
87
88impl<S> Layer<S> for DelayLayer {
89    type Service = Delay<S>;
90
91    #[inline]
92    fn layer(&self, service: S) -> Self::Service {
93        Delay::new(service, self.delay)
94    }
95}
96
97// ===== impl DelayLayerWith =====
98
99impl<P> DelayLayerWith<P> {
100    /// Creates a new [`DelayLayerWith`].
101    #[inline]
102    pub fn new(delay: Duration, predicate: P) -> Self {
103        Self { delay, predicate }
104    }
105}
106
107impl<P, S> Layer<S> for DelayLayerWith<P>
108where
109    P: Clone,
110{
111    type Service = DelayWith<S, P>;
112
113    #[inline]
114    fn layer(&self, inner: S) -> Self::Service {
115        DelayWith::new(inner, self.delay, self.predicate.clone())
116    }
117}
118
119// ===== impl JitterDelayLayer =====
120
121impl JitterDelayLayer {
122    /// Create a new [`JitterDelayLayer`] with the given base delay and jitter percentage.
123    ///
124    /// # Arguments
125    /// * `base` - The base delay duration
126    /// * `pct` - The jitter percentage (0.0 to 1.0), representing ±pct deviation
127    ///
128    /// # Example
129    ///
130    /// ```
131    /// use std::time::Duration;
132    /// use wreq_util::middleware::delay::JitterDelayLayer;
133    ///
134    /// // Creates delays in range [800ms, 1200ms]
135    /// let layer = JitterDelayLayer::new(Duration::from_secs(1), 0.2);
136    /// ```
137    #[inline]
138    pub fn new(base: Duration, pct: f64) -> Self {
139        Self {
140            base,
141            pct: pct.clamp(0.0, 1.0),
142        }
143    }
144
145    /// Apply jitter delay only to requests that satisfy a predicate.
146    ///
147    /// Requests that don't match the predicate will pass through without delay.
148    ///
149    /// # Example
150    ///
151    /// ```ignore
152    /// use std::time::Duration;
153    /// use http::Request;
154    /// use wreq_util::middleware::delay::JitterDelayLayer;
155    ///
156    /// // Only delay requests to paths starting with "/slow"
157    /// let layer = JitterDelayLayer::new(Duration::from_secs(1), 0.2)
158    ///     .when(|req: &Request<_>| req.uri().path().starts_with("/slow"));
159    /// ```
160    pub fn when<P, Req>(self, predicate: P) -> JitterDelayLayerWith<P>
161    where
162        P: Fn(&Req) -> bool + Clone,
163    {
164        JitterDelayLayerWith::new(self.base, self.pct, predicate)
165    }
166}
167
168impl<S> Layer<S> for JitterDelayLayer {
169    type Service = JitterDelay<S>;
170
171    #[inline]
172    fn layer(&self, inner: S) -> Self::Service {
173        JitterDelay::new(inner, self.base, self.pct)
174    }
175}
176
177// ===== impl JitterDelayLayerWith =====
178
179impl<P> JitterDelayLayerWith<P> {
180    /// Creates a new [`JitterDelayLayerWith`].
181    #[inline]
182    pub fn new(base: Duration, pct: f64, predicate: P) -> Self {
183        Self {
184            base,
185            pct: pct.clamp(0.0, 1.0),
186            predicate,
187        }
188    }
189}
190
191impl<P, S> Layer<S> for JitterDelayLayerWith<P>
192where
193    P: Clone,
194{
195    type Service = JitterDelayWith<S, P>;
196
197    #[inline]
198    fn layer(&self, inner: S) -> Self::Service {
199        JitterDelayWith::new(inner, self.base, self.pct, self.predicate.clone())
200    }
201}