/ thirdparty / hyperfine / src / export / markup.rs
markup.rs
  1  use crate::benchmark::relative_speed::BenchmarkResultWithRelativeSpeed;
  2  use crate::benchmark::{benchmark_result::BenchmarkResult, relative_speed};
  3  use crate::options::SortOrder;
  4  use crate::output::format::format_duration_value;
  5  use crate::util::units::Unit;
  6  
  7  use super::Exporter;
  8  use anyhow::Result;
  9  
 10  pub enum Alignment {
 11      Left,
 12      Right,
 13  }
 14  
 15  pub trait MarkupExporter {
 16      fn table_results(&self, entries: &[BenchmarkResultWithRelativeSpeed], unit: Unit) -> String {
 17          // prepare table header strings
 18          let notation = format!("[{}]", unit.short_name());
 19  
 20          // prepare table cells alignment
 21          let cells_alignment = [
 22              Alignment::Left,
 23              Alignment::Right,
 24              Alignment::Right,
 25              Alignment::Right,
 26              Alignment::Right,
 27          ];
 28  
 29          // emit table header format
 30          let mut table = self.table_header(&cells_alignment);
 31  
 32          // emit table header data
 33          table.push_str(&self.table_row(&[
 34              "Command",
 35              &format!("Mean {notation}"),
 36              &format!("Min {notation}"),
 37              &format!("Max {notation}"),
 38              "Relative",
 39          ]));
 40  
 41          // emit horizontal line
 42          table.push_str(&self.table_divider(&cells_alignment));
 43  
 44          for entry in entries {
 45              let measurement = &entry.result;
 46              // prepare data row strings
 47              let cmd_str = measurement
 48                  .command_with_unused_parameters
 49                  .replace('|', "\\|");
 50              let mean_str = format_duration_value(measurement.mean, Some(unit)).0;
 51              let stddev_str = if let Some(stddev) = measurement.stddev {
 52                  format!(" ± {}", format_duration_value(stddev, Some(unit)).0)
 53              } else {
 54                  "".into()
 55              };
 56              let min_str = format_duration_value(measurement.min, Some(unit)).0;
 57              let max_str = format_duration_value(measurement.max, Some(unit)).0;
 58              let rel_str = format!("{:.2}", entry.relative_speed);
 59              let rel_stddev_str = if entry.is_reference {
 60                  "".into()
 61              } else if let Some(stddev) = entry.relative_speed_stddev {
 62                  format!(" ± {stddev:.2}")
 63              } else {
 64                  "".into()
 65              };
 66  
 67              // prepare table row entries
 68              table.push_str(&self.table_row(&[
 69                  &self.command(&cmd_str),
 70                  &format!("{mean_str}{stddev_str}"),
 71                  &min_str,
 72                  &max_str,
 73                  &format!("{rel_str}{rel_stddev_str}"),
 74              ]))
 75          }
 76  
 77          // emit table footer format
 78          table.push_str(&self.table_footer(&cells_alignment));
 79  
 80          table
 81      }
 82  
 83      fn table_row(&self, cells: &[&str]) -> String;
 84  
 85      fn table_divider(&self, cell_aligmnents: &[Alignment]) -> String;
 86  
 87      fn table_header(&self, _cell_aligmnents: &[Alignment]) -> String {
 88          "".to_string()
 89      }
 90  
 91      fn table_footer(&self, _cell_aligmnents: &[Alignment]) -> String {
 92          "".to_string()
 93      }
 94  
 95      fn command(&self, size: &str) -> String;
 96  }
 97  
 98  fn determine_unit_from_results(results: &[BenchmarkResult]) -> Unit {
 99      if let Some(first_result) = results.first() {
100          // Use the first BenchmarkResult entry to determine the unit for all entries.
101          format_duration_value(first_result.mean, None).1
102      } else {
103          // Default to `Second`.
104          Unit::Second
105      }
106  }
107  
108  impl<T: MarkupExporter> Exporter for T {
109      fn serialize(
110          &self,
111          results: &[BenchmarkResult],
112          unit: Option<Unit>,
113          sort_order: SortOrder,
114      ) -> Result<Vec<u8>> {
115          let unit = unit.unwrap_or_else(|| determine_unit_from_results(results));
116          let entries = relative_speed::compute(results, sort_order);
117  
118          let table = self.table_results(&entries, unit);
119          Ok(table.as_bytes().to_vec())
120      }
121  }