grpcpulse 0.1.0

Benchmark and compare gRPC endpoints side by side — latency, throughput, and stream lag
Documentation
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}");
}