Improves commands that support range input (#13113)

# Description
Fixes: #13105
Fixes: #13077

This pr makes `str substring`, `bytes at` work better with negative
index.

And it also fixes the false range semantic on `detect columns -c` in
some cases.

# User-Facing Changes
For `str substring`, `bytes at`, it will no-longer return an error if
start index is larger than end index. It makes sense to return an empty
string of empty bytes directly.

### Before
```nushell
# str substring
❯ ("aaa" | str substring 2..-3) == ""
Error: nu:🐚:type_mismatch

  × Type mismatch.
   ╭─[entry #23:1:10]
 1 │ ("aaa" | str substring 2..-3) == ""
   ·          ──────┬──────
   ·                ╰── End must be greater than or equal to Start
 2 │ true
   ╰────

# bytes at
❯ ("aaa" | encode utf-8 | bytes at 2..-3) == ("" | encode utf-8)
Error: nu:🐚:type_mismatch

  × Type mismatch.
   ╭─[entry #27:1:25]
 1 │ ("aaa" | encode utf-8 | bytes at 2..-3) == ("" | encode utf-8)
   ·                         ────┬───
   ·                             ╰── End must be greater than or equal to Start
   ╰────
```
### After
```nushell
# str substring
❯ ("aaa" | str substring 2..-3) == ""
true

# bytes at
❯  ("aaa" | encode utf-8 | bytes at 2..-3) == ("" | encode utf-8)
true
```
# Tests + Formatting
Added some tests, adjust existing tests
This commit is contained in:
Wind
2024-06-18 20:19:13 +08:00
committed by GitHub
parent ae6489f04b
commit 28ed0fe700
5 changed files with 85 additions and 98 deletions

View File

@ -128,48 +128,24 @@ fn action(input: &Value, args: &Arguments, head: Span) -> Value {
let range = &args.indexes;
match input {
Value::Binary { val, .. } => {
use std::cmp::{self, Ordering};
let len = val.len() as isize;
let start = if range.0 < 0 { range.0 + len } else { range.0 };
let end = if range.1 < 0 { range.1 + len } else { range.1 };
let end = if range.1 < 0 {
cmp::max(range.1 + len, 0)
} else {
range.1
};
if start < len && end >= 0 {
match start.cmp(&end) {
Ordering::Equal => Value::binary(vec![], head),
Ordering::Greater => Value::error(
ShellError::TypeMismatch {
err_message: "End must be greater than or equal to Start".to_string(),
span: head,
},
head,
),
Ordering::Less => Value::binary(
if end == isize::MAX {
val.iter()
.skip(start as usize)
.copied()
.collect::<Vec<u8>>()
} else {
val.iter()
.skip(start as usize)
.take((end - start) as usize)
.copied()
.collect()
},
head,
),
}
} else {
if start > end {
Value::binary(vec![], head)
} else {
let val_iter = val.iter().skip(start as usize);
Value::binary(
if end == isize::MAX {
val_iter.copied().collect::<Vec<u8>>()
} else {
val_iter.take((end - start + 1) as usize).copied().collect()
},
head,
)
}
}
Value::Error { .. } => input.clone(),
other => Value::error(