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}