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}