From 923d89ad59d5b7e77a77d014687dbe7126c7e5cb Mon Sep 17 00:00:00 2001 From: Dan Rassi <129646+drassi@users.noreply.github.com> Date: Fri, 6 Mar 2026 16:53:17 -0500 Subject: [PATCH] fix: handle array-of-arrays in CSV formatter The CSV formatter assumed all items were JSON objects when collecting column names. APIs that return arrays of arrays (e.g. Sheets values) produced empty newlines instead of data. Add an early return path for non-object arrays that emits each inner array's elements as CSV cells directly, mirroring the table formatter's existing "array of non-objects" handling. Fixes #283 Co-Authored-By: Claude Opus 4.6 --- .changeset/fix-csv-array-of-arrays.md | 5 ++++ src/formatter.rs | 34 +++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 .changeset/fix-csv-array-of-arrays.md diff --git a/.changeset/fix-csv-array-of-arrays.md b/.changeset/fix-csv-array-of-arrays.md new file mode 100644 index 00000000..b0682ed3 --- /dev/null +++ b/.changeset/fix-csv-array-of-arrays.md @@ -0,0 +1,5 @@ +--- +"@googleworkspace/cli": patch +--- + +Fix `--format csv` for array-of-arrays responses (e.g. Sheets values API) diff --git a/src/formatter.rs b/src/formatter.rs index 1b069668..00f50340 100644 --- a/src/formatter.rs +++ b/src/formatter.rs @@ -356,6 +356,23 @@ fn format_csv_page(value: &Value, emit_header: bool) -> String { return String::new(); } + // Array of non-objects + if !arr.iter().any(|v| v.is_object()) { + let mut output = String::new(); + for item in arr { + if let Value::Array(inner) = item { + let cells: Vec = inner + .iter() + .map(|v| csv_escape(&value_to_cell(v))) + .collect(); + let _ = writeln!(output, "{}", cells.join(",")); + } else { + let _ = writeln!(output, "{}", csv_escape(&value_to_cell(item))); + } + } + return output; + } + // Collect columns let mut columns: Vec = Vec::new(); for item in arr { @@ -564,6 +581,23 @@ mod tests { assert!(output.contains("2,world")); } + #[test] + fn test_format_csv_array_of_arrays() { + // Sheets API returns {"values": [["col1","col2"], ["a","b"]]} + let val = json!({ + "values": [ + ["Student Name", "Gender", "Class Level"], + ["Alexandra", "Female", "4. Senior"], + ["Andrew", "Male", "1. Freshman"] + ] + }); + let output = format_value(&val, &OutputFormat::Csv); + let lines: Vec<&str> = output.lines().collect(); + assert_eq!(lines[0], "Student Name,Gender,Class Level"); + assert_eq!(lines[1], "Alexandra,Female,4. Senior"); + assert_eq!(lines[2], "Andrew,Male,1. Freshman"); + } + #[test] fn test_format_csv_escape() { assert_eq!(csv_escape("simple"), "simple");