From f2e8c391a2262694131187c42dadd6920f933dd7 Mon Sep 17 00:00:00 2001 From: Darren Schroeder <343840+fdncred@users.noreply.github.com> Date: Thu, 19 Dec 2024 08:43:49 -0600 Subject: [PATCH] lookup closures/blockids and get content in `config flatten` (#14635) # Description This PR continues to tweak `config flatten` by looking up the closures and block_ids and extracts the content into the produced record. Example ![image](https://github.com/user-attachments/assets/99a9db54-e477-40b2-8468-bbadcf0aa5b7) # User-Facing Changes # Tests + Formatting # After Submitting --- .../src/env/config/config_flatten.rs | 59 ++++++++++++++++--- crates/nu-utils/src/flatten_json.rs | 14 +++-- 2 files changed, 60 insertions(+), 13 deletions(-) diff --git a/crates/nu-command/src/env/config/config_flatten.rs b/crates/nu-command/src/env/config/config_flatten.rs index 3d27f02def..ee41897466 100644 --- a/crates/nu-command/src/env/config/config_flatten.rs +++ b/crates/nu-command/src/env/config/config_flatten.rs @@ -53,16 +53,21 @@ impl Command for ConfigFlatten { }; // Flatten the JSON value let flattened_config_str = flattener.flatten(&serialized_config).to_string(); - let flattened_values = convert_string_to_value(&flattened_config_str, call.head)?; + let flattened_values = + convert_string_to_value(&flattened_config_str, engine_state, call.head)?; Ok(flattened_values.into_pipeline_data()) } } // From here below is taken from `from json`. Would be nice to have a nu-utils-value crate that could be shared -fn convert_string_to_value(string_input: &str, span: Span) -> Result { +fn convert_string_to_value( + string_input: &str, + engine_state: &EngineState, + span: Span, +) -> Result { match nu_json::from_str(string_input) { - Ok(value) => Ok(convert_nujson_to_value(value, span)), + Ok(value) => Ok(convert_nujson_to_value(None, value, engine_state, span)), Err(x) => match x { nu_json::Error::Syntax(_, row, col) => { @@ -91,22 +96,42 @@ fn convert_string_to_value(string_input: &str, span: Span) -> Result Value { +fn convert_nujson_to_value( + key: Option, + value: nu_json::Value, + engine_state: &EngineState, + span: Span, +) -> Value { match value { nu_json::Value::Array(array) => Value::list( array .into_iter() - .map(|x| convert_nujson_to_value(x, span)) + .map(|x| convert_nujson_to_value(key.clone(), x, engine_state, span)) .collect(), span, ), nu_json::Value::Bool(b) => Value::bool(b, span), nu_json::Value::F64(f) => Value::float(f, span), - nu_json::Value::I64(i) => Value::int(i, span), + nu_json::Value::I64(i) => { + if let Some(closure_str) = expand_closure(key.clone(), i, engine_state) { + Value::string(closure_str, span) + } else { + Value::int(i, span) + } + } nu_json::Value::Null => Value::nothing(span), nu_json::Value::Object(k) => Value::record( k.into_iter() - .map(|(k, v)| (k, convert_nujson_to_value(v, span))) + .map(|(k, v)| { + let mut key = k.clone(); + // Keep .Closure.val and .block_id as part of the key during conversion to value + let value = convert_nujson_to_value(Some(key.clone()), v, engine_state, span); + // Replace .Closure.val and .block_id from the key after the conversion + if key.contains(".Closure.val") || key.contains(".block_id") { + key = key.replace(".Closure.val", "").replace(".block_id", ""); + } + (key, value) + }) .collect(), span, ), @@ -121,6 +146,8 @@ fn convert_nujson_to_value(value: nu_json::Value, span: Span) -> Value { }, span, ) + } else if let Some(closure_str) = expand_closure(key.clone(), u as i64, engine_state) { + Value::string(closure_str, span) } else { Value::int(u as i64, span) } @@ -129,6 +156,24 @@ fn convert_nujson_to_value(value: nu_json::Value, span: Span) -> Value { } } +// If the block_id is a real block id, then it should expand into the closure contents, otherwise return None +fn expand_closure( + key: Option, + block_id: i64, + engine_state: &EngineState, +) -> Option { + match key { + Some(key) if key.contains(".Closure.val") || key.contains(".block_id") => engine_state + .try_get_block(nu_protocol::BlockId::new(block_id as usize)) + .and_then(|block| block.span) + .map(|span| { + let contents = engine_state.get_span_contents(span); + String::from_utf8_lossy(contents).to_string() + }), + _ => None, + } +} + // Converts row+column to a Span, assuming bytes (1-based rows) fn convert_row_column_to_span(row: usize, col: usize, contents: &str) -> Span { let mut cur_row = 1; diff --git a/crates/nu-utils/src/flatten_json.rs b/crates/nu-utils/src/flatten_json.rs index 31972d14eb..ed384db765 100644 --- a/crates/nu-utils/src/flatten_json.rs +++ b/crates/nu-utils/src/flatten_json.rs @@ -227,9 +227,6 @@ impl<'a> JsonFlattener<'a> { if filtered_key.contains(".Record.val") { filtered_key = filtered_key.replace(".Record.val", ""); } - if filtered_key.contains(".Closure.val") { - filtered_key = filtered_key.replace(".Closure.val", ""); - } if filtered_key.contains(".List.vals") { filtered_key = filtered_key.replace(".List.vals", ""); } @@ -239,9 +236,14 @@ impl<'a> JsonFlattener<'a> { if filtered_key.contains(".Bool.val") { filtered_key = filtered_key.replace(".Bool.val", ""); } - if filtered_key.contains(".block_id") { - filtered_key = filtered_key.replace(".block_id", ""); - } + // For now, let's skip replacing these because they tell us which + // numbers are closures and blocks which is useful for extracting the content + // if filtered_key.contains(".Closure.val") { + // filtered_key = filtered_key.replace(".Closure.val", ""); + // } + // if filtered_key.contains(".block_id") { + // filtered_key = filtered_key.replace(".block_id", ""); + // } filtered_key } }