From 19157c2211c1f65f1c536248258f4b80d583523a Mon Sep 17 00:00:00 2001
From: Joel Natividad <1980690+jqnatividad@users.noreply.github.com>
Date: Sat, 15 Jun 2024 10:54:44 -0400
Subject: [PATCH] `excel`: add stdin support for xls, xlsx and ods formats
---
src/cmd/excel.rs | 31 +++++++++++++++++++++++++++++--
1 file changed, 29 insertions(+), 2 deletions(-)
diff --git a/src/cmd/excel.rs b/src/cmd/excel.rs
index 77f649805..6f90c7305 100644
--- a/src/cmd/excel.rs
+++ b/src/cmd/excel.rs
@@ -51,6 +51,10 @@ Usage:
qsv excel [options] []
qsv excel --help
+Excel argument:
+ The spreadsheet file to read. Use "-" to read from stdin.
+ Supported formats: xls, xlsx, xlsm, xlsb, ods.
+
Excel options:
-s, --sheet Name (case-insensitive) or zero-based index of sheet to export.
Negative indices start from the end (-1 = last sheet).
@@ -114,9 +118,10 @@ Common options:
-Q, --quiet Do not display export summary message.
"#;
-use std::{cmp, fmt::Write, path::PathBuf};
+use std::{cmp, fmt::Write, io::Read, path::PathBuf};
use calamine::{open_workbook, Data, Error, Range, Reader, SheetType, Sheets};
+use file_format::FileFormat;
use indicatif::HumanCount;
use log::info;
use rayon::prelude::{ParallelIterator, ParallelSlice};
@@ -262,7 +267,29 @@ impl RequestedRange {
pub fn run(argv: &[&str]) -> CliResult<()> {
let args: Args = util::get_args(USAGE, argv)?;
- let path = &args.arg_input;
+
+ // accept spreadsheets from stdin
+ let tmpdir = tempfile::tempdir()?;
+ let path_string = if args.arg_input == "-" {
+ let mut buffer = Vec::new();
+ std::io::stdin().read_to_end(&mut buffer)?;
+ let fmt = FileFormat::from_bytes(&buffer);
+ let spreadsheet_kind = match fmt {
+ FileFormat::OfficeOpenXmlSpreadsheet => "xlsx",
+ FileFormat::MicrosoftExcelSpreadsheet => "xls",
+ FileFormat::OpendocumentSpreadsheet => "ods",
+ _ => {
+ return fail_clierror!("Unsupported file format detected on stdin: {fmt:?}.");
+ },
+ };
+
+ let tmpfile = tmpdir.path().join(format!("stdin.{spreadsheet_kind}"));
+ std::fs::write(&tmpfile, &buffer)?;
+ tmpfile.to_string_lossy().to_string()
+ } else {
+ args.arg_input
+ };
+ let path = &path_string;
let sce = PathBuf::from(path);
let filename = sce