use std::{
collections::{HashMap, HashSet},
iter::FromIterator,
time,
};
use async_std::task;
use rand::Rng;
use serde::Deserialize;
use crate::{config, error, statics};
#[derive(Deserialize, Debug, Clone)]
pub struct Distribution {
pub min: u64,
pub p50: u64,
pub p75: u64,
pub p90: u64,
pub p95: u64,
pub p99: u64,
pub max: u64,
}
pub type Latencies = HashMap<String, Distribution>;
#[derive(Deserialize, Debug, Clone)]
pub struct Collection {
pub regex: String,
pub latencies: Latencies,
}
impl Collection {
pub fn default() -> Self {
Collection {
regex: r".+".to_string(),
latencies: HashMap::new(),
}
}
}
pub fn key(api: &str) -> Result<&str, error::FunesError> {
match statics::API_REGEX.find(api) {
Some(v) => Ok(v.as_str()),
None => Err(error::FunesError::LatencyCollection(api.to_string())),
}
}
fn latency(api: &str, collection: &Collection) -> Result<time::Duration, error::FunesError> {
let key = key(api)?;
let latency = collection.latencies.get(key).unwrap();
let mut rng = rand::thread_rng();
let random = rng.gen_range(0..=100);
let api_res_time = match random {
0..=50 => latency.p50,
51..=75 => latency.p75,
76..=90 => latency.p90,
91..=95 => latency.p95,
96..=99 => latency.p99,
100 => latency.max,
_ => latency.min,
};
Ok(time::Duration::from_millis(
api_res_time / *statics::ASYNC_TASK_SLEEP_MODIFIER,
))
}
pub async fn sleep(api: &str, collection: &Collection) -> Result<(), error::FunesError> {
if statics::ENVS.latency_enable {
let latency = latency(api, collection);
task::sleep(latency?).await;
}
Ok(())
}
pub fn validate() {
if statics::ENVS.latency_enable {
let keys = statics::LATENCY_COLLECTION
.latencies
.keys()
.cloned()
.collect::<Vec<String>>();
let regex_map: Vec<_> = keys
.iter()
.map(|v| key(v).unwrap_or("None").to_owned())
.collect();
if keys != regex_map {
let keys_set: HashSet<String> = HashSet::from_iter(keys.iter().cloned());
let regex_map_set: HashSet<String> = regex_map.iter().clone().cloned().collect();
let diff: HashSet<_> = keys_set.difference(®ex_map_set).collect();
log::error!(
"\n\nREGEX: \"{}\"\nDoesn`t fully match all keys from {}: {:#?}\n\nUnmatched keys \
are:\n {:#?}\n\n",
statics::LATENCY_COLLECTION.regex,
config::FUNES_LATENCY_COLLECTION,
keys_set,
diff,
);
panic!("Unmatched keys in {}", config::FUNES_LATENCY_COLLECTION);
}
}
}