shohei 0.5.1

Infrastructure diagnostics library: DNS, DNSSEC, TLS certificate inspection, email security, DNS propagation, and MCP-integrated AI agent support
Documentation
//! Multi-protocol latency benchmarking.

use serde::{Deserialize, Serialize};
use crate::error::Result;
use crate::api::{check_dns, DnsCheckRequest, Transport};

/// Benchmark DNS query latency across multiple transports.
pub async fn benchmark_latency(req: &LatencyBenchRequest) -> Result<LatencyBenchResult> {
    let mut results = Vec::new();

    for bench_transport in &req.transports {
        let mut samples_ms = Vec::new();

        for _ in 0..req.rounds {
            let dns_req = DnsCheckRequest {
                domain: req.domain.clone(),
                record_types: vec![req.record_type.clone()],
                transport: bench_transport.transport.clone(),
                timeout_secs: req.timeout_secs,
                ..Default::default()
            };

            let sample = match check_dns(&dns_req).await {
                Ok(results) if !results.is_empty() => Some(results[0].duration_ms),
                _ => None,
            };
            samples_ms.push(sample);
        }

        let valid_samples: Vec<u64> = samples_ms.iter().filter_map(|s| *s).collect();
        let success_rate = if req.rounds > 0 {
            valid_samples.len() as f64 / req.rounds as f64
        } else {
            0.0
        };

        let (min_ms, max_ms, avg_ms) = if !valid_samples.is_empty() {
            let min = *valid_samples.iter().min().unwrap_or(&0);
            let max = *valid_samples.iter().max().unwrap_or(&0);
            let avg = valid_samples.iter().sum::<u64>() as f64 / valid_samples.len() as f64;
            (Some(min), Some(max), Some(avg))
        } else {
            (None, None, None)
        };

        results.push(TransportLatency {
            label: bench_transport.label.clone(),
            transport: bench_transport.transport.clone(),
            samples_ms,
            min_ms,
            max_ms,
            avg_ms,
            success_rate,
        });
    }

    Ok(LatencyBenchResult {
        domain: req.domain.clone(),
        record_type: req.record_type.clone(),
        results,
    })
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LatencyBenchRequest {
    pub domain: String,
    pub record_type: String,
    pub transports: Vec<BenchTransport>,
    #[serde(default = "default_rounds")]
    pub rounds: u32,
    #[serde(default = "default_timeout")]
    pub timeout_secs: u64,
}

fn default_rounds() -> u32 { 3 }
fn default_timeout() -> u64 { 5 }

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BenchTransport {
    pub transport: Transport,
    pub label: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LatencyBenchResult {
    pub domain: String,
    pub record_type: String,
    pub results: Vec<TransportLatency>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TransportLatency {
    pub label: String,
    pub transport: Transport,
    pub samples_ms: Vec<Option<u64>>,
    pub min_ms: Option<u64>,
    pub max_ms: Option<u64>,
    pub avg_ms: Option<f64>,
    pub success_rate: f64,
}