diff --git a/src/datastore.rs b/src/datastore.rs index 2d44061..0b50e11 100644 --- a/src/datastore.rs +++ b/src/datastore.rs @@ -50,17 +50,26 @@ impl DataStore { } } - pub fn stats(&self) -> Vec { + pub fn stats(&self) -> Vec> { self.data .iter() .map(|data| { let mut hist = Histogram::new(); + let mut min = f64::INFINITY; + let mut max = 0_f64; for (_, val) in data.iter().filter(|v| v.1 != 0f64) { hist.increment(*val as u64).unwrap_or(()); + min = min.min(*val); + max = max.max(*val); } - hist + if let Ok(p95) = hist.percentile(95.0) { + if min != f64::INFINITY && max != 0_f64 { + return Ok((min, max, p95)); + } + } + Err("No data") }) .collect() } @@ -121,12 +130,8 @@ impl DataStore { } fn format_tick(&self, increment: f64, value: f64) -> String { - if increment >= 1.0 { - format!("{:.0}", value) - } else { - let precision: usize = increment.log10().abs().ceil() as usize; - format!("{:.precision$}", value) - } + let precision: usize = increment.log10().abs().ceil().max(1.0) as usize; + format!("{:.precision$}", value) } pub fn y_axis_labels(&self, bounds: [f64; 2], num_ticks: i32) -> Vec { diff --git a/src/ui.rs b/src/ui.rs index 0747812..d6561f7 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -13,63 +13,6 @@ pub fn draw_ui( ) { terminal .draw(|f| { - let chunks: Vec = Layout::default() - .direction(Direction::Vertical) - .margin(2) - .constraints( - iter::repeat(Constraint::Length(1)) - .take(args.cmds.len()) - .chain(iter::once(Constraint::Percentage(10))) - .collect::>(), - ) - .split(f.size()); - for (((cmd_id, cmd), stats), &style) in args - .cmds - .iter() - .enumerate() - .zip(data_store.stats()) - .zip(&data_store.styles) - { - let header_layout = Layout::default() - .direction(Direction::Horizontal) - .constraints( - [ - Constraint::Percentage(40), - Constraint::Percentage(15), - Constraint::Percentage(15), - Constraint::Percentage(15), - Constraint::Percentage(15), - ] - .as_ref(), - ) - .split(chunks[cmd_id]); - - f.render_widget( - Paragraph::new(format!("Running cmd: {}", cmd)).style(style), - header_layout[0], - ); - - f.render_widget( - Paragraph::new(format!("current {:?}", data_store.last(cmd_id) as u64)) - .style(style), - header_layout[1], - ); - - f.render_widget( - Paragraph::new(format!("min {:?}", stats.minimum().unwrap_or(0))).style(style), - header_layout[2], - ); - f.render_widget( - Paragraph::new(format!("max {:?}", stats.maximum().unwrap_or(0))).style(style), - header_layout[3], - ); - f.render_widget( - Paragraph::new(format!("p95 {:?}", stats.percentile(95.0).unwrap_or(0))) - .style(style), - header_layout[4], - ); - } - let datasets: Vec<_> = data_store .data .iter() @@ -114,6 +57,71 @@ pub fn draw_ui( ([min, max], num_ticks) }; + let increment = (y_axis_bounds[1] - y_axis_bounds[0]) / (y_axis_num_ticks - 1) as f64; + let precision: usize = increment.log10().abs().ceil().max(1.0) as usize + 1; + + // Top level layout + let chunks: Vec = Layout::default() + .direction(Direction::Vertical) + .margin(2) + .constraints( + iter::repeat(Constraint::Length(1)) + .take(args.cmds.len()) + .chain(iter::once(Constraint::Percentage(10))) + .collect::>(), + ) + .split(f.size()); + + // Header line for each command + for (((cmd_id, cmd), stats), &style) in args + .cmds + .iter() + .enumerate() + .zip(data_store.stats()) + .zip(&data_store.styles) + { + let header_layout = Layout::default() + .direction(Direction::Horizontal) + .constraints( + [ + Constraint::Percentage(40), + Constraint::Percentage(15), + Constraint::Percentage(15), + Constraint::Percentage(15), + Constraint::Percentage(15), + ] + .as_ref(), + ) + .split(chunks[cmd_id]); + + f.render_widget( + Paragraph::new(format!("Running cmd: {}", cmd)).style(style), + header_layout[0], + ); + + f.render_widget( + Paragraph::new(format!("current {:.precision$}", data_store.last(cmd_id))) + .style(style), + header_layout[1], + ); + + if let Ok((min, max, p95)) = stats { + f.render_widget( + Paragraph::new(format!("min {:.precision$}", min)).style(style), + header_layout[2], + ); + f.render_widget( + Paragraph::new(format!("max {:.precision$}", max)).style(style), + header_layout[3], + ); + f.render_widget( + Paragraph::new(format!("p95 {:.precision$}", p95)).style(style), + header_layout[4], + ); + } + } + + // Chart let chart = Chart::new(datasets) .block(Block::default().borders(Borders::NONE)) .x_axis(