add --raw-value option to debug command (#15581)

# Description

This adds a new option `--raw-value`/`-v` to the `debug` command to
allow you to only get the debug string part of the nushell value.
Because, sometimes you don't need the span or nushell datatype and you
just want the val part.

You can see the difference between `debug -r` and `debug -v` here.

![image](https://github.com/user-attachments/assets/ac16cdf0-2ec8-4f61-a2c4-81341f8d363b)

It should work on all datatypes except Value::Error and Value::Closure.

# 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 2025-04-17 12:12:07 -05:00 committed by GitHub
parent 7fcebf37ec
commit 38e761493d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -23,6 +23,11 @@ impl Command for Debug {
])
.category(Category::Debug)
.switch("raw", "Prints the raw value representation", Some('r'))
.switch(
"raw-value",
"Prints the raw value representation but not the nushell value part",
Some('v'),
)
}
fn run(
@ -35,6 +40,7 @@ impl Command for Debug {
let head = call.head;
let config = stack.get_config(engine_state);
let raw = call.has_flag(engine_state, stack, "raw")?;
let raw_value = call.has_flag(engine_state, stack, "raw-value")?;
// Should PipelineData::Empty result in an error here?
@ -42,6 +48,11 @@ impl Command for Debug {
move |x| {
if raw {
Value::string(x.to_debug_string(), head)
} else if raw_value {
match x.coerce_into_string_all() {
Ok(s) => Value::string(format!("{s:#?}"), head),
Err(e) => Value::error(e, head),
}
} else {
Value::string(x.to_expanded_string(", ", &config), head)
}
@ -78,10 +89,75 @@ impl Command for Debug {
Span::test_data(),
)),
},
Example {
description: "Debug print an ansi escape encoded string and get the raw value",
example: "$'(ansi red)nushell(ansi reset)' | debug -v",
result: Some(Value::test_string("\"\\u{1b}[31mnushell\\u{1b}[0m\"")),
},
]
}
}
// This is just a local Value Extension trait to avoid having to
// put another *_to_string() converter in nu_protocol
trait ValueExt {
fn coerce_into_string_all(&self) -> Result<String, ShellError>;
fn cant_convert_to<T>(&self, typ: &str) -> Result<T, ShellError>;
}
impl ValueExt for Value {
fn cant_convert_to<T>(&self, typ: &str) -> Result<T, ShellError> {
Err(ShellError::CantConvert {
to_type: typ.into(),
from_type: self.get_type().to_string(),
span: self.span(),
help: None,
})
}
fn coerce_into_string_all(&self) -> Result<String, ShellError> {
let span = self.span();
match self {
Value::Bool { val, .. } => Ok(val.to_string()),
Value::Int { val, .. } => Ok(val.to_string()),
Value::Float { val, .. } => Ok(val.to_string()),
Value::String { val, .. } => Ok(val.to_string()),
Value::Glob { val, .. } => Ok(val.to_string()),
Value::Filesize { val, .. } => Ok(val.get().to_string()),
Value::Duration { val, .. } => Ok(val.to_string()),
Value::Date { val, .. } => Ok(val.to_rfc3339_opts(chrono::SecondsFormat::Nanos, true)),
Value::Range { val, .. } => Ok(val.to_string()),
Value::Record { val, .. } => Ok(format!(
"{{{}}}",
val.iter()
.map(|(x, y)| match y.coerce_into_string_all() {
Ok(value) => format!("{x}: {value}"),
Err(err) => format!("Error: {err}"),
})
.collect::<Vec<_>>()
.join(", ")
)),
Value::List { vals, .. } => Ok(format!(
"[{}]",
vals.iter()
.map(|x| match x.coerce_into_string_all() {
Ok(value) => value,
Err(err) => format!("Error: {err}"),
})
.collect::<Vec<_>>()
.join(", ")
)),
Value::Binary { val, .. } => match String::from_utf8(val.to_vec()) {
Ok(s) => Ok(s),
Err(err) => Value::binary(err.into_bytes(), span).cant_convert_to("string"),
},
Value::CellPath { val, .. } => Ok(val.to_string()),
Value::Nothing { .. } => Ok("nothing".to_string()),
val => val.cant_convert_to("string"),
}
}
}
#[cfg(test)]
mod test {
#[test]