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>
This commit is contained in:
Tyarel8 2025-06-13 23:13:26 +02:00 committed by GitHub
parent fb691c0da5
commit 28a94048c5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -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<Example> {
vec![Example {
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"),
"binary" => Value::test_string("0b101010"),
"lowerexp" => Value::test_string("4.2e1"),
"lowerhex" => Value::test_string("0x2a"),
"octal" => Value::test_string("0o52"),
"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<PipelineData, ShellError> {
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
let args = CellPathOnlyArgs::from(cell_paths);
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,
)