use std::collections::HashMap;
use std::fmt;
use std::path::Path;
pub mod endpoint_tester;
pub mod report_generator;
pub mod rfc_parser;
pub mod spec_validator;
#[cfg(test)]
mod tests;
#[derive(Debug, Clone, PartialEq)]
pub struct ComplianceRequirement {
pub spec_id: String,
pub section: String,
pub level: RequirementLevel,
pub description: String,
pub category: RequirementCategory,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum RequirementLevel {
Must,
MustNot,
Should,
ShouldNot,
May,
}
impl fmt::Display for RequirementLevel {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Must => write!(f, "MUST"),
Self::MustNot => write!(f, "MUST NOT"),
Self::Should => write!(f, "SHOULD"),
Self::ShouldNot => write!(f, "SHOULD NOT"),
Self::May => write!(f, "MAY"),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum RequirementCategory {
Transport,
FrameFormat,
TransportParameters,
ConnectionEstablishment,
NatTraversal,
AddressDiscovery,
ErrorHandling,
Security,
Performance,
}
#[derive(Debug, Clone)]
pub struct ComplianceResult {
pub requirement: ComplianceRequirement,
pub compliant: bool,
pub details: String,
pub evidence: Vec<Evidence>,
}
#[derive(Debug, Clone)]
pub enum Evidence {
TestResult {
test_name: String,
passed: bool,
output: String,
},
PacketCapture {
description: String,
packets: Vec<u8>,
},
CodeReference {
file: String,
line: usize,
snippet: String,
},
EndpointTest {
endpoint: String,
result: String,
},
}
pub struct ComplianceValidator {
requirements: Vec<ComplianceRequirement>,
validators: HashMap<String, Box<dyn SpecValidator>>,
test_endpoints: Vec<String>,
}
impl Default for ComplianceValidator {
fn default() -> Self {
Self::new()
}
}
impl ComplianceValidator {
pub fn new() -> Self {
Self {
requirements: Vec::new(),
validators: HashMap::new(),
test_endpoints: Vec::new(),
}
}
pub fn load_requirements(&mut self, rfc_path: &Path) -> Result<(), ValidationError> {
let parser = rfc_parser::RfcParser::new();
let requirements = parser.parse_file(rfc_path)?;
self.requirements.extend(requirements);
Ok(())
}
pub fn register_validator(&mut self, spec_id: String, validator: Box<dyn SpecValidator>) {
self.validators.insert(spec_id, validator);
}
pub fn add_test_endpoint(&mut self, endpoint: String) {
self.test_endpoints.push(endpoint);
}
pub fn validate_all(&self) -> ComplianceReport {
let mut results = Vec::new();
for requirement in &self.requirements {
if let Some(validator) = self.validators.get(&requirement.spec_id) {
let result = validator.validate(requirement);
results.push(result);
} else {
results.push(ComplianceResult {
requirement: requirement.clone(),
compliant: false,
details: format!("No validator registered for {}", requirement.spec_id),
evidence: vec![],
});
}
}
ComplianceReport::new(results)
}
pub async fn validate_endpoints(&self) -> EndpointValidationReport {
let mut tester = endpoint_tester::EndpointTester::new();
for endpoint in &self.test_endpoints {
tester.add_endpoint(endpoint.clone());
}
tester.test_all_endpoints().await
}
}
pub trait SpecValidator: Send + Sync {
fn validate(&self, requirement: &ComplianceRequirement) -> ComplianceResult;
fn spec_id(&self) -> &str;
}
#[derive(Debug)]
pub struct ComplianceReport {
pub results: Vec<ComplianceResult>,
pub summary: ComplianceSummary,
pub timestamp: std::time::SystemTime,
}
impl ComplianceReport {
fn new(results: Vec<ComplianceResult>) -> Self {
let summary = ComplianceSummary::from_results(&results);
Self {
results,
summary,
timestamp: std::time::SystemTime::now(),
}
}
pub fn to_html(&self) -> String {
report_generator::generate_html_report(self)
}
pub fn to_json(&self) -> serde_json::Value {
report_generator::generate_json_report(self)
}
}
#[derive(Debug)]
pub struct ComplianceSummary {
pub total_requirements: usize,
pub passed: usize,
pub failed: usize,
pub pass_rate_by_level: HashMap<RequirementLevel, f64>,
pub pass_rate_by_category: HashMap<RequirementCategory, f64>,
}
impl ComplianceSummary {
fn from_results(results: &[ComplianceResult]) -> Self {
let total_requirements = results.len();
let passed = results.iter().filter(|r| r.compliant).count();
let failed = total_requirements - passed;
let mut pass_rate_by_level = HashMap::new();
let mut pass_rate_by_category = HashMap::new();
for level in &[
RequirementLevel::Must,
RequirementLevel::MustNot,
RequirementLevel::Should,
RequirementLevel::ShouldNot,
RequirementLevel::May,
] {
let level_results: Vec<_> = results
.iter()
.filter(|r| &r.requirement.level == level)
.collect();
if !level_results.is_empty() {
let level_passed = level_results.iter().filter(|r| r.compliant).count();
let pass_rate = level_passed as f64 / level_results.len() as f64;
pass_rate_by_level.insert(level.clone(), pass_rate);
}
}
for category in &[
RequirementCategory::Transport,
RequirementCategory::FrameFormat,
RequirementCategory::TransportParameters,
RequirementCategory::ConnectionEstablishment,
RequirementCategory::NatTraversal,
RequirementCategory::AddressDiscovery,
RequirementCategory::ErrorHandling,
RequirementCategory::Security,
RequirementCategory::Performance,
] {
let category_results: Vec<_> = results
.iter()
.filter(|r| &r.requirement.category == category)
.collect();
if !category_results.is_empty() {
let category_passed = category_results.iter().filter(|r| r.compliant).count();
let pass_rate = category_passed as f64 / category_results.len() as f64;
pass_rate_by_category.insert(category.clone(), pass_rate);
}
}
Self {
total_requirements,
passed,
failed,
pass_rate_by_level,
pass_rate_by_category,
}
}
pub fn compliance_percentage(&self) -> f64 {
if self.total_requirements == 0 {
0.0
} else {
(self.passed as f64 / self.total_requirements as f64) * 100.0
}
}
pub fn must_requirements_met(&self) -> bool {
self.pass_rate_by_level
.get(&RequirementLevel::Must)
.map(|&rate| rate == 1.0)
.unwrap_or(true)
}
}
#[derive(Debug)]
pub struct EndpointValidationReport {
pub endpoint_results: HashMap<String, EndpointResult>,
pub success_rate: f64,
pub common_issues: Vec<String>,
}
#[derive(Debug)]
pub struct EndpointResult {
pub endpoint: String,
pub connected: bool,
pub quic_versions: Vec<u32>,
pub extensions: Vec<String>,
pub issues: Vec<String>,
}
#[derive(Debug)]
pub enum ValidationError {
RfcParseError(String),
SpecLoadError(String),
ValidationError(String),
IoError(std::io::Error),
}
impl fmt::Display for ValidationError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::RfcParseError(e) => write!(f, "RFC parse error: {e}"),
Self::SpecLoadError(e) => write!(f, "Specification load error: {e}"),
Self::ValidationError(e) => write!(f, "Validation error: {e}"),
Self::IoError(e) => write!(f, "IO error: {e}"),
}
}
}
impl std::error::Error for ValidationError {}
impl From<std::io::Error> for ValidationError {
fn from(err: std::io::Error) -> Self {
Self::IoError(err)
}
}