Implementing ByteStream interuption on infinite stream (#13552)

# Description

This PR should address #13530 by explicitly handling ByteStreams. 

The issue can be replicated easily on linux by running:

```nushell
open /dev/urandom | into binary | bytes at ..10
```

Would leave the output hanging and with no way to cancel it, this was
likely because it was trying to collect the input stream and would not
complete.

I have also put in an error to say that using negative offsets for a
bytestream without a length cannot be used.

```nushell
~/git/nushell> open /dev/urandom | into binary | bytes at (-1)..
Error: nu:🐚:incorrect_value

  × Incorrect value.
   ╭─[entry #3:1:35]
 1 │ open /dev/urandom | into binary | bytes at (-1)..
   ·                                   ────┬─── ───┬──
   ·                                       │       ╰── encountered here
   ·                                       ╰── Negative range values cannot be used with streams that don't specify a length
   ╰────
   ```

# User-Facing Changes

No operation changes, only the warning you get back for negative offsets

# Tests + Formatting

Ran `toolkit check pr ` with no errors or warnings

Manual testing of the example commands above

---------

Co-authored-by: Ian Manske <ian.manske@pm.me>
Co-authored-by: Simon Curtis <simon.curtis@candc-uk.com>
This commit is contained in:
Simon Curtis
2025-01-11 21:28:08 +00:00
committed by GitHub
parent 0b71eb201c
commit f05162811c
15 changed files with 612 additions and 103 deletions

View File

@@ -81,10 +81,46 @@ mod int_range {
self.start
}
// Resolves the absolute start position given the length of the input value
pub fn absolute_start(&self, len: u64) -> u64 {
let max_index = len - 1;
match self.start {
start if start < 0 => len.saturating_sub(start.unsigned_abs()),
start => max_index.min(start as u64),
}
}
/// Returns the distance between the start and end of the range
/// The result will always be 0 or positive
pub fn distance(&self) -> Bound<u64> {
match self.end {
Bound::Unbounded => Bound::Unbounded,
Bound::Included(end) if self.start > end => Bound::Included(0),
Bound::Excluded(end) if self.start > end => Bound::Excluded(0),
Bound::Included(end) => Bound::Included((end - self.start) as u64),
Bound::Excluded(end) => Bound::Excluded((end - self.start) as u64),
}
}
pub fn end(&self) -> Bound<i64> {
self.end
}
pub fn absolute_end(&self, len: u64) -> Bound<u64> {
let max_index = len - 1;
match self.end {
Bound::Unbounded => Bound::Unbounded,
Bound::Included(i) => Bound::Included(match i {
i if i < 0 => len.saturating_sub(i.unsigned_abs()),
i => max_index.min(i as u64),
}),
Bound::Excluded(i) => Bound::Excluded(match i {
i if i < 0 => len.saturating_sub(i.unsigned_abs()),
i => len.min(i as u64),
}),
}
}
pub fn step(&self) -> i64 {
self.step
}
@@ -93,6 +129,21 @@ mod int_range {
self.end == Bound::Unbounded
}
pub fn is_relative(&self) -> bool {
self.is_start_relative() || self.is_end_relative()
}
pub fn is_start_relative(&self) -> bool {
self.start < 0
}
pub fn is_end_relative(&self) -> bool {
match self.end {
Bound::Included(end) | Bound::Excluded(end) => end < 0,
_ => false,
}
}
pub fn contains(&self, value: i64) -> bool {
if self.step < 0 {
// Decreasing range