diff --git a/.changeset/fix-append-json-multirow.md b/.changeset/fix-append-json-multirow.md new file mode 100644 index 00000000..1e3f6de0 --- /dev/null +++ b/.changeset/fix-append-json-multirow.md @@ -0,0 +1,5 @@ +--- +"@googleworkspace/cli": patch +--- + +Fix `+append --json-values` multi-row bug by changing AppendConfig.values to Vec> diff --git a/src/helpers/sheets.rs b/src/helpers/sheets.rs index 5c2a9e90..b179c75c 100644 --- a/src/helpers/sheets.rs +++ b/src/helpers/sheets.rs @@ -102,7 +102,7 @@ TIPS: ) -> Pin> + Send + 'a>> { Box::pin(async move { if let Some(matches) = matches.subcommand_matches("+append") { - let config = parse_append_args(matches); + let config = parse_append_args(matches)?; let (params_str, body_str, scopes) = build_append_request(&config, doc)?; let scope_strs: Vec<&str> = scopes.iter().map(|s| s.as_str()).collect(); @@ -222,7 +222,7 @@ fn build_append_request( // This allows us to easily create nested objects without defining explicit structs // for every API request body. let body = json!({ - "values": [config.values] + "values": config.values }); // Map `&String` scope URLs to owned `String`s for the return value @@ -260,35 +260,41 @@ fn build_read_request( /// Configuration for appending values to a spreadsheet. /// /// Holds the parsed arguments for the `+append` subcommand. +#[derive(Debug)] pub struct AppendConfig { /// The ID of the spreadsheet to append to. pub spreadsheet_id: String, - /// The values to append, as a vector of strings. - pub values: Vec, + /// The values to append, as a 2D array of strings (one inner vec per row). + pub values: Vec>, } /// Parses arguments for the `+append` command. /// /// Splits the comma-separated `values` argument into a `Vec`. -pub fn parse_append_args(matches: &ArgMatches) -> AppendConfig { +pub fn parse_append_args(matches: &ArgMatches) -> Result { let values = if let Some(json_str) = matches.get_one::("json-values") { - // Parse JSON array of rows + // Try parsing as 2D array first, then fall back to 1D array if let Ok(parsed) = serde_json::from_str::>>(json_str) { - parsed.into_iter().flatten().collect() + parsed } else { - // Treat as single row JSON array - serde_json::from_str::>(json_str).unwrap_or_default() + serde_json::from_str::>(json_str) + .map(|row| vec![row]) + .map_err(|e| { + GwsError::Validation(format!( + "Invalid --json-values: {e}. Expected a JSON array of strings or array of arrays." + )) + })? } } else if let Some(values_str) = matches.get_one::("values") { - values_str.split(',').map(|s| s.to_string()).collect() + vec![values_str.split(',').map(|s| s.to_string()).collect()] } else { Vec::new() }; - AppendConfig { + Ok(AppendConfig { spreadsheet_id: matches.get_one::("spreadsheet").unwrap().clone(), values, - } + }) } /// Configuration for reading values from a spreadsheet. @@ -365,14 +371,18 @@ mod tests { let doc = make_mock_doc(); let config = AppendConfig { spreadsheet_id: "123".to_string(), - values: vec!["a".to_string(), "b".to_string(), "c".to_string()], + values: vec![vec!["a".to_string(), "b".to_string(), "c".to_string()]], }; let (params, body, scopes) = build_append_request(&config, &doc).unwrap(); assert!(params.contains("123")); assert!(params.contains("USER_ENTERED")); - assert!(body.contains("a")); - assert!(body.contains("b")); + + let body_json: serde_json::Value = serde_json::from_str(&body).unwrap(); + assert_eq!( + body_json, + serde_json::json!({"values": [["a", "b", "c"]]}) + ); assert_eq!(scopes[0], "https://scope"); } @@ -393,9 +403,41 @@ mod tests { #[test] fn test_parse_append_args() { let matches = make_matches_append(&["test", "--spreadsheet", "123", "--values", "a,b,c"]); - let config = parse_append_args(&matches); + let config = parse_append_args(&matches).unwrap(); assert_eq!(config.spreadsheet_id, "123"); - assert_eq!(config.values, vec!["a", "b", "c"]); + assert_eq!(config.values, vec![vec!["a", "b", "c"]]); + } + + #[test] + fn test_parse_append_args_json_multirow() { + let matches = make_matches_append(&[ + "test", + "--spreadsheet", + "123", + "--json-values", + r#"[["Alice","100"],["Bob","200"]]"#, + ]); + let config = parse_append_args(&matches).unwrap(); + assert_eq!(config.spreadsheet_id, "123"); + assert_eq!( + config.values, + vec![vec!["Alice", "100"], vec!["Bob", "200"]] + ); + } + + #[test] + fn test_parse_append_args_json_malformed() { + let matches = make_matches_append(&[ + "test", + "--spreadsheet", + "123", + "--json-values", + "not valid json", + ]); + let result = parse_append_args(&matches); + assert!(result.is_err()); + let err_msg = result.unwrap_err().to_string(); + assert!(err_msg.contains("Invalid --json-values")); } #[test]