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
<!-- List of all changes that impact the user experience here. This
helps us keep track of breaking changes. -->

# Tests + Formatting
<!--
Don't forget to add tests that cover your changes.

Make sure you've run and fixed any issues with these commands:

- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used` to
check that you're using the standard code style
- `cargo test --workspace` to check that all tests pass (on Windows make
sure to [enable developer
mode](https://learn.microsoft.com/en-us/windows/apps/get-started/developer-mode-features-and-debugging))
- `cargo run -- -c "use toolkit.nu; toolkit test stdlib"` to run the
tests for the standard library

> **Note**
> from `nushell` you can also use the `toolkit` as follows
> ```bash
> use toolkit.nu # or use an `env_change` hook to activate it
automatically
> toolkit check pr
> ```
-->

# After Submitting
<!-- If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
-->
This commit is contained in:
Darren Schroeder 2024-12-19 08:43:49 -06:00 committed by GitHub
parent 7029d24f42
commit f2e8c391a2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 60 additions and 13 deletions

View file

@ -53,16 +53,21 @@ impl Command for ConfigFlatten {
}; };
// Flatten the JSON value // Flatten the JSON value
let flattened_config_str = flattener.flatten(&serialized_config).to_string(); 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()) 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 // 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<Value, ShellError> { fn convert_string_to_value(
string_input: &str,
engine_state: &EngineState,
span: Span,
) -> Result<Value, ShellError> {
match nu_json::from_str(string_input) { 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 { Err(x) => match x {
nu_json::Error::Syntax(_, row, col) => { nu_json::Error::Syntax(_, row, col) => {
@ -91,22 +96,42 @@ fn convert_string_to_value(string_input: &str, span: Span) -> Result<Value, Shel
} }
} }
fn convert_nujson_to_value(value: nu_json::Value, span: Span) -> Value { fn convert_nujson_to_value(
key: Option<String>,
value: nu_json::Value,
engine_state: &EngineState,
span: Span,
) -> Value {
match value { match value {
nu_json::Value::Array(array) => Value::list( nu_json::Value::Array(array) => Value::list(
array array
.into_iter() .into_iter()
.map(|x| convert_nujson_to_value(x, span)) .map(|x| convert_nujson_to_value(key.clone(), x, engine_state, span))
.collect(), .collect(),
span, span,
), ),
nu_json::Value::Bool(b) => Value::bool(b, span), nu_json::Value::Bool(b) => Value::bool(b, span),
nu_json::Value::F64(f) => Value::float(f, 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::Null => Value::nothing(span),
nu_json::Value::Object(k) => Value::record( nu_json::Value::Object(k) => Value::record(
k.into_iter() 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(), .collect(),
span, span,
), ),
@ -121,6 +146,8 @@ fn convert_nujson_to_value(value: nu_json::Value, span: Span) -> Value {
}, },
span, span,
) )
} else if let Some(closure_str) = expand_closure(key.clone(), u as i64, engine_state) {
Value::string(closure_str, span)
} else { } else {
Value::int(u as i64, span) 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<String>,
block_id: i64,
engine_state: &EngineState,
) -> Option<String> {
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) // Converts row+column to a Span, assuming bytes (1-based rows)
fn convert_row_column_to_span(row: usize, col: usize, contents: &str) -> Span { fn convert_row_column_to_span(row: usize, col: usize, contents: &str) -> Span {
let mut cur_row = 1; let mut cur_row = 1;

View file

@ -227,9 +227,6 @@ impl<'a> JsonFlattener<'a> {
if filtered_key.contains(".Record.val") { if filtered_key.contains(".Record.val") {
filtered_key = filtered_key.replace(".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") { if filtered_key.contains(".List.vals") {
filtered_key = filtered_key.replace(".List.vals", ""); filtered_key = filtered_key.replace(".List.vals", "");
} }
@ -239,9 +236,14 @@ impl<'a> JsonFlattener<'a> {
if filtered_key.contains(".Bool.val") { if filtered_key.contains(".Bool.val") {
filtered_key = filtered_key.replace(".Bool.val", ""); filtered_key = filtered_key.replace(".Bool.val", "");
} }
if filtered_key.contains(".block_id") { // For now, let's skip replacing these because they tell us which
filtered_key = filtered_key.replace(".block_id", ""); // 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 filtered_key
} }
} }