use crate::error::ApiError;
use madhyamas_core::{
ConditionalResponse, MatchCondition, MockResponse, ProbabilisticResponse, ResponseConfig,
ThrottleProfile,
};
use validator::{Validate, ValidationError};
pub fn validate<T: Validate>(t: &T) -> Result<(), ApiError> {
t.validate()
.map_err(|errors| ApiError::bad_request(errors.to_string()))
}
pub fn validate_match_condition(condition: &MatchCondition) -> Result<(), ValidationError> {
match condition {
MatchCondition::UrlPattern { pattern }
| MatchCondition::PathPattern { pattern }
| MatchCondition::Host { pattern }
| MatchCondition::BodyPattern { pattern }
| MatchCondition::ContentType { pattern } => {
if pattern.trim().is_empty() {
return Err(ValidationError::new("url_pattern cannot be empty"));
}
}
MatchCondition::HeaderPattern { name, pattern } => {
if name.trim().is_empty() {
return Err(ValidationError::new("header name cannot be empty"));
}
if pattern.trim().is_empty() {
return Err(ValidationError::new("header pattern cannot be empty"));
}
}
MatchCondition::QueryParamPattern { name, pattern } => {
if name.trim().is_empty() {
return Err(ValidationError::new("query param name cannot be empty"));
}
if pattern.trim().is_empty() {
return Err(ValidationError::new("query param pattern cannot be empty"));
}
}
MatchCondition::Header { name, .. } | MatchCondition::QueryParam { name, .. } => {
if name.trim().is_empty() {
return Err(ValidationError::new("name cannot be empty"));
}
}
MatchCondition::And { conditions } | MatchCondition::Or { conditions } => {
for c in conditions {
validate_match_condition(c)?;
}
}
MatchCondition::Not { condition } => validate_match_condition(condition)?,
_ => {}
}
Ok(())
}
pub fn validate_mock_response(response: &MockResponse) -> Result<(), ValidationError> {
validate_status_code(response.status_code)
}
pub fn validate_response_config(config: &ResponseConfig) -> Result<(), ValidationError> {
match config {
ResponseConfig::Single { response } => validate_mock_response(response),
ResponseConfig::Sequence { responses, .. } => {
for r in responses {
validate_mock_response(r)?;
}
Ok(())
}
ResponseConfig::Conditional {
conditions,
default_response,
} => {
validate_mock_response(default_response)?;
for ConditionalResponse { response, .. } in conditions {
validate_mock_response(response)?;
}
Ok(())
}
ResponseConfig::Probabilistic { responses } => {
for ProbabilisticResponse { response, .. } in responses {
validate_mock_response(response)?;
}
Ok(())
}
}
}
pub fn validate_status_code(code: u16) -> Result<(), ValidationError> {
if !(100..=599).contains(&code) {
return Err(ValidationError::new(
"status_code must be between 100 and 599",
));
}
Ok(())
}
pub fn validate_throttle_profile(profile: &ThrottleProfile) -> Result<(), ValidationError> {
const MAX_LATENCY_MS: u64 = 60_000;
const MAX_BANDWIDTH_BPS: u64 = 1_000_000_000_000;
if profile.latency_ms > MAX_LATENCY_MS {
return Err(ValidationError::new(
"latency_ms must be between 0 and 60000",
));
}
if profile.jitter_ms > MAX_LATENCY_MS {
return Err(ValidationError::new(
"jitter_ms must be between 0 and 60000",
));
}
if profile.packet_loss_percent > 100 {
return Err(ValidationError::new(
"packet_loss_percent must be between 0 and 100",
));
}
if profile.download_bps > MAX_BANDWIDTH_BPS {
return Err(ValidationError::new(
"download_bps exceeds the maximum reasonable value (1 Tbps)",
));
}
if profile.upload_bps > MAX_BANDWIDTH_BPS {
return Err(ValidationError::new(
"upload_bps exceeds the maximum reasonable value (1 Tbps)",
));
}
Ok(())
}