strest 0.1.10

Blazing-fast async HTTP load tester in Rust - lock-free design, real-time stats, distributed runs, and optional chart exports for high-load API testing.
Documentation
use std::time::Duration;

#[derive(Debug, Clone)]
pub struct ReplayUi {
    pub playing: bool,
    pub window_start_ms: u64,
    pub window_end_ms: u64,
    pub cursor_ms: u64,
    pub snapshot_start_ms: Option<u64>,
    pub snapshot_end_ms: Option<u64>,
}

#[derive(Debug, Clone, Default)]
pub struct StatusCounts {
    pub status_2xx: u64,
    pub status_3xx: u64,
    pub status_4xx: u64,
    pub status_5xx: u64,
    pub status_other: u64,
}

#[derive(Debug, Clone)]
pub struct DataUsage {
    pub total_bytes: u128,
    pub bytes_per_sec: u64,
    pub series: Vec<(u64, u64)>,
}

#[derive(Debug, Clone)]
pub struct UiData {
    pub elapsed_time: Duration,
    pub target_duration: Duration,
    pub current_requests: u64,
    pub successful_requests: u64,
    pub timeout_requests: u64,
    pub transport_errors: u64,
    pub non_expected_status: u64,
    pub in_flight_ops: u64,
    pub ui_window_ms: u64,
    pub no_color: bool,
    pub latencies: Vec<(u64, u64)>,
    pub rps_series: Vec<(u64, u64)>,
    pub status_counts: Option<StatusCounts>,
    pub data_usage: Option<DataUsage>,
    pub p50: u64,
    pub p90: u64,
    pub p99: u64,
    pub p50_ok: u64,
    pub p90_ok: u64,
    pub p99_ok: u64,
    pub rps: u64,
    pub rpm: u64,
    pub replay: Option<ReplayUi>,
    pub compare: Option<CompareOverlay>,
}

#[derive(Debug, Clone)]
pub struct CompareOverlay {
    pub label: String,
    pub current_requests: u64,
    pub successful_requests: u64,
    pub timeout_requests: u64,
    pub transport_errors: u64,
    pub non_expected_status: u64,
    pub in_flight_ops: u64,
    pub latencies: Vec<(u64, u64)>,
    pub rps_series: Vec<(u64, u64)>,
    pub data_usage: Option<DataUsage>,
    pub p50: u64,
    pub p90: u64,
    pub p99: u64,
    pub p50_ok: u64,
    pub p90_ok: u64,
    pub p99_ok: u64,
    pub rps: u64,
    pub rpm: u64,
}

impl CompareOverlay {
    #[must_use]
    pub fn from_ui(label: String, data: &UiData) -> Self {
        Self {
            label,
            current_requests: data.current_requests,
            successful_requests: data.successful_requests,
            timeout_requests: data.timeout_requests,
            transport_errors: data.transport_errors,
            non_expected_status: data.non_expected_status,
            in_flight_ops: data.in_flight_ops,
            latencies: data.latencies.clone(),
            rps_series: data.rps_series.clone(),
            data_usage: data.data_usage.clone(),
            p50: data.p50,
            p90: data.p90,
            p99: data.p99,
            p50_ok: data.p50_ok,
            p90_ok: data.p90_ok,
            p99_ok: data.p99_ok,
            rps: data.rps,
            rpm: data.rpm,
        }
    }
}

#[derive(Clone)]
pub struct UiRenderData {
    pub elapsed_time: Duration,
    pub target_duration: Duration,
    pub current_request: u64,
    pub successful_requests: u64,
    pub timeout_requests: u64,
    pub transport_errors: u64,
    pub non_expected_status: u64,
    pub in_flight_ops: u64,
    pub ui_window_ms: u64,
    pub no_color: bool,
    pub latencies: Vec<(u64, u64)>,
    pub rps_series: Vec<(u64, u64)>,
    pub status_counts: Option<StatusCounts>,
    pub data_usage: Option<DataUsage>,
    pub p50: u64,
    pub p90: u64,
    pub p99: u64,
    pub p50_ok: u64,
    pub p90_ok: u64,
    pub p99_ok: u64,
    pub rps: u64,
    pub rpm: u64,
    pub replay: Option<ReplayUi>,
    pub compare: Option<CompareOverlay>,
}

impl Default for UiData {
    fn default() -> Self {
        Self {
            elapsed_time: Duration::from_secs(0),
            target_duration: Duration::from_secs(0),
            current_requests: 0,
            successful_requests: 0,
            timeout_requests: 0,
            transport_errors: 0,
            non_expected_status: 0,
            in_flight_ops: 0,
            ui_window_ms: 10_000,
            no_color: false,
            latencies: Vec::new(),
            rps_series: Vec::new(),
            status_counts: None,
            data_usage: None,
            p50: 0,
            p90: 0,
            p99: 0,
            p50_ok: 0,
            p90_ok: 0,
            p99_ok: 0,
            rps: 0,
            rpm: 0,
            replay: None,
            compare: None,
        }
    }
}

impl From<&UiData> for UiRenderData {
    fn from(data: &UiData) -> Self {
        Self {
            elapsed_time: data.elapsed_time,
            target_duration: data.target_duration,
            current_request: data.current_requests,
            successful_requests: data.successful_requests,
            timeout_requests: data.timeout_requests,
            transport_errors: data.transport_errors,
            non_expected_status: data.non_expected_status,
            in_flight_ops: data.in_flight_ops,
            ui_window_ms: data.ui_window_ms,
            no_color: data.no_color,
            latencies: data.latencies.clone(),
            rps_series: data.rps_series.clone(),
            status_counts: data.status_counts.clone(),
            data_usage: data.data_usage.clone(),
            p50: data.p50,
            p90: data.p90,
            p99: data.p99,
            p50_ok: data.p50_ok,
            p90_ok: data.p90_ok,
            p99_ok: data.p99_ok,
            rps: data.rps,
            rpm: data.rpm,
            replay: data.replay.clone(),
            compare: data.compare.clone(),
        }
    }
}