netem_trace/model/delay.rs
1//! This module contains some predefined delay trace models.
2//!
3//! Enabled with feature `delay-model` or `model`.
4//!
5//! ## Predefined models
6//!
7//! - [`StaticDelay`]: A trace model with static delay.
8//! - [`RepeatedDelayPattern`]: A trace model with a repeated delay pattern.
9//!
10//! ## Examples
11//!
12//! An example to build model from configuration:
13//!
14//! ```
15//! # use netem_trace::model::StaticDelayConfig;
16//! # use netem_trace::{Delay, Duration, DelayTrace};
17//! let mut static_delay = StaticDelayConfig::new()
18//! .delay(Delay::from_millis(10))
19//! .duration(Duration::from_secs(1))
20//! .build();
21//! assert_eq!(static_delay.next_delay(), Some((Delay::from_millis(10), Duration::from_secs(1))));
22//! assert_eq!(static_delay.next_delay(), None);
23//! ```
24//!
25//! A more common use case is to build model from a configuration file (e.g. json file):
26//!
27//! ```
28//! # use netem_trace::model::{StaticDelayConfig, DelayTraceConfig};
29//! # use netem_trace::{Delay, Duration, DelayTrace};
30//! # #[cfg(feature = "human")]
31//! # let config_file_content = "{\"RepeatedDelayPatternConfig\":{\"pattern\":[{\"StaticDelayConfig\":{\"delay\":\"10ms\",\"duration\":\"1s\"}},{\"StaticDelayConfig\":{\"delay\":\"20ms\",\"duration\":\"1s\"}}],\"count\":2}}";
32//! // The content would be "{\"RepeatedDelayPatternConfig\":{\"pattern\":[{\"StaticDelayConfig\":{\"delay\":{\"secs\":0,\"nanos\":10000000},\"duration\":{\"secs\":1,\"nanos\":0}}},{\"StaticDelayConfig\":{\"delay\":{\"secs\":0,\"nanos\":20000000},\"duration\":{\"secs\":1,\"nanos\":0}}}],\"count\":2}}"
33//! // if the `human` feature is not enabled.
34//! # #[cfg(not(feature = "human"))]
35//! let config_file_content = "{\"RepeatedDelayPatternConfig\":{\"pattern\":[{\"StaticDelayConfig\":{\"delay\":{\"secs\":0,\"nanos\":10000000},\"duration\":{\"secs\":1,\"nanos\":0}}},{\"StaticDelayConfig\":{\"delay\":{\"secs\":0,\"nanos\":20000000},\"duration\":{\"secs\":1,\"nanos\":0}}}],\"count\":2}}";
36//! let des: Box<dyn DelayTraceConfig> = serde_json::from_str(config_file_content).unwrap();
37//! let mut model = des.into_model();
38//! assert_eq!(
39//! model.next_delay(),
40//! Some((Delay::from_millis(10), Duration::from_secs(1)))
41//! );
42//! assert_eq!(
43//! model.next_delay(),
44//! Some((Delay::from_millis(20), Duration::from_secs(1)))
45//! );
46//! assert_eq!(
47//! model.next_delay(),
48//! Some((Delay::from_millis(10), Duration::from_secs(1)))
49//! );
50//! assert_eq!(
51//! model.next_delay(),
52//! Some((Delay::from_millis(20), Duration::from_secs(1)))
53//! );
54//! assert_eq!(model.next_delay(), None);
55//! ```
56use crate::{Delay, DelayTrace, Duration};
57use dyn_clone::DynClone;
58
59/// This trait is used to convert a delay trace configuration into a delay trace model.
60///
61/// Since trace model is often configured with files and often has inner states which
62/// is not suitable to be serialized/deserialized, this trait makes it possible to
63/// separate the configuration part into a simple struct for serialization/deserialization, and
64/// construct the model from the configuration.
65#[cfg_attr(feature = "serde", typetag::serde)]
66pub trait DelayTraceConfig: DynClone + Send {
67 fn into_model(self: Box<Self>) -> Box<dyn DelayTrace>;
68}
69
70dyn_clone::clone_trait_object!(DelayTraceConfig);
71
72#[cfg(feature = "serde")]
73use serde::{Deserialize, Serialize};
74
75/// The model of a static delay trace.
76///
77/// ## Examples
78///
79/// ```
80/// # use netem_trace::model::StaticDelayConfig;
81/// # use netem_trace::{Delay, Duration, DelayTrace};
82/// let mut static_delay = StaticDelayConfig::new()
83/// .delay(Delay::from_millis(10))
84/// .duration(Duration::from_secs(1))
85/// .build();
86/// assert_eq!(static_delay.next_delay(), Some((Delay::from_millis(10), Duration::from_secs(1))));
87/// assert_eq!(static_delay.next_delay(), None);
88/// ```
89#[derive(Debug, Clone)]
90pub struct StaticDelay {
91 pub delay: Delay,
92 pub duration: Option<Duration>,
93}
94
95/// The configuration struct for [`StaticDelay`].
96///
97/// See [`StaticDelay`] for more details.
98#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(default))]
99#[derive(Debug, Clone, Default)]
100pub struct StaticDelayConfig {
101 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
102 #[cfg_attr(
103 all(feature = "serde", feature = "human"),
104 serde(with = "humantime_serde")
105 )]
106 pub delay: Option<Delay>,
107 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
108 #[cfg_attr(
109 all(feature = "serde", feature = "human"),
110 serde(with = "humantime_serde")
111 )]
112 pub duration: Option<Duration>,
113}
114
115/// The model contains an array of delay trace models.
116///
117/// Combine multiple delay trace models into one delay pattern,
118/// and repeat the pattern for `count` times.
119///
120/// If `count` is 0, the pattern will be repeated forever.
121///
122/// ## Examples
123///
124/// The most common use case is to read from a configuration file and
125/// deserialize it into a [`RepeatedDelayPatternConfig`]:
126///
127/// ```
128/// # use netem_trace::model::{StaticDelayConfig, DelayTraceConfig};
129/// # use netem_trace::{Delay, Duration, DelayTrace};
130/// # #[cfg(feature = "human")]
131/// # let config_file_content = "{\"RepeatedDelayPatternConfig\":{\"pattern\":[{\"StaticDelayConfig\":{\"delay\":\"10ms\",\"duration\":\"1s\"}},{\"StaticDelayConfig\":{\"delay\":\"20ms\",\"duration\":\"1s\"}}],\"count\":2}}";
132/// // The content would be "{\"RepeatedDelayPatternConfig\":{\"pattern\":[{\"StaticDelayConfig\":{\"delay\":{\"secs\":0,\"nanos\":10000000},\"duration\":{\"secs\":1,\"nanos\":0}}},{\"StaticDelayConfig\":{\"delay\":{\"secs\":0,\"nanos\":20000000},\"duration\":{\"secs\":1,\"nanos\":0}}}],\"count\":2}}"
133/// // if the `human` feature is not enabled.
134/// # #[cfg(not(feature = "human"))]
135/// let config_file_content = "{\"RepeatedDelayPatternConfig\":{\"pattern\":[{\"StaticDelayConfig\":{\"delay\":{\"secs\":0,\"nanos\":10000000},\"duration\":{\"secs\":1,\"nanos\":0}}},{\"StaticDelayConfig\":{\"delay\":{\"secs\":0,\"nanos\":20000000},\"duration\":{\"secs\":1,\"nanos\":0}}}],\"count\":2}}";
136/// let des: Box<dyn DelayTraceConfig> = serde_json::from_str(config_file_content).unwrap();
137/// let mut model = des.into_model();
138/// assert_eq!(
139/// model.next_delay(),
140/// Some((Delay::from_millis(10), Duration::from_secs(1)))
141/// );
142/// assert_eq!(
143/// model.next_delay(),
144/// Some((Delay::from_millis(20), Duration::from_secs(1)))
145/// );
146/// assert_eq!(
147/// model.next_delay(),
148/// Some((Delay::from_millis(10), Duration::from_secs(1)))
149/// );
150/// assert_eq!(
151/// model.next_delay(),
152/// Some((Delay::from_millis(20), Duration::from_secs(1)))
153/// );
154/// assert_eq!(model.next_delay(), None);
155/// ```
156///
157/// You can also build manually:
158///
159/// ```
160/// # use netem_trace::model::{StaticDelayConfig, DelayTraceConfig, RepeatedDelayPatternConfig};
161/// # use netem_trace::{Delay, Duration, DelayTrace};
162/// let pat = vec![
163/// Box::new(
164/// StaticDelayConfig::new()
165/// .delay(Delay::from_millis(10))
166/// .duration(Duration::from_secs(1)),
167/// ) as Box<dyn DelayTraceConfig>,
168/// Box::new(
169/// StaticDelayConfig::new()
170/// .delay(Delay::from_millis(20))
171/// .duration(Duration::from_secs(1)),
172/// ) as Box<dyn DelayTraceConfig>,
173/// ];
174/// let ser = Box::new(RepeatedDelayPatternConfig::new().pattern(pat).count(2)) as Box<dyn DelayTraceConfig>;
175/// let ser_str = serde_json::to_string(&ser).unwrap();
176/// # #[cfg(feature = "human")]
177/// # let json_str = "{\"RepeatedDelayPatternConfig\":{\"pattern\":[{\"StaticDelayConfig\":{\"delay\":\"10ms\",\"duration\":\"1s\"}},{\"StaticDelayConfig\":{\"delay\":\"20ms\",\"duration\":\"1s\"}}],\"count\":2}}";
178/// // The json string would be "{\"RepeatedDelayPatternConfig\":{\"pattern\":[{\"StaticDelayConfig\":{\"delay\":{\"secs\":0,\"nanos\":10000000},\"duration\":{\"secs\":1,\"nanos\":0}}},{\"StaticDelayConfig\":{\"delay\":{\"secs\":0,\"nanos\":20000000},\"duration\":{\"secs\":1,\"nanos\":0}}}],\"count\":2}}"
179/// // if the `human` feature is not enabled.
180/// # #[cfg(not(feature = "human"))]
181/// let json_str = "{\"RepeatedDelayPatternConfig\":{\"pattern\":[{\"StaticDelayConfig\":{\"delay\":{\"secs\":0,\"nanos\":10000000},\"duration\":{\"secs\":1,\"nanos\":0}}},{\"StaticDelayConfig\":{\"delay\":{\"secs\":0,\"nanos\":20000000},\"duration\":{\"secs\":1,\"nanos\":0}}}],\"count\":2}}";
182/// assert_eq!(ser_str, json_str);
183/// ```
184pub struct RepeatedDelayPattern {
185 pub pattern: Vec<Box<dyn DelayTraceConfig>>,
186 pub count: usize,
187 current_model: Option<Box<dyn DelayTrace>>,
188 current_cycle: usize,
189 current_pattern: usize,
190}
191
192/// The configuration struct for [`RepeatedDelayPattern`].
193///
194/// See [`RepeatedDelayPattern`] for more details.
195#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(default))]
196#[derive(Default, Clone)]
197pub struct RepeatedDelayPatternConfig {
198 pub pattern: Vec<Box<dyn DelayTraceConfig>>,
199 pub count: usize,
200}
201
202impl DelayTrace for StaticDelay {
203 fn next_delay(&mut self) -> Option<(Delay, Duration)> {
204 if let Some(duration) = self.duration.take() {
205 if duration.is_zero() {
206 None
207 } else {
208 Some((self.delay, duration))
209 }
210 } else {
211 None
212 }
213 }
214}
215
216impl DelayTrace for RepeatedDelayPattern {
217 fn next_delay(&mut self) -> Option<(Delay, Duration)> {
218 if self.pattern.is_empty() || (self.count != 0 && self.current_cycle >= self.count) {
219 None
220 } else {
221 if self.current_model.is_none() {
222 self.current_model = Some(self.pattern[self.current_pattern].clone().into_model());
223 }
224 match self.current_model.as_mut().unwrap().next_delay() {
225 Some(delay) => Some(delay),
226 None => {
227 self.current_model = None;
228 self.current_pattern += 1;
229 if self.current_pattern >= self.pattern.len() {
230 self.current_pattern = 0;
231 self.current_cycle += 1;
232 if self.count != 0 && self.current_cycle >= self.count {
233 return None;
234 }
235 }
236 self.next_delay()
237 }
238 }
239 }
240 }
241}
242
243impl StaticDelayConfig {
244 pub fn new() -> Self {
245 Self {
246 delay: None,
247 duration: None,
248 }
249 }
250
251 pub fn delay(mut self, delay: Delay) -> Self {
252 self.delay = Some(delay);
253 self
254 }
255
256 pub fn duration(mut self, duration: Duration) -> Self {
257 self.duration = Some(duration);
258 self
259 }
260
261 pub fn build(self) -> StaticDelay {
262 StaticDelay {
263 delay: self.delay.unwrap_or_else(|| Delay::from_millis(10)),
264 duration: Some(self.duration.unwrap_or_else(|| Duration::from_secs(1))),
265 }
266 }
267}
268
269impl RepeatedDelayPatternConfig {
270 pub fn new() -> Self {
271 Self {
272 pattern: vec![],
273 count: 0,
274 }
275 }
276
277 pub fn pattern(mut self, pattern: Vec<Box<dyn DelayTraceConfig>>) -> Self {
278 self.pattern = pattern;
279 self
280 }
281
282 pub fn count(mut self, count: usize) -> Self {
283 self.count = count;
284 self
285 }
286
287 pub fn build(self) -> RepeatedDelayPattern {
288 RepeatedDelayPattern {
289 pattern: self.pattern,
290 count: self.count,
291 current_model: None,
292 current_cycle: 0,
293 current_pattern: 0,
294 }
295 }
296}
297
298macro_rules! impl_delay_trace_config {
299 ($name:ident) => {
300 #[cfg_attr(feature = "serde", typetag::serde)]
301 impl DelayTraceConfig for $name {
302 fn into_model(self: Box<$name>) -> Box<dyn DelayTrace> {
303 Box::new(self.build())
304 }
305 }
306 };
307}
308
309impl_delay_trace_config!(StaticDelayConfig);
310impl_delay_trace_config!(RepeatedDelayPatternConfig);
311
312#[cfg(test)]
313mod test {
314 use super::*;
315 use crate::model::StaticDelayConfig;
316 use crate::DelayTrace;
317
318 #[test]
319 fn test_static_delay_model() {
320 let mut static_delay = StaticDelayConfig::new()
321 .delay(Delay::from_millis(10))
322 .duration(Duration::from_secs(1))
323 .build();
324 assert_eq!(
325 static_delay.next_delay(),
326 Some((Delay::from_millis(10), Duration::from_secs(1)))
327 );
328 assert_eq!(static_delay.next_delay(), None);
329 }
330
331 #[test]
332 #[cfg(feature = "serde")]
333 fn test_serde() {
334 let a = vec![
335 Box::new(
336 StaticDelayConfig::new()
337 .delay(Delay::from_millis(10))
338 .duration(Duration::from_secs(1)),
339 ) as Box<dyn DelayTraceConfig>,
340 Box::new(
341 StaticDelayConfig::new()
342 .delay(Delay::from_millis(20))
343 .duration(Duration::from_secs(1)),
344 ) as Box<dyn DelayTraceConfig>,
345 ];
346 let ser = Box::new(RepeatedDelayPatternConfig::new().pattern(a).count(2))
347 as Box<dyn DelayTraceConfig>;
348 let ser_str = serde_json::to_string(&ser).unwrap();
349 #[cfg(feature = "human")]
350 let des_str = "{\"RepeatedDelayPatternConfig\":{\"pattern\":[{\"StaticDelayConfig\":{\"delay\":\"10ms\",\"duration\":\"1s\"}},{\"StaticDelayConfig\":{\"delay\":\"20ms\",\"duration\":\"1s\"}}],\"count\":2}}";
351 #[cfg(not(feature = "human"))]
352 let des_str = "{\"RepeatedDelayPatternConfig\":{\"pattern\":[{\"StaticDelayConfig\":{\"delay\":{\"secs\":0,\"nanos\":10000000},\"duration\":{\"secs\":1,\"nanos\":0}}},{\"StaticDelayConfig\":{\"delay\":{\"secs\":0,\"nanos\":20000000},\"duration\":{\"secs\":1,\"nanos\":0}}}],\"count\":2}}";
353 assert_eq!(ser_str, des_str);
354 let des: Box<dyn DelayTraceConfig> = serde_json::from_str(des_str).unwrap();
355 let mut model = des.into_model();
356 assert_eq!(
357 model.next_delay(),
358 Some((Delay::from_millis(10), Duration::from_secs(1)))
359 );
360 }
361}