use anyhow::Result;
use std::io::{BufRead, Write};
use std::time::Duration;
use crate::models::Result as AttackResult;
use crate::utils::{get_reader, get_writer};
pub async fn run(
output: String,
threshold: usize,
title: String,
) -> Result<()> {
let reader = get_reader("stdin")?;
let mut writer = get_writer(&output)?;
generate_plot(reader, &mut writer, threshold, &title)?;
Ok(())
}
fn generate_plot<R: BufRead, W: Write>(
reader: R,
writer: &mut W,
threshold: usize,
title: &str,
) -> Result<()> {
let mut results: Vec<AttackResult> = reader
.lines()
.filter_map(|line| {
let line = line.ok()?;
serde_json::from_str(&line).ok()
})
.collect();
results.sort_by(|a, b| a.timestamp.cmp(&b.timestamp));
if results.len() > threshold {
let factor = results.len() / threshold;
results = results
.into_iter()
.enumerate()
.filter(|(i, _)| i % factor == 0)
.map(|(_, r)| r)
.collect();
}
let timestamps: Vec<f64> = results
.iter()
.map(|r| r.timestamp.timestamp_millis() as f64 / 1000.0)
.collect();
let latencies: Vec<f64> = results
.iter()
.map(|r| r.latency.as_secs_f64() * 1000.0) .collect();
let status_codes: Vec<u16> = results
.iter()
.map(|r| r.status_code)
.collect();
let html = format!(
r#"<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>{title}</title>
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
<style>
body {{ font-family: Arial, sans-serif; margin: 20px; }}
.plot {{ width: 100%; height: 500px; }}
h1 {{ color: #333; }}
</style>
</head>
<body>
<h1>{title}</h1>
<div id="latency-plot" class="plot"></div>
<div id="status-plot" class="plot"></div>
<script>
// Latency plot
var latencyData = {{
x: {timestamps:?},
y: {latencies:?},
type: 'scatter',
mode: 'lines',
name: 'Latency (ms)'
}};
var latencyLayout = {{
title: 'Request Latencies',
xaxis: {{ title: 'Time (s)' }},
yaxis: {{ title: 'Latency (ms)' }}
}};
Plotly.newPlot('latency-plot', [latencyData], latencyLayout);
// Status code plot
var statusData = {{
x: {timestamps:?},
y: {status_codes:?},
type: 'scatter',
mode: 'markers',
marker: {{ size: 5 }},
name: 'Status Codes'
}};
var statusLayout = {{
title: 'Response Status Codes',
xaxis: {{ title: 'Time (s)' }},
yaxis: {{ title: 'Status Code' }}
}};
Plotly.newPlot('status-plot', [statusData], statusLayout);
</script>
</body>
</html>"#,
title = title,
timestamps = timestamps,
latencies = latencies,
status_codes = status_codes
);
write!(writer, "{}", html)?;
Ok(())
}