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:
Steve Pentland authored and David Peter committed 2018-03-20 23:19:06 +01:00
1 parent df1500dc60
commit 8b933dc64d
5 files changed
+67 -25

No files matched your search

+3 -10
View File
@@ -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))
}
}
+4 -9
View File
@@ -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))
}
}
+38
View File
@@ -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(),
);
}
+12 -6
View File
@@ -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
View File
@@ -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 {