mod client;
mod server;
pub use client::*;
pub use server::*;
use web_time::{Duration, Instant};
use std::fmt::Debug;
use rand::Rng;
use rand_distr::{Distribution, Normal};
#[derive(Debug, Clone, Default)]
pub struct ConditionerConfig {
pub loss_rate: f32,
pub delay_mean: f32,
pub delay_std_dev: f32,
}
#[derive(Debug, Clone)]
struct Conditioner<E> {
loss_rate: f32,
delay_distr: Normal<f32>,
event_buf: Vec<ScheduledEvent<E>>,
}
#[derive(Debug, Clone)]
struct ScheduledEvent<E> {
event: E,
send_at: Instant,
}
impl<E> Conditioner<E> {
fn new(config: &ConditionerConfig) -> Self {
let loss_rate = config.loss_rate.clamp(0.0, 1.0);
let delay_distr = Normal::new(config.delay_mean, config.delay_std_dev)
.expect("should be a valid normal distribution");
Self {
loss_rate,
delay_distr,
event_buf: Vec::new(),
}
}
fn set_config(&mut self, config: &ConditionerConfig) {
let from = Self::new(config);
self.loss_rate = from.loss_rate;
self.delay_distr = from.delay_distr;
}
fn condition(&mut self, event: E) -> Option<E> {
let mut rng = rand::thread_rng();
if rng.gen::<f32>() < self.loss_rate {
return None;
}
let delay_sec = self.delay_distr.sample(&mut rand::thread_rng());
if delay_sec <= 0.0 {
return Some(event);
}
let send_at = Instant::now() + Duration::from_secs_f32(delay_sec);
self.event_buf.push(ScheduledEvent { event, send_at });
None
}
fn buffered(&mut self) -> impl Iterator<Item = E> {
let now = Instant::now();
let event_buf = std::mem::take(&mut self.event_buf);
let mut buffered = Vec::new();
for event in event_buf {
if now > event.send_at {
buffered.push(event.event);
} else {
self.event_buf.push(event);
}
}
buffered.into_iter()
}
}