From 01bc12af67cf12c8a4ac6efd44d0669692acea3b Mon Sep 17 00:00:00 2001 From: Ritvik Gupta Date: Thu, 28 Aug 2025 06:52:33 +0530 Subject: [PATCH] feat: add unified '--export' flag to export multiple formats Adds a unified '--export ' flag that determines the export format from the file extension (.json, .csv, .md, .adoc, .org). Closes #691 The flag can be specified multiple times to export results to different files and formats simultaneously. Defaults to 'JSON' if the extension isn't supported. For example: `hyperfine "ps aux" --export output.{json, csv, md, adoc, org}` `hyperfine "ps aux" --export output.json --export output.md` Signed-off-by: Ritvik Gupta --- src/cli.rs | 24 ++++++++++++++++++++++++ src/export/mod.rs | 27 +++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/src/cli.rs b/src/cli.rs index b12f6d34c..9e034096d 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -279,6 +279,30 @@ fn build_command() -> Command { If the option is not given, the time unit is determined automatically. \ This option affects the standard output as well as all export formats except for CSV and JSON."), ) + .arg( + Arg::new("export") + .long("export") + .action(ArgAction::Append) + .value_name("FILE") + .value_hint(ValueHint::FilePath) + .num_args(0..=1) + .default_missing_value("export.json") + .help( + "Export timing results to the given FILE. + \n\ + Supported formats:\n\ + .json Includes summary and individual run timings (in seconds).\n\ + .csv Summary only (in seconds).\n\ + .md Markdown table.\n\ + .adoc AsciiDoc table.\n\ + .org Org-mode table.\n\ + \n\ + Notes:\n\ + - Repeat --export to generate multiple formats.\n\ + - Except for JSON and CSV, the output time unit can be changed using '--time-unit' option.\n\ + - Defaults to JSON for unsupported or missing file extensions." + ), + ) .arg( Arg::new("export-asciidoc") .long("export-asciidoc") diff --git a/src/export/mod.rs b/src/export/mod.rs index 3947a2e50..7abcf7e0d 100644 --- a/src/export/mod.rs +++ b/src/export/mod.rs @@ -1,5 +1,7 @@ +use std::ffi::OsStr; use std::fs::{File, OpenOptions}; use std::io::Write; +use std::path::Path; mod asciidoc; mod csv; @@ -83,6 +85,14 @@ impl ExportManager { time_unit, sort_order, }; + + if let Some(args) = matches.get_many::("export") { + for filename in args { + let export_type = get_export_type_from_filename(filename); + export_manager.add_exporter(export_type, filename)?; + } + } + { let mut add_exporter = |flag, exporttype| -> Result<()> { if let Some(filename) = matches.get_one::(flag) { @@ -96,6 +106,7 @@ impl ExportManager { add_exporter("export-markdown", ExportType::Markdown)?; add_exporter("export-orgmode", ExportType::Orgmode)?; } + Ok(export_manager) } @@ -160,3 +171,19 @@ fn write_to_file(filename: &str, content: &[u8]) -> Result<()> { file.write_all(content) .with_context(|| format!("Failed to export results to '{filename}'")) } + +/// Determine the export-type from the file extension. Defaults to JSON. +fn get_export_type_from_filename(filename: &str) -> ExportType { + match Path::new(filename) + .extension() + .and_then(OsStr::to_str) + .map(|s| s.to_ascii_lowercase()) + .as_deref() + { + Some("adoc" | "asciidoc") => ExportType::Asciidoc, + Some("csv") => ExportType::Csv, + Some("md") => ExportType::Markdown, + Some("org") => ExportType::Orgmode, + Some("json") | _ => ExportType::Json, + } +}