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 ratatui::{
    layout::{Constraint, Direction, Layout, Rect},
    prelude::{Backend, Frame, text},
    text::Span,
    widgets::{Block, Borders, Paragraph, Wrap},
};

use crate::ui::model::UiRenderData;

use super::formatting::{format_count_compact, format_ms_as_tenths};
use super::progress::progress_bar_line;
use super::theme::{
    ACCENT_LOAD_RGB, ACCENT_REPLAY_RGB, ACCENT_SERIES_COMPARE_RGB, ACCENT_SERIES_PRIMARY_RGB,
    PANEL_MUTED_RGB, panel_block_style, panel_border_style, panel_title_style, rgb, style_color,
};

pub fn render_run_panel<B: Backend>(f: &mut Frame<'_, B>, data: &UiRenderData, run_chunk: Rect) {
    let run_block = Block::default()
        .title("Run")
        .borders(Borders::ALL)
        .style(panel_block_style(data.no_color))
        .border_style(panel_border_style(data.no_color))
        .title_style(panel_title_style(data.no_color));
    let run_inner = run_block.inner(run_chunk);
    f.render_widget(run_block, run_chunk);

    let run_chunks = Layout::default()
        .direction(Direction::Vertical)
        .constraints([Constraint::Length(1), Constraint::Min(0)])
        .split(run_inner);

    let target_ms = data.target_duration.as_millis();
    let elapsed_ms = data.elapsed_time.as_millis();
    let progress_label = if target_ms > 0 {
        format!(
            "{} / {}",
            format_ms_as_tenths(elapsed_ms),
            format_ms_as_tenths(target_ms)
        )
    } else {
        format!("{} / --", format_ms_as_tenths(elapsed_ms))
    };

    let run_details = data.replay.as_ref().map_or_else(
        || {
            let mut lines = vec![text::Line::from(vec![
                Span::from("In-flight: "),
                Span::styled(
                    format_count_compact(data.in_flight_ops),
                    style_color(data.no_color, rgb(ACCENT_LOAD_RGB)),
                ),
            ])];
            if let Some(compare) = data.compare.as_ref() {
                lines.push(text::Line::from(vec![
                    Span::from("Legend: "),
                    Span::styled(
                        "Current",
                        style_color(data.no_color, rgb(ACCENT_SERIES_PRIMARY_RGB)),
                    ),
                    Span::from(" vs "),
                    Span::styled(
                        compare.label.clone(),
                        style_color(data.no_color, rgb(ACCENT_SERIES_COMPARE_RGB)),
                    ),
                ]));
            }
            lines
        },
        |replay| {
            let status = if replay.playing { "playing" } else { "paused" };
            let snapshot_start = replay.snapshot_start_ms.map_or_else(
                || "-".to_owned(),
                |value| format_ms_as_tenths(u128::from(value)),
            );
            let snapshot_end = replay.snapshot_end_ms.map_or_else(
                || "-".to_owned(),
                |value| format_ms_as_tenths(u128::from(value)),
            );
            vec![
                text::Line::from(vec![
                    Span::from("Replay: "),
                    Span::styled(status, style_color(data.no_color, rgb(ACCENT_REPLAY_RGB))),
                    Span::from("  Cursor: "),
                    Span::styled(
                        format_ms_as_tenths(u128::from(replay.cursor_ms)),
                        style_color(data.no_color, rgb(PANEL_MUTED_RGB)),
                    ),
                ]),
                text::Line::from(vec![
                    Span::from("Window: "),
                    Span::styled(
                        format!(
                            "{} -> {}",
                            format_ms_as_tenths(u128::from(replay.window_start_ms)),
                            format_ms_as_tenths(u128::from(replay.window_end_ms))
                        ),
                        style_color(data.no_color, rgb(PANEL_MUTED_RGB)),
                    ),
                    Span::from("  Snap: "),
                    Span::styled(
                        format!("{snapshot_start} / {snapshot_end}"),
                        style_color(data.no_color, rgb(ACCENT_REPLAY_RGB)),
                    ),
                ]),
            ]
        },
    );
    let run_text = Paragraph::new(run_details)
        .style(panel_block_style(data.no_color))
        .wrap(Wrap { trim: true });

    if let Some(chunk) = run_chunks.first() {
        let progress = Paragraph::new(progress_bar_line(
            elapsed_ms,
            target_ms,
            chunk.width,
            data.no_color,
            &progress_label,
        ))
        .style(panel_block_style(data.no_color))
        .wrap(Wrap { trim: true });
        f.render_widget(progress, *chunk);
    }
    if let Some(chunk) = run_chunks.get(1) {
        f.render_widget(run_text, *chunk);
    }
}