netem_trace/model/
duplicate.rs

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