From 38e761493df661e4025066e4cf1c616331384e4e Mon Sep 17 00:00:00 2001 From: Darren Schroeder <343840+fdncred@users.noreply.github.com> Date: Thu, 17 Apr 2025 12:12:07 -0500 Subject: [PATCH] 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 # Tests + Formatting # After Submitting --- crates/nu-command/src/debug/debug_.rs | 76 +++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/crates/nu-command/src/debug/debug_.rs b/crates/nu-command/src/debug/debug_.rs index 8ce3407c89..1e559158b3 100644 --- a/crates/nu-command/src/debug/debug_.rs +++ b/crates/nu-command/src/debug/debug_.rs @@ -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; + fn cant_convert_to(&self, typ: &str) -> Result; +} + +impl ValueExt for Value { + fn cant_convert_to(&self, typ: &str) -> Result { + 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 { + 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::>() + .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::>() + .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]