From 574106bc0380e540c47f1e7f68b6f58269c953e1 Mon Sep 17 00:00:00 2001 From: Firegem Date: Sat, 21 Jun 2025 21:53:45 -0400 Subject: [PATCH] allow `update cells` to work on single records (#16018) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description The `update cells` command used to "work" on records by converting them into single-row tables in the past, but strengthened input type checking made it so that no longer worked. This commit introduces correct record -> record functionality. # User-Facing Changes Users can now pipe records into `update cells`. An example inspired by a conversation in the Discord: ```nushell > version | update cells { split row ', ' } -c [features, installed_plugins] ╭────────────────────┬──────────────────────────────────────────╮ │ version │ 0.105.2 │ │ major │ 0 │ │ minor │ 105 │ │ patch │ 2 │ │ branch │ update-cells-record │ │ commit_hash │ 4f7e9aac62653a5118dd783dd8fb6aba8c9d4212 │ │ build_os │ macos-x86_64 │ │ build_target │ x86_64-apple-darwin │ │ rust_version │ rustc 1.85.1 (4eb161250 2025-03-15) │ │ rust_channel │ 1.85.1-x86_64-apple-darwin │ │ cargo_version │ cargo 1.85.1 (d73d2caf9 2024-12-31) │ │ build_time │ 2025-06-21 12:02:06 -04:00 │ │ build_rust_channel │ debug │ │ allocator │ standard │ │ │ ╭───┬───────────────╮ │ │ features │ │ 0 │ default │ │ │ │ │ 1 │ plugin │ │ │ │ │ 2 │ rustls-tls │ │ │ │ │ 3 │ sqlite │ │ │ │ │ 4 │ trash-support │ │ │ │ ╰───┴───────────────╯ │ │ │ ╭───┬─────────────────╮ │ │ installed_plugins │ │ 0 │ formats 0.104.0 │ │ │ │ │ 1 │ polars 0.104.0 │ │ │ │ │ 2 │ query 0.104.0 │ │ │ │ │ 3 │ todotxt 0.3.0 │ │ │ │ ╰───┴─────────────────╯ │ ╰────────────────────┴──────────────────────────────────────────╯ ``` # Tests + Formatting 👍. Let me know if more tests besides the new example are needed. # After Submitting --- .../src/extra/filters/update_cells.rs | 80 ++++++++++++++----- 1 file changed, 59 insertions(+), 21 deletions(-) diff --git a/crates/nu-cmd-extra/src/extra/filters/update_cells.rs b/crates/nu-cmd-extra/src/extra/filters/update_cells.rs index 0b62ac4d6d..4a976c3a9b 100644 --- a/crates/nu-cmd-extra/src/extra/filters/update_cells.rs +++ b/crates/nu-cmd-extra/src/extra/filters/update_cells.rs @@ -12,7 +12,10 @@ impl Command for UpdateCells { fn signature(&self) -> Signature { Signature::build("update cells") - .input_output_types(vec![(Type::table(), Type::table())]) + .input_output_types(vec![ + (Type::table(), Type::table()), + (Type::record(), Type::record()), + ]) .required( "closure", SyntaxShape::Closure(Some(vec![SyntaxShape::Any])), @@ -77,6 +80,15 @@ impl Command for UpdateCells { "2021-11-18" => Value::test_string(""), })])), }, + Example { + example: r#"{a: 1, b: 2, c: 3} | update cells { $in + 10 }"#, + description: "Update each value in a record.", + result: Some(Value::test_record(record! { + "a" => Value::test_int(11), + "b" => Value::test_int(12), + "c" => Value::test_int(13), + })), + }, ] } @@ -85,7 +97,7 @@ impl Command for UpdateCells { engine_state: &EngineState, stack: &mut Stack, call: &Call, - input: PipelineData, + mut input: PipelineData, ) -> Result { let head = call.head; let closure: Closure = call.req(engine_state, stack, 0)?; @@ -102,14 +114,51 @@ impl Command for UpdateCells { let metadata = input.metadata(); - Ok(UpdateCellIterator { - iter: input.into_iter(), - closure: ClosureEval::new(engine_state, stack, closure), - columns, - span: head, + match input { + PipelineData::Value( + Value::Record { + ref mut val, + internal_span, + }, + .., + ) => { + let val = val.to_mut(); + update_record( + val, + &mut ClosureEval::new(engine_state, stack, closure), + internal_span, + columns.as_ref(), + ); + Ok(input) + } + _ => Ok(UpdateCellIterator { + iter: input.into_iter(), + closure: ClosureEval::new(engine_state, stack, closure), + columns, + span: head, + } + .into_pipeline_data(head, engine_state.signals().clone()) + .set_metadata(metadata)), + } + } +} + +fn update_record( + record: &mut Record, + closure: &mut ClosureEval, + span: Span, + cols: Option<&HashSet>, +) { + if let Some(columns) = cols { + for (col, val) in record.iter_mut() { + if columns.contains(col) { + *val = eval_value(closure, span, std::mem::take(val)); + } + } + } else { + for (_, val) in record.iter_mut() { + *val = eval_value(closure, span, std::mem::take(val)) } - .into_pipeline_data(head, engine_state.signals().clone()) - .set_metadata(metadata)) } } @@ -128,18 +177,7 @@ impl Iterator for UpdateCellIterator { let value = if let Value::Record { val, .. } = &mut value { let val = val.to_mut(); - if let Some(columns) = &self.columns { - for (col, val) in val.iter_mut() { - if columns.contains(col) { - *val = eval_value(&mut self.closure, self.span, std::mem::take(val)); - } - } - } else { - for (_, val) in val.iter_mut() { - *val = eval_value(&mut self.closure, self.span, std::mem::take(val)) - } - } - + update_record(val, &mut self.closure, self.span, self.columns.as_ref()); value } else { eval_value(&mut self.closure, self.span, value)