mirror of
https://github.com/sharkdp/hyperfine.git
synced 2026-11-01 08:50:19 +00:00
Add markdown support.
Don't create Strings to convert to bytes later, just serialize directly to bytes in the first place
This commit is contained in:
1 parent
df1500dc60
commit
8b933dc64d
5 files changed
+67
-25
No files matched your search
@@ -3,10 +3,11 @@ use super::{ExportEntry, Exporter};
|
||||
use csv::WriterBuilder;
|
||||
use std::io::{Error, ErrorKind, Result};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct CsvExporter {}
|
||||
|
||||
impl Exporter for CsvExporter {
|
||||
fn serialize(&self, results: &Vec<ExportEntry>) -> Result<String> {
|
||||
fn serialize(&self, results: &Vec<ExportEntry>) -> Result<Vec<u8>> {
|
||||
let mut writer = WriterBuilder::new().from_writer(vec![]);
|
||||
for res in results {
|
||||
writer.serialize(res)?;
|
||||
@@ -14,14 +15,6 @@ impl Exporter for CsvExporter {
|
||||
|
||||
writer
|
||||
.into_inner()
|
||||
.ok()
|
||||
.and_then(|bytes| String::from_utf8(bytes).ok())
|
||||
.ok_or(Error::new(ErrorKind::Other, "Error serializing to CSV"))
|
||||
}
|
||||
}
|
||||
|
||||
impl CsvExporter {
|
||||
pub fn new() -> Self {
|
||||
CsvExporter {}
|
||||
.map_err(|e| Error::new(ErrorKind::Other, e))
|
||||
}
|
||||
}
|
||||
@@ -2,23 +2,18 @@ use super::{ExportEntry, Exporter};
|
||||
|
||||
use std::io::{Error, ErrorKind, Result};
|
||||
|
||||
use serde_json::to_string_pretty;
|
||||
use serde_json::to_vec_pretty;
|
||||
|
||||
#[derive(Serialize, Debug)]
|
||||
struct HyperfineSummary<'a> {
|
||||
results: &'a Vec<ExportEntry>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct JsonExporter {}
|
||||
|
||||
impl Exporter for JsonExporter {
|
||||
fn serialize(&self, results: &Vec<ExportEntry>) -> Result<String> {
|
||||
to_string_pretty(&HyperfineSummary { results }).map_err(|e| Error::new(ErrorKind::Other, e))
|
||||
}
|
||||
}
|
||||
|
||||
impl JsonExporter {
|
||||
pub fn new() -> Self {
|
||||
JsonExporter {}
|
||||
fn serialize(&self, results: &Vec<ExportEntry>) -> Result<Vec<u8>> {
|
||||
to_vec_pretty(&HyperfineSummary { results }).map_err(|e| Error::new(ErrorKind::Other, e))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
use super::{ExportEntry, Exporter};
|
||||
|
||||
use std::io::Result;
|
||||
|
||||
const MULTIPLIER: f64 = 1e3;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct MarkdownExporter {}
|
||||
|
||||
impl Exporter for MarkdownExporter {
|
||||
fn serialize(&self, results: &Vec<ExportEntry>) -> Result<Vec<u8>> {
|
||||
let mut destination = start_table();
|
||||
|
||||
for result in results {
|
||||
add_table_row(&mut destination, result);
|
||||
}
|
||||
|
||||
Ok(destination)
|
||||
}
|
||||
}
|
||||
|
||||
fn start_table() -> Vec<u8> {
|
||||
"| Benchmark | Mean [ms] | Min. [ms] | Max. [ms] |\n|----|----|----|----|\n"
|
||||
.bytes()
|
||||
.collect()
|
||||
}
|
||||
fn add_table_row(dest: &mut Vec<u8>, entry: &ExportEntry) {
|
||||
dest.extend(
|
||||
format!(
|
||||
"| {} | {:.1} ± {:.1} | {:.1} | {:.1} |\n",
|
||||
entry.command.replace("|", "\\|"),
|
||||
entry.mean * MULTIPLIER,
|
||||
entry.stddev * MULTIPLIER,
|
||||
entry.min * MULTIPLIER,
|
||||
entry.max * MULTIPLIER
|
||||
).as_bytes(),
|
||||
);
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
mod csv;
|
||||
mod json;
|
||||
mod markdown;
|
||||
|
||||
use self::csv::CsvExporter;
|
||||
use self::json::JsonExporter;
|
||||
use self::markdown::MarkdownExporter;
|
||||
|
||||
use std::io::{Result, Write};
|
||||
use std::fs::File;
|
||||
@@ -65,12 +67,15 @@ pub enum ExportType {
|
||||
|
||||
/// JSON format
|
||||
Json,
|
||||
|
||||
/// Markdown table
|
||||
Markdown,
|
||||
}
|
||||
|
||||
/// Interface for different exporters.
|
||||
trait Exporter {
|
||||
/// Export the given entries in the serialized form.
|
||||
fn serialize(&self, results: &Vec<ExportEntry>) -> Result<String>;
|
||||
fn serialize(&self, results: &Vec<ExportEntry>) -> Result<Vec<u8>>;
|
||||
}
|
||||
|
||||
struct ExporterWithFilename {
|
||||
@@ -94,8 +99,9 @@ impl ExportManager {
|
||||
/// Add an additional exporter to the ExportManager
|
||||
pub fn add_exporter(&mut self, export_type: ExportType, filename: &str) {
|
||||
let exporter: Box<Exporter> = match export_type {
|
||||
ExportType::Csv => Box::new(CsvExporter::new()),
|
||||
ExportType::Json => Box::new(JsonExporter::new()),
|
||||
ExportType::Csv => Box::new(CsvExporter::default()),
|
||||
ExportType::Json => Box::new(JsonExporter::default()),
|
||||
ExportType::Markdown => Box::new(MarkdownExporter::default()),
|
||||
};
|
||||
self.exporters.push(ExporterWithFilename {
|
||||
exporter,
|
||||
@@ -107,14 +113,14 @@ impl ExportManager {
|
||||
pub fn write_results(&self, results: Vec<ExportEntry>) -> Result<()> {
|
||||
for e in &self.exporters {
|
||||
let file_content = e.exporter.serialize(&results)?;
|
||||
write_to_file(&e.filename, file_content)?;
|
||||
write_to_file(&e.filename, &file_content)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Write the given content to a file with the specified name
|
||||
fn write_to_file(filename: &String, content: String) -> Result<()> {
|
||||
fn write_to_file(filename: &String, content: &Vec<u8>) -> Result<()> {
|
||||
let mut file = File::create(filename)?;
|
||||
file.write_all(content.as_bytes())
|
||||
file.write_all(content)
|
||||
}
|
||||
+10
@@ -152,6 +152,13 @@ fn main() {
|
||||
.value_name("FILE")
|
||||
.help("Export the timing results as JSON to the given FILE."),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("export-md")
|
||||
.long("export-md")
|
||||
.takes_value(true)
|
||||
.value_name("FILE")
|
||||
.help("Export the timing results to a markdown table in the given file"),
|
||||
)
|
||||
.help_message("Print this help message.")
|
||||
.version_message("Show version information.")
|
||||
.get_matches();
|
||||
@@ -188,6 +195,9 @@ fn main() {
|
||||
if let Some(filename) = matches.value_of("export-csv") {
|
||||
export_manager.add_exporter(ExportType::Csv, filename);
|
||||
}
|
||||
if let Some(filename) = matches.value_of("export-md") {
|
||||
export_manager.add_exporter(ExportType::Markdown, filename);
|
||||
}
|
||||
|
||||
// We default Windows to NoColor if full had been specified.
|
||||
if cfg!(windows) && options.output_style == OutputStyleOption::Full {
|
||||
|
||||
Reference in new issue
Block a user