Fix incorrect handling of boolean flags for builtin commands (#11492)

# Description
Possible fix of #11456
This PR fixes a bug where builtin commands did not respect the logic of
dynamically passed boolean flags. The reason is
[has_flag](6f59abaf43/crates/nu-protocol/src/ast/call.rs (L204C5-L212C6))
method did not evaluate and take into consideration expression used with
flag.

To address this issue a solution is proposed:
1. `has_flag` method is moved to `CallExt` and new logic to evaluate
expression and check if it is a boolean value is added
2. `has_flag_const` method is added to `CallExt` which is a constant
version of `has_flag`
3. `has_named` method is added to `Call` which is basically the old
logic of `has_flag`
4. All usages of `has_flag` in code are updated, mostly to pass
`engine_state` and `stack` to new `has_flag`. In `run_const` commands it
is replaced with `has_flag_const`. And in a few select places: parser,
`to nuon` and `into string` old logic via `has_named` is used.

# User-Facing Changes
Explicit values of boolean flags are now respected in builtin commands.
Before:

![image](https://github.com/nushell/nushell/assets/17511668/f9fbabb2-3cfd-43f9-ba9e-ece76d80043c)
After:

![image](https://github.com/nushell/nushell/assets/17511668/21867596-2075-437f-9c85-45563ac70083)

Another example:
Before:

![image](https://github.com/nushell/nushell/assets/17511668/efdbc5ca-5227-45a4-ac5b-532cdc2bbf5f)
After:

![image](https://github.com/nushell/nushell/assets/17511668/2907d5c5-aa93-404d-af1c-21cdc3d44646)


# Tests + Formatting
Added test reproducing some variants of original issue.
This commit is contained in:
Artemiy
2024-01-11 18:19:48 +03:00
committed by GitHub
parent 62272975f2
commit 1867bb1a88
149 changed files with 771 additions and 504 deletions

View File

@ -148,7 +148,7 @@ fn into_binary(
_ => {
let args = Arguments {
cell_paths,
compact: call.has_flag("compact"),
compact: call.has_flag(engine_state, stack, "compact")?,
};
operate(action, args, input, call.head, engine_state.ctrlc.clone())
}

View File

@ -116,9 +116,9 @@ impl Command for SubCommand {
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
if call.has_flag("list") {
if call.has_flag(engine_state, stack, "list")? {
Ok(generate_strftime_list(call.head, true).into_pipeline_data())
} else if call.has_flag("list-human") {
} else if call.has_flag(engine_state, stack, "list-human")? {
Ok(list_human_readable_examples(call.head).into_pipeline_data())
} else {
let cell_paths = call.rest(engine_state, stack, 0)?;

View File

@ -11,7 +11,6 @@ use num_format::ToFormattedString;
struct Arguments {
decimals_value: Option<i64>,
decimals: bool,
cell_paths: Option<Vec<CellPath>>,
config: Config,
}
@ -148,11 +147,10 @@ fn string_helper(
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let decimals = call.has_flag("decimals");
let head = call.head;
let decimals_value: Option<i64> = call.get_flag(engine_state, stack, "decimals")?;
if let Some(decimal_val) = decimals_value {
if decimals && decimal_val.is_negative() {
if decimal_val.is_negative() {
return Err(ShellError::TypeMismatch {
err_message: "Cannot accept negative integers for decimals arguments".to_string(),
span: head,
@ -164,7 +162,6 @@ fn string_helper(
let config = engine_state.get_config().clone();
let args = Arguments {
decimals_value,
decimals,
cell_paths,
config,
};
@ -186,7 +183,6 @@ fn string_helper(
}
fn action(input: &Value, args: &Arguments, span: Span) -> Value {
let decimals = args.decimals;
let digits = args.decimals_value;
let config = &args.config;
match input {
@ -196,8 +192,8 @@ fn action(input: &Value, args: &Arguments, span: Span) -> Value {
Value::string(res, span)
}
Value::Float { val, .. } => {
if decimals {
let decimal_value = digits.unwrap_or(2) as usize;
if let Some(decimal_value) = digits {
let decimal_value = decimal_value as usize;
Value::string(format!("{val:.decimal_value$}"), span)
} else {
Value::string(val.to_string(), span)