use std::time::Duration;
use comfy_table::{Table,Cell,Color,Attribute};
#[derive(Debug)]
pub struct EndpointResult{
pub name:String,
pub latencies:Vec<Duration>,
pub errors:u32,
pub total_requests:u32,
}
impl EndpointResult{
pub fn p50(&self)->Duration{
self.percentile(0.50)
}
pub fn p95(&self)->Duration{
self.percentile(0.95)
}
pub fn p99(&self)->Duration{
self.percentile(0.99)
}
pub fn max(&self) ->Duration{
self.latencies.iter().max().copied().unwrap_or_default()
}
pub fn error_rate(&self)->f64{
if self.total_requests==0{
return 0.0;
}
(self.errors as f64 /self.total_requests as f64)*100.0
}
pub fn percentile(&self,p:f64)-> Duration {
if self.latencies.is_empty(){
return Duration::ZERO;
}
let mut sorted =self.latencies.clone();
sorted.sort();
let index=((p*sorted.len() as f64).ceil() as usize).saturating_sub(1);
sorted[index]
}
}
pub fn print_table(results:&[EndpointResult]){
let mut table=Table::new();
table.set_header(vec!["Endpoint","p50","p90","p99","Max","Errors"]);
let best_p50=results.iter()
.filter(|r| !r.latencies.is_empty())
.min_by_key(|r| r.p50());
for result in results{
let is_best=best_p50.map(|b|b.name==result.name).unwrap_or(false);
let name_cell=if is_best{
Cell::new(&result.name)
.fg(Color::Green)
.add_attribute(Attribute::Bold)
}else{
Cell::new(&result.name)
};
table.add_row(vec![
name_cell,
Cell::new(format!("{:.2?}", result.p50())),
Cell::new(format!("{:.2?}", result.p95())),
Cell::new(format!("{:.2?}", result.p99())),
Cell::new(format!("{:.2?}", result.max())),
Cell::new(format!("{:.1?}", result.error_rate())),
]);
}
println!("{table}");
}