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}