From 28a94048c5d728fd9ac47e03632c58755bd7b8d0 Mon Sep 17 00:00:00 2001 From: Tyarel8 <98483313+Tyarel8@users.noreply.github.com> Date: Fri, 13 Jun 2025 23:13:26 +0200 Subject: [PATCH] feat(format number): add `--no-prefix` flag (#15960) # Description I have added a `--no-prefix` flag to the `format number` command to not include the `0b`, `0x` and `0o` prefixes in the output. Also, I've changed the order in which the formats are displayed to one I thinks makes it easier to read, with the upper and lower alternatives next to each other. ![image](https://github.com/user-attachments/assets/cd50631d-1b27-40d4-84d9-f2ac125586d4) # User-Facing Changes The formatting of floats previously did not include prefixes while integers did. Now prefixes are on by default for both, while including the new flag removes them. Changing the order of the record shouldn't have any effect on previous code. # Tests + Formatting I have added an additional example that test this behavior. # After Submitting --------- Co-authored-by: Darren Schroeder <343840+fdncred@users.noreply.github.com> --- .../src/extra/strings/format/number.rs | 134 ++++++++++++++---- 1 file changed, 106 insertions(+), 28 deletions(-) diff --git a/crates/nu-cmd-extra/src/extra/strings/format/number.rs b/crates/nu-cmd-extra/src/extra/strings/format/number.rs index ad8a6b08fb..7af6a6e798 100644 --- a/crates/nu-cmd-extra/src/extra/strings/format/number.rs +++ b/crates/nu-cmd-extra/src/extra/strings/format/number.rs @@ -16,6 +16,11 @@ impl Command for FormatNumber { fn signature(&self) -> nu_protocol::Signature { Signature::build("format number") .input_output_types(vec![(Type::Number, Type::record())]) + .switch( + "no-prefix", + "don't include the binary, hex or octal prefixes", + Some('n'), + ) .category(Category::Conversions) } @@ -24,20 +29,36 @@ impl Command for FormatNumber { } fn examples(&self) -> Vec { - vec![Example { - description: "Get a record containing multiple formats for the number 42", - example: "42 | format number", - result: Some(Value::test_record(record! { - "binary" => Value::test_string("0b101010"), - "debug" => Value::test_string("42"), - "display" => Value::test_string("42"), - "lowerexp" => Value::test_string("4.2e1"), - "lowerhex" => Value::test_string("0x2a"), - "octal" => Value::test_string("0o52"), - "upperexp" => Value::test_string("4.2E1"), - "upperhex" => Value::test_string("0x2A"), - })), - }] + vec![ + Example { + description: "Get a record containing multiple formats for the number 42", + example: "42 | format number", + result: Some(Value::test_record(record! { + "debug" => Value::test_string("42"), + "display" => Value::test_string("42"), + "binary" => Value::test_string("0b101010"), + "lowerexp" => Value::test_string("4.2e1"), + "upperexp" => Value::test_string("4.2E1"), + "lowerhex" => Value::test_string("0x2a"), + "upperhex" => Value::test_string("0x2A"), + "octal" => Value::test_string("0o52"), + })), + }, + Example { + description: "Format float without prefixes", + example: "3.14 | format number --no-prefix", + result: Some(Value::test_record(record! { + "debug" => Value::test_string("3.14"), + "display" => Value::test_string("3.14"), + "binary" => Value::test_string("100000000001001000111101011100001010001111010111000010100011111"), + "lowerexp" => Value::test_string("3.14e0"), + "upperexp" => Value::test_string("3.14E0"), + "lowerhex" => Value::test_string("40091eb851eb851f"), + "upperhex" => Value::test_string("40091EB851EB851F"), + "octal" => Value::test_string("400110753412172702437"), + })), + }, + ] } fn run( @@ -59,14 +80,24 @@ pub(crate) fn format_number( ) -> Result { let cell_paths: Vec = call.rest(engine_state, stack, 0)?; let args = CellPathOnlyArgs::from(cell_paths); - operate(action, args, input, call.head, engine_state.signals()) + if call.has_flag(engine_state, stack, "no-prefix")? { + operate( + action_no_prefix, + args, + input, + call.head, + engine_state.signals(), + ) + } else { + operate(action, args, input, call.head, engine_state.signals()) + } } fn action(input: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value { match input { - Value::Float { val, .. } => format_f64(*val, span), - Value::Int { val, .. } => format_i64(*val, span), - Value::Filesize { val, .. } => format_i64(val.get(), span), + Value::Float { val, .. } => format_f64(*val, false, span), + Value::Int { val, .. } => format_i64(*val, false, span), + Value::Filesize { val, .. } => format_i64(val.get(), false, span), // Propagate errors by explicitly matching them before the final case. Value::Error { .. } => input.clone(), other => Value::error( @@ -81,33 +112,80 @@ fn action(input: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value { } } -fn format_i64(num: i64, span: Span) -> Value { +fn action_no_prefix(input: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value { + match input { + Value::Float { val, .. } => format_f64(*val, true, span), + Value::Int { val, .. } => format_i64(*val, true, span), + Value::Filesize { val, .. } => format_i64(val.get(), true, span), + // Propagate errors by explicitly matching them before the final case. + Value::Error { .. } => input.clone(), + other => Value::error( + ShellError::OnlySupportsThisInputType { + exp_input_type: "float, int, or filesize".into(), + wrong_type: other.get_type().to_string(), + dst_span: span, + src_span: other.span(), + }, + span, + ), + } +} + +fn format_i64(num: i64, no_prefix: bool, span: Span) -> Value { Value::record( record! { - "binary" => Value::string(format!("{num:#b}"), span), "debug" => Value::string(format!("{num:#?}"), span), "display" => Value::string(format!("{num}"), span), + "binary" => Value::string( + if no_prefix { format!("{num:b}") } else { format!("{num:#b}") }, + span, + ), "lowerexp" => Value::string(format!("{num:#e}"), span), - "lowerhex" => Value::string(format!("{num:#x}"), span), - "octal" => Value::string(format!("{num:#o}"), span), "upperexp" => Value::string(format!("{num:#E}"), span), - "upperhex" => Value::string(format!("{num:#X}"), span), + "lowerhex" => Value::string( + if no_prefix { format!("{num:x}") } else { format!("{num:#x}") }, + span, + ), + "upperhex" => Value::string( + if no_prefix { format!("{num:X}") } else { format!("{num:#X}") }, + span, + ), + "octal" => Value::string( + if no_prefix { format!("{num:o}") } else { format!("{num:#o}") }, + span, + ) }, span, ) } -fn format_f64(num: f64, span: Span) -> Value { +fn format_f64(num: f64, no_prefix: bool, span: Span) -> Value { Value::record( record! { - "binary" => Value::string(format!("{:b}", num.to_bits()), span), "debug" => Value::string(format!("{num:#?}"), span), "display" => Value::string(format!("{num}"), span), + "binary" => Value::string( + if no_prefix { + format!("{:b}", num.to_bits()) + } else { + format!("{:#b}", num.to_bits()) + }, + span, + ), "lowerexp" => Value::string(format!("{num:#e}"), span), - "lowerhex" => Value::string(format!("{:0x}", num.to_bits()), span), - "octal" => Value::string(format!("{:0o}", num.to_bits()), span), "upperexp" => Value::string(format!("{num:#E}"), span), - "upperhex" => Value::string(format!("{:0X}", num.to_bits()), span), + "lowerhex" => Value::string( + if no_prefix { format!("{:x}", num.to_bits()) } else { format!("{:#x}", num.to_bits()) }, + span, + ), + "upperhex" => Value::string( + if no_prefix { format!("{:X}", num.to_bits()) } else { format!("{:#X}", num.to_bits()) }, + span, + ), + "octal" => Value::string( + if no_prefix { format!("{:o}", num.to_bits()) } else { format!("{:#o}", num.to_bits()) }, + span, + ) }, span, )