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 plotters::prelude::*;

use crate::error::AppResult;
use crate::metrics::AggregatedMetricSample;

use super::util::bucket_last_value_u64;

pub fn plot_aggregated_latency_percentiles(
    samples: &[AggregatedMetricSample],
    base_path: &str,
) -> AppResult<()> {
    if samples.is_empty() {
        return Ok(());
    }
    let mut p50s = bucket_last_value_u64(samples, 1000, |sample| sample.p50_latency_ms);
    let mut p90s = bucket_last_value_u64(samples, 1000, |sample| sample.p90_latency_ms);
    let mut p99s = bucket_last_value_u64(samples, 1000, |sample| sample.p99_latency_ms);

    p50s.sort_by_key(|(x, _)| *x);
    p90s.sort_by_key(|(x, _)| *x);
    p99s.sort_by_key(|(x, _)| *x);

    let x_min = p50s.first().map(|(x, _)| *x).unwrap_or(0);
    let x_max = p50s.last().map(|(x, _)| x.saturating_add(1)).unwrap_or(1);

    let y_max_p50 = p50s
        .iter()
        .map(|(_, y)| *y)
        .max()
        .unwrap_or(1)
        .saturating_add(1);
    let y_max_p90 = p90s
        .iter()
        .map(|(_, y)| *y)
        .max()
        .unwrap_or(1)
        .saturating_add(1);
    let y_max_p99 = p99s
        .iter()
        .map(|(_, y)| *y)
        .max()
        .unwrap_or(1)
        .saturating_add(1);

    let draw_chart = |series: &[(u64, u64)],
                      title: &str,
                      color: RGBColor,
                      file_path: &str,
                      y_max: u64|
     -> AppResult<()> {
        let root = BitMapBackend::new(file_path, (1600, 600)).into_drawing_area();
        root.fill(&WHITE)?;

        let mut chart = ChartBuilder::on(&root)
            .caption(title, ("sans-serif", 30))
            .margin(10)
            .x_label_area_size(30)
            .y_label_area_size(50)
            .build_cartesian_2d(x_min..x_max, 0u64..y_max)?;

        chart
            .configure_mesh()
            .x_desc("Elapsed Time (s)")
            .y_desc("Latency (ms)")
            .draw()?;

        chart.draw_series(LineSeries::new(series.iter().copied(), &color))?;
        root.present()?;
        Ok(())
    };

    draw_chart(
        &p50s,
        "Latency P50",
        BLUE,
        &format!("{}_P50.png", base_path),
        y_max_p50,
    )?;
    draw_chart(
        &p90s,
        "Latency P90",
        GREEN,
        &format!("{}_P90.png", base_path),
        y_max_p90,
    )?;
    draw_chart(
        &p99s,
        "Latency P99",
        RED,
        &format!("{}_P99.png", base_path),
        y_max_p99,
    )?;

    Ok(())
}