use std::collections::HashMap;
use std::time::Duration;
use crate::{DrawSite, RandomProvider, SimulationConfig};
use rand::SeedableRng;
pub trait ArrivalPattern: Send {
fn next_arrival_time(&mut self) -> Duration;
}
#[derive(Debug, Clone, Default)]
pub struct RequestContext {
pub method: String,
pub uri: String,
pub headers: HashMap<String, String>,
pub body_size: usize,
pub payload: Vec<u8>,
pub client_info: Option<ClientInfo>,
}
#[derive(Debug, Clone)]
pub struct ClientInfo {
pub client_id: String,
pub priority: u8,
pub region: String,
}
pub trait ServiceTimeDistribution: Send {
fn sample(&mut self) -> Duration {
self.sample_service_time(&RequestContext::default())
}
fn sample_service_time(&mut self, request: &RequestContext) -> Duration;
fn expected_service_time(&self, _request: &RequestContext) -> Duration {
unimplemented!("expected_service_time must be implemented by each distribution")
}
}
#[derive(Debug, Clone)]
pub struct ConstantArrivalPattern {
inter_arrival_time: Duration,
}
impl ConstantArrivalPattern {
pub fn new(inter_arrival_time: Duration) -> Self {
Self { inter_arrival_time }
}
}
impl ArrivalPattern for ConstantArrivalPattern {
fn next_arrival_time(&mut self) -> Duration {
self.inter_arrival_time
}
}
pub struct PoissonArrivals {
rate: f64,
rng: rand::rngs::StdRng,
exp_dist: rand_distr::Exp<f64>,
provider: Option<Box<dyn RandomProvider>>,
site: DrawSite,
}
impl PoissonArrivals {
pub fn new(rate: f64) -> Self {
assert!(rate > 0.0, "Rate must be positive");
let exp_dist = rand_distr::Exp::new(rate).expect("Rate must be positive");
Self {
rate,
rng: rand::SeedableRng::from_entropy(),
exp_dist,
provider: None,
site: DrawSite::new("arrival", 0),
}
}
pub fn from_config(config: &SimulationConfig, rate: f64) -> Self {
assert!(rate > 0.0, "Rate must be positive");
let exp_dist = rand_distr::Exp::new(rate).expect("Rate must be positive");
let mut seed = config.seed ^ 0xA5A5_5A5A_0101_0203u64;
seed ^= rate.to_bits();
Self {
rate,
rng: rand::rngs::StdRng::seed_from_u64(seed),
exp_dist,
provider: None,
site: DrawSite::new("arrival", 0),
}
}
#[must_use]
pub fn with_provider(mut self, provider: Box<dyn RandomProvider>, site: DrawSite) -> Self {
self.provider = Some(provider);
self.site = site;
self
}
pub fn rate(&self) -> f64 {
self.rate
}
}
impl ArrivalPattern for PoissonArrivals {
fn next_arrival_time(&mut self) -> Duration {
use rand::Rng;
let inter_arrival_seconds: f64 = if let Some(provider) = &mut self.provider {
provider.sample_exp_seconds(self.site, self.rate)
} else {
self.rng.sample(self.exp_dist)
};
Duration::from_secs_f64(inter_arrival_seconds)
}
}
pub struct BurstyArrivals {
burst_duration: Duration,
quiet_duration: Duration,
current_phase: BurstPhase,
phase_remaining: Duration,
rng: rand::rngs::StdRng,
burst_dist: Option<rand_distr::Exp<f64>>,
quiet_dist: Option<rand_distr::Exp<f64>>,
burst_rate: f64,
quiet_rate: f64,
provider: Option<Box<dyn RandomProvider>>,
site: DrawSite,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum BurstPhase {
Burst,
Quiet,
}
impl BurstyArrivals {
pub fn new(
burst_duration: Duration,
quiet_duration: Duration,
burst_rate: f64,
quiet_rate: f64,
) -> Self {
assert!(burst_rate > 0.0, "Burst rate must be positive");
assert!(quiet_rate >= 0.0, "Quiet rate must be non-negative");
let burst_dist =
Some(rand_distr::Exp::new(burst_rate).expect("Burst rate must be positive"));
let quiet_dist = if quiet_rate > 0.0 {
Some(rand_distr::Exp::new(quiet_rate).expect("Quiet rate must be positive"))
} else {
None
};
Self {
burst_duration,
quiet_duration,
current_phase: BurstPhase::Burst,
phase_remaining: burst_duration,
rng: rand::SeedableRng::from_entropy(),
burst_dist,
quiet_dist,
burst_rate,
quiet_rate,
provider: None,
site: DrawSite::new("bursty_arrival", 0),
}
}
pub fn from_config(
config: &SimulationConfig,
burst_duration: Duration,
quiet_duration: Duration,
burst_rate: f64,
quiet_rate: f64,
) -> Self {
assert!(burst_rate > 0.0, "Burst rate must be positive");
assert!(quiet_rate >= 0.0, "Quiet rate must be non-negative");
let burst_dist =
Some(rand_distr::Exp::new(burst_rate).expect("Burst rate must be positive"));
let quiet_dist = if quiet_rate > 0.0 {
Some(rand_distr::Exp::new(quiet_rate).expect("Quiet rate must be positive"))
} else {
None
};
let mut seed = config.seed ^ 0xB4B4_4B4B_0202_0305u64;
seed ^= burst_duration.as_nanos() as u64;
seed ^= (quiet_duration.as_nanos() as u64).rotate_left(11);
seed ^= burst_rate.to_bits();
seed ^= quiet_rate.to_bits().rotate_left(7);
Self {
burst_duration,
quiet_duration,
current_phase: BurstPhase::Burst,
phase_remaining: burst_duration,
rng: rand::rngs::StdRng::seed_from_u64(seed),
burst_dist,
quiet_dist,
burst_rate,
quiet_rate,
provider: None,
site: DrawSite::new("bursty_arrival", 0),
}
}
pub fn current_phase(&self) -> BurstPhase {
self.current_phase
}
pub fn phase_remaining(&self) -> Duration {
self.phase_remaining
}
#[must_use]
pub fn with_provider(mut self, provider: Box<dyn RandomProvider>, site: DrawSite) -> Self {
self.provider = Some(provider);
self.site = site;
self
}
}
impl ArrivalPattern for BurstyArrivals {
fn next_arrival_time(&mut self) -> Duration {
use rand::Rng;
let inter_arrival = match self.current_phase {
BurstPhase::Burst => {
if let Some(ref dist) = self.burst_dist {
let seconds: f64 = if let Some(provider) = &mut self.provider {
provider.sample_exp_seconds(self.site, self.burst_rate)
} else {
self.rng.sample(dist)
};
Duration::from_secs_f64(seconds)
} else {
Duration::from_secs(1)
}
}
BurstPhase::Quiet => {
if let Some(ref dist) = self.quiet_dist {
let seconds: f64 = if let Some(provider) = &mut self.provider {
provider.sample_exp_seconds(self.site, self.quiet_rate)
} else {
self.rng.sample(dist)
};
Duration::from_secs_f64(seconds)
} else {
self.phase_remaining
}
}
};
if inter_arrival >= self.phase_remaining {
let remaining_time = inter_arrival - self.phase_remaining;
match self.current_phase {
BurstPhase::Burst => {
self.current_phase = BurstPhase::Quiet;
self.phase_remaining = self.quiet_duration;
}
BurstPhase::Quiet => {
self.current_phase = BurstPhase::Burst;
self.phase_remaining = self.burst_duration;
}
}
if remaining_time > Duration::ZERO {
self.phase_remaining = self.phase_remaining.saturating_sub(remaining_time);
inter_arrival
} else {
self.next_arrival_time()
}
} else {
self.phase_remaining = self.phase_remaining.saturating_sub(inter_arrival);
inter_arrival
}
}
}
#[derive(Debug, Clone)]
pub struct ConstantServiceTime {
duration: Duration,
}
impl ConstantServiceTime {
pub fn new(duration: Duration) -> Self {
Self { duration }
}
}
impl ServiceTimeDistribution for ConstantServiceTime {
fn sample(&mut self) -> Duration {
self.duration
}
fn sample_service_time(&mut self, _request: &RequestContext) -> Duration {
self.duration
}
fn expected_service_time(&self, _request: &RequestContext) -> Duration {
self.duration
}
}
pub struct ExponentialDistribution {
rate: f64,
rng: rand::rngs::StdRng,
exp_dist: rand_distr::Exp<f64>,
provider: Option<Box<dyn RandomProvider>>,
site: DrawSite,
}
impl ExponentialDistribution {
pub fn new(rate: f64) -> Self {
assert!(rate > 0.0, "Rate must be positive");
let exp_dist = rand_distr::Exp::new(rate).expect("Rate must be positive");
Self {
rate,
rng: rand::SeedableRng::from_entropy(),
exp_dist,
provider: None,
site: DrawSite::new("service", 0),
}
}
pub fn from_config(config: &SimulationConfig, rate: f64) -> Self {
assert!(rate > 0.0, "Rate must be positive");
let exp_dist = rand_distr::Exp::new(rate).expect("Rate must be positive");
let mut seed = config.seed ^ 0xC3C3_3C3C_0303_0407u64;
seed ^= rate.to_bits();
Self {
rate,
rng: rand::rngs::StdRng::seed_from_u64(seed),
exp_dist,
provider: None,
site: DrawSite::new("service", 0),
}
}
#[must_use]
pub fn with_provider(mut self, provider: Box<dyn RandomProvider>, site: DrawSite) -> Self {
self.provider = Some(provider);
self.site = site;
self
}
pub fn rate(&self) -> f64 {
self.rate
}
pub fn mean_service_time(&self) -> Duration {
Duration::from_secs_f64(1.0 / self.rate)
}
}
impl ServiceTimeDistribution for ExponentialDistribution {
fn sample(&mut self) -> Duration {
use rand::Rng;
let service_time_seconds: f64 = if let Some(provider) = &mut self.provider {
provider.sample_exp_seconds(self.site, self.rate)
} else {
self.rng.sample(self.exp_dist)
};
Duration::from_secs_f64(service_time_seconds)
}
fn sample_service_time(&mut self, _request: &RequestContext) -> Duration {
self.sample()
}
fn expected_service_time(&self, _request: &RequestContext) -> Duration {
self.mean_service_time()
}
}
pub struct UniformDistribution {
min_duration: Duration,
max_duration: Duration,
rng: rand::rngs::StdRng,
uniform_dist: rand_distr::Uniform<f64>,
}
impl UniformDistribution {
pub fn new(min_duration: Duration, max_duration: Duration) -> Self {
assert!(
min_duration < max_duration,
"Minimum duration must be less than maximum duration"
);
let min_secs = min_duration.as_secs_f64();
let max_secs = max_duration.as_secs_f64();
let uniform_dist = rand_distr::Uniform::new(min_secs, max_secs);
Self {
min_duration,
max_duration,
rng: rand::SeedableRng::from_entropy(),
uniform_dist,
}
}
pub fn from_config(
config: &SimulationConfig,
min_duration: Duration,
max_duration: Duration,
) -> Self {
assert!(
min_duration < max_duration,
"Minimum duration must be less than maximum duration"
);
let min_secs = min_duration.as_secs_f64();
let max_secs = max_duration.as_secs_f64();
let uniform_dist = rand_distr::Uniform::new(min_secs, max_secs);
let mut seed = config.seed ^ 0xD2D2_2D2D_0404_0509u64;
seed ^= min_duration.as_nanos() as u64;
seed ^= (max_duration.as_nanos() as u64).rotate_left(13);
Self {
min_duration,
max_duration,
rng: rand::rngs::StdRng::seed_from_u64(seed),
uniform_dist,
}
}
pub fn min_duration(&self) -> Duration {
self.min_duration
}
pub fn max_duration(&self) -> Duration {
self.max_duration
}
pub fn mean_service_time(&self) -> Duration {
let mean_secs = (self.min_duration.as_secs_f64() + self.max_duration.as_secs_f64()) / 2.0;
Duration::from_secs_f64(mean_secs)
}
}
impl ServiceTimeDistribution for UniformDistribution {
fn sample(&mut self) -> Duration {
use rand::Rng;
let service_time_seconds: f64 = self.rng.sample(self.uniform_dist);
Duration::from_secs_f64(service_time_seconds)
}
fn sample_service_time(&mut self, _request: &RequestContext) -> Duration {
self.sample()
}
fn expected_service_time(&self, _request: &RequestContext) -> Duration {
self.mean_service_time()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_constant_arrival_pattern() {
let mut pattern = ConstantArrivalPattern::new(Duration::from_millis(100));
assert_eq!(pattern.next_arrival_time(), Duration::from_millis(100));
assert_eq!(pattern.next_arrival_time(), Duration::from_millis(100));
}
#[test]
fn test_poisson_arrivals_creation() {
let pattern = PoissonArrivals::new(1.0);
assert_eq!(pattern.rate(), 1.0);
}
#[test]
#[should_panic(expected = "Rate must be positive")]
fn test_poisson_arrivals_invalid_rate() {
PoissonArrivals::new(0.0);
}
#[test]
fn test_poisson_arrivals_generates_positive_times() {
let mut pattern = PoissonArrivals::new(10.0);
for _ in 0..10 {
let time = pattern.next_arrival_time();
assert!(
time > Duration::ZERO,
"Inter-arrival time should be positive"
);
assert!(
time < Duration::from_secs(1),
"Inter-arrival time should be reasonable"
);
}
}
#[test]
fn test_poisson_arrivals_provider_injection() {
struct MockProvider {
call_count: std::cell::RefCell<usize>,
fixed_value: f64,
}
impl MockProvider {
fn new(fixed_value: f64) -> Self {
Self {
call_count: std::cell::RefCell::new(0),
fixed_value,
}
}
fn get_call_count(&self) -> usize {
*self.call_count.borrow()
}
}
impl RandomProvider for MockProvider {
fn sample_exp_seconds(&mut self, _site: DrawSite, _rate: f64) -> f64 {
*self.call_count.borrow_mut() += 1;
self.fixed_value
}
}
let provider = Box::new(MockProvider::new(0.123)); let provider_ptr = provider.as_ref() as *const MockProvider;
let mut pattern =
PoissonArrivals::new(2.0).with_provider(provider, DrawSite::new("test", 1));
for _ in 0..3 {
let time = pattern.next_arrival_time();
assert_eq!(time, Duration::from_secs_f64(0.123));
}
unsafe {
assert_eq!((*provider_ptr).get_call_count(), 3);
}
}
#[test]
fn test_bursty_arrivals_creation() {
let pattern = BurstyArrivals::new(
Duration::from_secs(1), Duration::from_secs(2), 10.0, 1.0, );
assert_eq!(pattern.current_phase(), BurstPhase::Burst);
assert_eq!(pattern.phase_remaining(), Duration::from_secs(1));
}
#[test]
#[should_panic(expected = "Burst rate must be positive")]
fn test_bursty_arrivals_invalid_burst_rate() {
BurstyArrivals::new(
Duration::from_secs(1),
Duration::from_secs(2),
0.0, 1.0,
);
}
#[test]
#[should_panic(expected = "Quiet rate must be non-negative")]
fn test_bursty_arrivals_invalid_quiet_rate() {
BurstyArrivals::new(
Duration::from_secs(1),
Duration::from_secs(2),
10.0,
-1.0, );
}
#[test]
fn test_bursty_arrivals_generates_positive_times() {
let mut pattern = BurstyArrivals::new(
Duration::from_millis(100), Duration::from_millis(200), 100.0, 0.0, );
for _ in 0..5 {
let time = pattern.next_arrival_time();
assert!(
time > Duration::ZERO,
"Inter-arrival time should be positive"
);
}
}
#[test]
fn test_bursty_arrivals_with_zero_quiet_rate() {
let mut pattern = BurstyArrivals::new(
Duration::from_millis(10), Duration::from_millis(100), 1000.0, 0.0, );
let time = pattern.next_arrival_time();
assert!(time > Duration::ZERO);
}
#[test]
fn test_bursty_arrivals_provider_injection() {
struct MockProvider {
call_count: std::cell::RefCell<usize>,
fixed_value: f64,
}
impl MockProvider {
fn new(fixed_value: f64) -> Self {
Self {
call_count: std::cell::RefCell::new(0),
fixed_value,
}
}
fn get_call_count(&self) -> usize {
*self.call_count.borrow()
}
}
impl RandomProvider for MockProvider {
fn sample_exp_seconds(&mut self, _site: DrawSite, _rate: f64) -> f64 {
*self.call_count.borrow_mut() += 1;
self.fixed_value
}
}
let provider = Box::new(MockProvider::new(0.5)); let provider_ptr = provider.as_ref() as *const MockProvider;
let mut pattern = BurstyArrivals::new(
Duration::from_secs(1), Duration::from_secs(1), 2.0, 1.0, )
.with_provider(provider, DrawSite::new("test", 1));
for _ in 0..5 {
let time = pattern.next_arrival_time();
assert_eq!(time, Duration::from_secs_f64(0.5));
}
unsafe {
assert!((*provider_ptr).get_call_count() > 0);
}
}
#[test]
fn test_constant_service_time() {
let mut dist = ConstantServiceTime::new(Duration::from_millis(100));
assert_eq!(dist.sample(), Duration::from_millis(100));
assert_eq!(dist.sample(), Duration::from_millis(100));
}
#[test]
fn test_exponential_distribution_creation() {
let dist = ExponentialDistribution::new(2.0);
assert_eq!(dist.rate(), 2.0);
assert_eq!(dist.mean_service_time(), Duration::from_secs_f64(0.5));
}
#[test]
#[should_panic(expected = "Rate must be positive")]
fn test_exponential_distribution_invalid_rate() {
ExponentialDistribution::new(0.0);
}
#[test]
fn test_exponential_distribution_sampling() {
let mut dist = ExponentialDistribution::new(10.0);
for _ in 0..10 {
let time = dist.sample();
assert!(time > Duration::ZERO, "Service time should be positive");
assert!(
time < Duration::from_secs(1),
"Service time should be reasonable"
);
}
}
#[test]
fn test_exponential_distribution_provider_injection() {
struct MockProvider {
call_count: std::cell::RefCell<usize>,
fixed_value: f64,
}
impl MockProvider {
fn new(fixed_value: f64) -> Self {
Self {
call_count: std::cell::RefCell::new(0),
fixed_value,
}
}
fn get_call_count(&self) -> usize {
*self.call_count.borrow()
}
}
impl RandomProvider for MockProvider {
fn sample_exp_seconds(&mut self, _site: DrawSite, _rate: f64) -> f64 {
*self.call_count.borrow_mut() += 1;
self.fixed_value
}
}
let provider = Box::new(MockProvider::new(0.456)); let provider_ptr = provider.as_ref() as *const MockProvider;
let mut dist =
ExponentialDistribution::new(5.0).with_provider(provider, DrawSite::new("test", 1));
for _ in 0..3 {
let time = dist.sample();
assert_eq!(time, Duration::from_secs_f64(0.456));
}
unsafe {
assert_eq!((*provider_ptr).get_call_count(), 3);
}
}
#[test]
fn test_uniform_distribution_creation() {
let min = Duration::from_millis(50);
let max = Duration::from_millis(150);
let dist = UniformDistribution::new(min, max);
assert_eq!(dist.min_duration(), min);
assert_eq!(dist.max_duration(), max);
assert_eq!(dist.mean_service_time(), Duration::from_millis(100));
}
#[test]
#[should_panic(expected = "Minimum duration must be less than maximum duration")]
fn test_uniform_distribution_invalid_range() {
let min = Duration::from_millis(150);
let max = Duration::from_millis(50);
UniformDistribution::new(min, max);
}
#[test]
#[should_panic(expected = "Minimum duration must be less than maximum duration")]
fn test_uniform_distribution_equal_range() {
let duration = Duration::from_millis(100);
UniformDistribution::new(duration, duration);
}
#[test]
fn test_uniform_distribution_sampling() {
let min = Duration::from_millis(50);
let max = Duration::from_millis(150);
let mut dist = UniformDistribution::new(min, max);
for _ in 0..20 {
let time = dist.sample();
assert!(time >= min, "Service time should be >= minimum");
assert!(time <= max, "Service time should be <= maximum");
}
}
#[test]
fn test_uniform_distribution_range_coverage() {
let min = Duration::from_millis(100);
let max = Duration::from_millis(200);
let mut dist = UniformDistribution::new(min, max);
let mut samples = Vec::new();
for _ in 0..100 {
samples.push(dist.sample());
}
let min_sample = samples.iter().min().unwrap();
let max_sample = samples.iter().max().unwrap();
let range = max.as_millis() - min.as_millis();
let tolerance = range / 5;
assert!(min_sample.as_millis() <= min.as_millis() + tolerance);
assert!(max_sample.as_millis() >= max.as_millis() - tolerance);
}
}
pub struct RequestSizeBasedServiceTime {
base_time: Duration,
time_per_byte: Duration,
max_time: Duration,
}
impl RequestSizeBasedServiceTime {
pub fn new(base_time: Duration, time_per_byte: Duration, max_time: Duration) -> Self {
Self {
base_time,
time_per_byte,
max_time,
}
}
pub fn base_time(&self) -> Duration {
self.base_time
}
pub fn time_per_byte(&self) -> Duration {
self.time_per_byte
}
pub fn max_time(&self) -> Duration {
self.max_time
}
}
impl ServiceTimeDistribution for RequestSizeBasedServiceTime {
fn sample_service_time(&mut self, request: &RequestContext) -> Duration {
let size_time = self.time_per_byte * request.body_size as u32;
let total_time = self.base_time + size_time;
std::cmp::min(total_time, self.max_time)
}
fn expected_service_time(&self, request: &RequestContext) -> Duration {
let size_time = self.time_per_byte * request.body_size as u32;
let total_time = self.base_time + size_time;
std::cmp::min(total_time, self.max_time)
}
}
pub struct EndpointBasedServiceTime {
endpoint_distributions: HashMap<String, Box<dyn ServiceTimeDistribution>>,
default_distribution: Box<dyn ServiceTimeDistribution>,
}
impl EndpointBasedServiceTime {
pub fn new(default_distribution: Box<dyn ServiceTimeDistribution>) -> Self {
Self {
endpoint_distributions: HashMap::new(),
default_distribution,
}
}
pub fn add_endpoint(
&mut self,
endpoint: String,
distribution: Box<dyn ServiceTimeDistribution>,
) {
self.endpoint_distributions.insert(endpoint, distribution);
}
}
impl ServiceTimeDistribution for EndpointBasedServiceTime {
fn sample_service_time(&mut self, request: &RequestContext) -> Duration {
if let Some(dist) = self.endpoint_distributions.get_mut(&request.uri) {
dist.sample_service_time(request)
} else {
self.default_distribution.sample_service_time(request)
}
}
fn expected_service_time(&self, request: &RequestContext) -> Duration {
if let Some(dist) = self.endpoint_distributions.get(&request.uri) {
dist.expected_service_time(request)
} else {
self.default_distribution.expected_service_time(request)
}
}
}
pub trait RequestPredicate: Send {
fn matches(&self, request: &RequestContext) -> bool;
}
pub struct ServiceTimeRule {
pub condition: Box<dyn RequestPredicate>,
pub distribution: Box<dyn ServiceTimeDistribution>,
}
pub struct CompositeServiceTime {
rules: Vec<ServiceTimeRule>,
default_distribution: Box<dyn ServiceTimeDistribution>,
}
impl CompositeServiceTime {
pub fn new(default_distribution: Box<dyn ServiceTimeDistribution>) -> Self {
Self {
rules: Vec::new(),
default_distribution,
}
}
pub fn add_rule(
&mut self,
condition: Box<dyn RequestPredicate>,
distribution: Box<dyn ServiceTimeDistribution>,
) {
self.rules.push(ServiceTimeRule {
condition,
distribution,
});
}
}
impl ServiceTimeDistribution for CompositeServiceTime {
fn sample_service_time(&mut self, request: &RequestContext) -> Duration {
for rule in &mut self.rules {
if rule.condition.matches(request) {
return rule.distribution.sample_service_time(request);
}
}
self.default_distribution.sample_service_time(request)
}
fn expected_service_time(&self, request: &RequestContext) -> Duration {
for rule in &self.rules {
if rule.condition.matches(request) {
return rule.distribution.expected_service_time(request);
}
}
self.default_distribution.expected_service_time(request)
}
}
pub struct MethodPredicate(pub String);
impl RequestPredicate for MethodPredicate {
fn matches(&self, request: &RequestContext) -> bool {
request.method == self.0
}
}
pub struct UriPrefixPredicate(pub String);
impl RequestPredicate for UriPrefixPredicate {
fn matches(&self, request: &RequestContext) -> bool {
request.uri.starts_with(&self.0)
}
}
pub struct UriExactPredicate(pub String);
impl RequestPredicate for UriExactPredicate {
fn matches(&self, request: &RequestContext) -> bool {
request.uri == self.0
}
}
pub struct BodySizePredicate(pub std::ops::Range<usize>);
impl RequestPredicate for BodySizePredicate {
fn matches(&self, request: &RequestContext) -> bool {
self.0.contains(&request.body_size)
}
}
pub struct HeaderPredicate {
pub header_name: String,
pub header_value: String,
}
impl RequestPredicate for HeaderPredicate {
fn matches(&self, request: &RequestContext) -> bool {
request
.headers
.get(&self.header_name)
.map(|value| value == &self.header_value)
.unwrap_or(false)
}
}
pub struct AndPredicate(pub Vec<Box<dyn RequestPredicate>>);
impl RequestPredicate for AndPredicate {
fn matches(&self, request: &RequestContext) -> bool {
self.0.iter().all(|predicate| predicate.matches(request))
}
}
pub struct OrPredicate(pub Vec<Box<dyn RequestPredicate>>);
impl RequestPredicate for OrPredicate {
fn matches(&self, request: &RequestContext) -> bool {
self.0.iter().any(|predicate| predicate.matches(request))
}
}