[ty] Homogeneous and mixed tuples (#18600)
Some checks are pending
CI / cargo fuzz build (push) Blocked by required conditions
CI / Determine changes (push) Waiting to run
CI / cargo fmt (push) Waiting to run
CI / cargo clippy (push) Blocked by required conditions
CI / cargo test (linux) (push) Blocked by required conditions
CI / cargo test (linux, release) (push) Blocked by required conditions
CI / cargo test (windows) (push) Blocked by required conditions
CI / cargo test (wasm) (push) Blocked by required conditions
CI / cargo build (release) (push) Waiting to run
CI / cargo build (msrv) (push) Blocked by required conditions
CI / fuzz parser (push) Blocked by required conditions
CI / test scripts (push) Blocked by required conditions
CI / ecosystem (push) Blocked by required conditions
CI / Fuzz for new ty panics (push) Blocked by required conditions
CI / cargo shear (push) Blocked by required conditions
CI / python package (push) Waiting to run
CI / pre-commit (push) Waiting to run
CI / mkdocs (push) Waiting to run
CI / formatter instabilities and black similarity (push) Blocked by required conditions
CI / test ruff-lsp (push) Blocked by required conditions
CI / check playground (push) Blocked by required conditions
CI / benchmarks-instrumented (push) Blocked by required conditions
CI / benchmarks-walltime (push) Blocked by required conditions
[ty Playground] Release / publish (push) Waiting to run

We already had support for homogeneous tuples (`tuple[int, ...]`). This
PR extends this to also support mixed tuples (`tuple[str, str,
*tuple[int, ...], str str]`).

A mixed tuple consists of a fixed-length (possibly empty) prefix and
suffix, and a variable-length portion in the middle. Every element of
the variable-length portion must be of the same type. A homogeneous
tuple is then just a mixed tuple with an empty prefix and suffix.

The new data representation uses different Rust types for a fixed-length
(aka heterogeneous) tuple. Another option would have been to use the
`VariableLengthTuple` representation for all tuples, and to wrap the
"variable + suffix" portion in an `Option`. I don't think that would
simplify the method implementations much, though, since we would still
have a 2×2 case analysis for most of them.

One wrinkle is that the definition of the `tuple` class in the typeshed
has a single typevar, and canonically represents a homogeneous tuple.
When getting the class of a tuple instance, that means that we have to
summarize our detailed mixed tuple type information into its
"homogeneous supertype". (We were already doing this for heterogeneous
types.)

A similar thing happens when concatenating two mixed tuples: the
variable-length portion and suffix of the LHS, and the prefix and
variable-length portion of the RHS, all get unioned into the
variable-length portion of the result. The LHS prefix and RHS suffix
carry through unchanged.

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
This commit is contained in:
Douglas Creager 2025-06-20 18:23:54 -04:00 committed by GitHub
parent d9266284df
commit ea812d0813
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
32 changed files with 2432 additions and 758 deletions

View file

@ -4,13 +4,15 @@
use itertools::Either;
use crate::Db;
#[derive(Debug, Clone, Copy, PartialEq)]
pub(crate) struct OutOfBoundsError;
pub(crate) trait PyIndex {
type Item;
pub(crate) trait PyIndex<'db> {
type Item: 'db;
fn py_index(&mut self, index: i32) -> Result<Self::Item, OutOfBoundsError>;
fn py_index(self, db: &'db dyn Db, index: i32) -> Result<Self::Item, OutOfBoundsError>;
}
fn from_nonnegative_i32(index: i32) -> usize {
@ -39,13 +41,13 @@ enum Position {
AfterEnd,
}
enum Nth {
pub(crate) enum Nth {
FromStart(usize),
FromEnd(usize),
}
impl Nth {
fn from_index(index: i32) -> Self {
pub(crate) fn from_index(index: i32) -> Self {
if index >= 0 {
Nth::FromStart(from_nonnegative_i32(index))
} else {
@ -75,13 +77,26 @@ impl Nth {
}
}
impl<I, T> PyIndex for T
impl<'db, T> PyIndex<'db> for &'db [T] {
type Item = &'db T;
fn py_index(self, _db: &'db dyn Db, index: i32) -> Result<&'db T, OutOfBoundsError> {
match Nth::from_index(index) {
Nth::FromStart(nth) => self.get(nth).ok_or(OutOfBoundsError),
Nth::FromEnd(nth_rev) => (self.len().checked_sub(nth_rev + 1))
.map(|idx| &self[idx])
.ok_or(OutOfBoundsError),
}
}
}
impl<'db, I: 'db, T> PyIndex<'db> for &mut T
where
T: DoubleEndedIterator<Item = I>,
{
type Item = I;
fn py_index(&mut self, index: i32) -> Result<I, OutOfBoundsError> {
fn py_index(self, _db: &'db dyn Db, index: i32) -> Result<I, OutOfBoundsError> {
match Nth::from_index(index) {
Nth::FromStart(nth) => self.nth(nth).ok_or(OutOfBoundsError),
Nth::FromEnd(nth_rev) => self.nth_back(nth_rev).ok_or(OutOfBoundsError),
@ -92,32 +107,28 @@ where
#[derive(Debug, Clone, Copy, PartialEq)]
pub(crate) struct StepSizeZeroError;
pub(crate) trait PySlice {
type Item;
pub(crate) trait PySlice<'db> {
type Item: 'db;
fn py_slice(
&self,
&'db self,
db: &'db dyn Db,
start: Option<i32>,
stop: Option<i32>,
step: Option<i32>,
) -> Result<
Either<impl Iterator<Item = &Self::Item>, impl Iterator<Item = &Self::Item>>,
StepSizeZeroError,
>;
) -> Result<impl Iterator<Item = &'db Self::Item>, StepSizeZeroError>;
}
impl<T> PySlice for [T] {
impl<'db, T: 'db> PySlice<'db> for [T] {
type Item = T;
fn py_slice(
&self,
&'db self,
_db: &'db dyn Db,
start: Option<i32>,
stop: Option<i32>,
step_int: Option<i32>,
) -> Result<
Either<impl Iterator<Item = &Self::Item>, impl Iterator<Item = &Self::Item>>,
StepSizeZeroError,
> {
) -> Result<impl Iterator<Item = &'db Self::Item>, StepSizeZeroError> {
let step_int = step_int.unwrap_or(1);
if step_int == 0 {
return Err(StepSizeZeroError);
@ -194,6 +205,8 @@ impl<T> PySlice for [T] {
#[cfg(test)]
#[expect(clippy::redundant_clone)]
mod tests {
use crate::Db;
use crate::db::tests::setup_db;
use crate::util::subscript::{OutOfBoundsError, StepSizeZeroError};
use super::{PyIndex, PySlice};
@ -201,302 +214,387 @@ mod tests {
#[test]
fn py_index_empty() {
let db = setup_db();
let iter = std::iter::empty::<char>();
assert_eq!(iter.clone().py_index(0), Err(OutOfBoundsError));
assert_eq!(iter.clone().py_index(1), Err(OutOfBoundsError));
assert_eq!(iter.clone().py_index(-1), Err(OutOfBoundsError));
assert_eq!(iter.clone().py_index(i32::MIN), Err(OutOfBoundsError));
assert_eq!(iter.clone().py_index(i32::MAX), Err(OutOfBoundsError));
assert_eq!(iter.clone().py_index(&db, 0), Err(OutOfBoundsError));
assert_eq!(iter.clone().py_index(&db, 1), Err(OutOfBoundsError));
assert_eq!(iter.clone().py_index(&db, -1), Err(OutOfBoundsError));
assert_eq!(iter.clone().py_index(&db, i32::MIN), Err(OutOfBoundsError));
assert_eq!(iter.clone().py_index(&db, i32::MAX), Err(OutOfBoundsError));
}
#[test]
fn py_index_single_element() {
let db = setup_db();
let iter = ['a'].into_iter();
assert_eq!(iter.clone().py_index(0), Ok('a'));
assert_eq!(iter.clone().py_index(1), Err(OutOfBoundsError));
assert_eq!(iter.clone().py_index(-1), Ok('a'));
assert_eq!(iter.clone().py_index(-2), Err(OutOfBoundsError));
assert_eq!(iter.clone().py_index(&db, 0), Ok('a'));
assert_eq!(iter.clone().py_index(&db, 1), Err(OutOfBoundsError));
assert_eq!(iter.clone().py_index(&db, -1), Ok('a'));
assert_eq!(iter.clone().py_index(&db, -2), Err(OutOfBoundsError));
}
#[test]
fn py_index_more_elements() {
let db = setup_db();
let iter = ['a', 'b', 'c', 'd', 'e'].into_iter();
assert_eq!(iter.clone().py_index(0), Ok('a'));
assert_eq!(iter.clone().py_index(1), Ok('b'));
assert_eq!(iter.clone().py_index(4), Ok('e'));
assert_eq!(iter.clone().py_index(5), Err(OutOfBoundsError));
assert_eq!(iter.clone().py_index(&db, 0), Ok('a'));
assert_eq!(iter.clone().py_index(&db, 1), Ok('b'));
assert_eq!(iter.clone().py_index(&db, 4), Ok('e'));
assert_eq!(iter.clone().py_index(&db, 5), Err(OutOfBoundsError));
assert_eq!(iter.clone().py_index(-1), Ok('e'));
assert_eq!(iter.clone().py_index(-2), Ok('d'));
assert_eq!(iter.clone().py_index(-5), Ok('a'));
assert_eq!(iter.clone().py_index(-6), Err(OutOfBoundsError));
assert_eq!(iter.clone().py_index(&db, -1), Ok('e'));
assert_eq!(iter.clone().py_index(&db, -2), Ok('d'));
assert_eq!(iter.clone().py_index(&db, -5), Ok('a'));
assert_eq!(iter.clone().py_index(&db, -6), Err(OutOfBoundsError));
}
#[test]
fn py_index_uses_full_index_range() {
let db = setup_db();
let iter = 0..=u32::MAX;
// u32::MAX - |i32::MIN| + 1 = 2^32 - 1 - 2^31 + 1 = 2^31
assert_eq!(iter.clone().py_index(i32::MIN), Ok(2u32.pow(31)));
assert_eq!(iter.clone().py_index(-2), Ok(u32::MAX - 2 + 1));
assert_eq!(iter.clone().py_index(-1), Ok(u32::MAX - 1 + 1));
assert_eq!(iter.clone().py_index(&db, i32::MIN), Ok(2u32.pow(31)));
assert_eq!(iter.clone().py_index(&db, -2), Ok(u32::MAX - 2 + 1));
assert_eq!(iter.clone().py_index(&db, -1), Ok(u32::MAX - 1 + 1));
assert_eq!(iter.clone().py_index(0), Ok(0));
assert_eq!(iter.clone().py_index(1), Ok(1));
assert_eq!(iter.clone().py_index(i32::MAX), Ok(i32::MAX as u32));
assert_eq!(iter.clone().py_index(&db, 0), Ok(0));
assert_eq!(iter.clone().py_index(&db, 1), Ok(1));
assert_eq!(iter.clone().py_index(&db, i32::MAX), Ok(i32::MAX as u32));
}
#[track_caller]
fn assert_eq_slice<const N: usize, const M: usize>(
db: &dyn Db,
input: &[char; N],
start: Option<i32>,
stop: Option<i32>,
step: Option<i32>,
expected: &[char; M],
) {
assert_equal(input.py_slice(start, stop, step).unwrap(), expected.iter());
assert_equal(
input.py_slice(db, start, stop, step).unwrap(),
expected.iter(),
);
}
#[test]
fn py_slice_empty_input() {
let db = setup_db();
let input = [];
assert_eq_slice(&input, None, None, None, &[]);
assert_eq_slice(&input, Some(0), None, None, &[]);
assert_eq_slice(&input, None, Some(0), None, &[]);
assert_eq_slice(&input, Some(0), Some(0), None, &[]);
assert_eq_slice(&input, Some(-5), Some(-5), None, &[]);
assert_eq_slice(&input, None, None, Some(-1), &[]);
assert_eq_slice(&input, None, None, Some(2), &[]);
assert_eq_slice(&db, &input, None, None, None, &[]);
assert_eq_slice(&db, &input, Some(0), None, None, &[]);
assert_eq_slice(&db, &input, None, Some(0), None, &[]);
assert_eq_slice(&db, &input, Some(0), Some(0), None, &[]);
assert_eq_slice(&db, &input, Some(-5), Some(-5), None, &[]);
assert_eq_slice(&db, &input, None, None, Some(-1), &[]);
assert_eq_slice(&db, &input, None, None, Some(2), &[]);
}
#[test]
fn py_slice_single_element_input() {
let db = setup_db();
let input = ['a'];
assert_eq_slice(&input, None, None, None, &['a']);
assert_eq_slice(&db, &input, None, None, None, &['a']);
assert_eq_slice(&input, Some(0), None, None, &['a']);
assert_eq_slice(&input, None, Some(0), None, &[]);
assert_eq_slice(&input, Some(0), Some(0), None, &[]);
assert_eq_slice(&input, Some(0), Some(1), None, &['a']);
assert_eq_slice(&input, Some(0), Some(2), None, &['a']);
assert_eq_slice(&db, &input, Some(0), None, None, &['a']);
assert_eq_slice(&db, &input, None, Some(0), None, &[]);
assert_eq_slice(&db, &input, Some(0), Some(0), None, &[]);
assert_eq_slice(&db, &input, Some(0), Some(1), None, &['a']);
assert_eq_slice(&db, &input, Some(0), Some(2), None, &['a']);
assert_eq_slice(&input, Some(-1), None, None, &['a']);
assert_eq_slice(&input, Some(-1), Some(-1), None, &[]);
assert_eq_slice(&input, Some(-1), Some(0), None, &[]);
assert_eq_slice(&input, Some(-1), Some(1), None, &['a']);
assert_eq_slice(&input, Some(-1), Some(2), None, &['a']);
assert_eq_slice(&input, None, Some(-1), None, &[]);
assert_eq_slice(&db, &input, Some(-1), None, None, &['a']);
assert_eq_slice(&db, &input, Some(-1), Some(-1), None, &[]);
assert_eq_slice(&db, &input, Some(-1), Some(0), None, &[]);
assert_eq_slice(&db, &input, Some(-1), Some(1), None, &['a']);
assert_eq_slice(&db, &input, Some(-1), Some(2), None, &['a']);
assert_eq_slice(&db, &input, None, Some(-1), None, &[]);
assert_eq_slice(&input, Some(-2), None, None, &['a']);
assert_eq_slice(&input, Some(-2), Some(-1), None, &[]);
assert_eq_slice(&input, Some(-2), Some(0), None, &[]);
assert_eq_slice(&input, Some(-2), Some(1), None, &['a']);
assert_eq_slice(&input, Some(-2), Some(2), None, &['a']);
assert_eq_slice(&db, &input, Some(-2), None, None, &['a']);
assert_eq_slice(&db, &input, Some(-2), Some(-1), None, &[]);
assert_eq_slice(&db, &input, Some(-2), Some(0), None, &[]);
assert_eq_slice(&db, &input, Some(-2), Some(1), None, &['a']);
assert_eq_slice(&db, &input, Some(-2), Some(2), None, &['a']);
}
#[test]
fn py_slice_nonnegative_indices() {
let db = setup_db();
let input = ['a', 'b', 'c', 'd', 'e'];
assert_eq_slice(&input, None, Some(0), None, &[]);
assert_eq_slice(&input, None, Some(1), None, &['a']);
assert_eq_slice(&input, None, Some(4), None, &['a', 'b', 'c', 'd']);
assert_eq_slice(&input, None, Some(5), None, &['a', 'b', 'c', 'd', 'e']);
assert_eq_slice(&input, None, Some(6), None, &['a', 'b', 'c', 'd', 'e']);
assert_eq_slice(&input, None, None, None, &['a', 'b', 'c', 'd', 'e']);
assert_eq_slice(&db, &input, None, Some(0), None, &[]);
assert_eq_slice(&db, &input, None, Some(1), None, &['a']);
assert_eq_slice(&db, &input, None, Some(4), None, &['a', 'b', 'c', 'd']);
assert_eq_slice(&db, &input, None, Some(5), None, &['a', 'b', 'c', 'd', 'e']);
assert_eq_slice(&db, &input, None, Some(6), None, &['a', 'b', 'c', 'd', 'e']);
assert_eq_slice(&db, &input, None, None, None, &['a', 'b', 'c', 'd', 'e']);
assert_eq_slice(&input, Some(0), Some(0), None, &[]);
assert_eq_slice(&input, Some(0), Some(1), None, &['a']);
assert_eq_slice(&input, Some(0), Some(4), None, &['a', 'b', 'c', 'd']);
assert_eq_slice(&input, Some(0), Some(5), None, &['a', 'b', 'c', 'd', 'e']);
assert_eq_slice(&input, Some(0), Some(6), None, &['a', 'b', 'c', 'd', 'e']);
assert_eq_slice(&input, Some(0), None, None, &['a', 'b', 'c', 'd', 'e']);
assert_eq_slice(&db, &input, Some(0), Some(0), None, &[]);
assert_eq_slice(&db, &input, Some(0), Some(1), None, &['a']);
assert_eq_slice(&db, &input, Some(0), Some(4), None, &['a', 'b', 'c', 'd']);
assert_eq_slice(
&db,
&input,
Some(0),
Some(5),
None,
&['a', 'b', 'c', 'd', 'e'],
);
assert_eq_slice(
&db,
&input,
Some(0),
Some(6),
None,
&['a', 'b', 'c', 'd', 'e'],
);
assert_eq_slice(&db, &input, Some(0), None, None, &['a', 'b', 'c', 'd', 'e']);
assert_eq_slice(&input, Some(1), Some(0), None, &[]);
assert_eq_slice(&input, Some(1), Some(1), None, &[]);
assert_eq_slice(&input, Some(1), Some(2), None, &['b']);
assert_eq_slice(&input, Some(1), Some(4), None, &['b', 'c', 'd']);
assert_eq_slice(&input, Some(1), Some(5), None, &['b', 'c', 'd', 'e']);
assert_eq_slice(&input, Some(1), Some(6), None, &['b', 'c', 'd', 'e']);
assert_eq_slice(&input, Some(1), None, None, &['b', 'c', 'd', 'e']);
assert_eq_slice(&db, &input, Some(1), Some(0), None, &[]);
assert_eq_slice(&db, &input, Some(1), Some(1), None, &[]);
assert_eq_slice(&db, &input, Some(1), Some(2), None, &['b']);
assert_eq_slice(&db, &input, Some(1), Some(4), None, &['b', 'c', 'd']);
assert_eq_slice(&db, &input, Some(1), Some(5), None, &['b', 'c', 'd', 'e']);
assert_eq_slice(&db, &input, Some(1), Some(6), None, &['b', 'c', 'd', 'e']);
assert_eq_slice(&db, &input, Some(1), None, None, &['b', 'c', 'd', 'e']);
assert_eq_slice(&input, Some(4), Some(0), None, &[]);
assert_eq_slice(&input, Some(4), Some(4), None, &[]);
assert_eq_slice(&input, Some(4), Some(5), None, &['e']);
assert_eq_slice(&input, Some(4), Some(6), None, &['e']);
assert_eq_slice(&input, Some(4), None, None, &['e']);
assert_eq_slice(&db, &input, Some(4), Some(0), None, &[]);
assert_eq_slice(&db, &input, Some(4), Some(4), None, &[]);
assert_eq_slice(&db, &input, Some(4), Some(5), None, &['e']);
assert_eq_slice(&db, &input, Some(4), Some(6), None, &['e']);
assert_eq_slice(&db, &input, Some(4), None, None, &['e']);
assert_eq_slice(&input, Some(5), Some(0), None, &[]);
assert_eq_slice(&input, Some(5), Some(5), None, &[]);
assert_eq_slice(&input, Some(5), Some(6), None, &[]);
assert_eq_slice(&input, Some(5), None, None, &[]);
assert_eq_slice(&db, &input, Some(5), Some(0), None, &[]);
assert_eq_slice(&db, &input, Some(5), Some(5), None, &[]);
assert_eq_slice(&db, &input, Some(5), Some(6), None, &[]);
assert_eq_slice(&db, &input, Some(5), None, None, &[]);
assert_eq_slice(&input, Some(6), Some(0), None, &[]);
assert_eq_slice(&input, Some(6), Some(6), None, &[]);
assert_eq_slice(&input, Some(6), None, None, &[]);
assert_eq_slice(&db, &input, Some(6), Some(0), None, &[]);
assert_eq_slice(&db, &input, Some(6), Some(6), None, &[]);
assert_eq_slice(&db, &input, Some(6), None, None, &[]);
}
#[test]
fn py_slice_negative_indices() {
let db = setup_db();
let input = ['a', 'b', 'c', 'd', 'e'];
assert_eq_slice(&input, Some(-6), None, None, &['a', 'b', 'c', 'd', 'e']);
assert_eq_slice(&input, Some(-6), Some(-1), None, &['a', 'b', 'c', 'd']);
assert_eq_slice(&input, Some(-6), Some(-4), None, &['a']);
assert_eq_slice(&input, Some(-6), Some(-5), None, &[]);
assert_eq_slice(&input, Some(-6), Some(-6), None, &[]);
assert_eq_slice(&input, Some(-6), Some(-10), None, &[]);
assert_eq_slice(
&db,
&input,
Some(-6),
None,
None,
&['a', 'b', 'c', 'd', 'e'],
);
assert_eq_slice(&db, &input, Some(-6), Some(-1), None, &['a', 'b', 'c', 'd']);
assert_eq_slice(&db, &input, Some(-6), Some(-4), None, &['a']);
assert_eq_slice(&db, &input, Some(-6), Some(-5), None, &[]);
assert_eq_slice(&db, &input, Some(-6), Some(-6), None, &[]);
assert_eq_slice(&db, &input, Some(-6), Some(-10), None, &[]);
assert_eq_slice(&input, Some(-5), None, None, &['a', 'b', 'c', 'd', 'e']);
assert_eq_slice(&input, Some(-5), Some(-1), None, &['a', 'b', 'c', 'd']);
assert_eq_slice(&input, Some(-5), Some(-4), None, &['a']);
assert_eq_slice(&input, Some(-5), Some(-5), None, &[]);
assert_eq_slice(&input, Some(-5), Some(-6), None, &[]);
assert_eq_slice(&input, Some(-5), Some(-10), None, &[]);
assert_eq_slice(
&db,
&input,
Some(-5),
None,
None,
&['a', 'b', 'c', 'd', 'e'],
);
assert_eq_slice(&db, &input, Some(-5), Some(-1), None, &['a', 'b', 'c', 'd']);
assert_eq_slice(&db, &input, Some(-5), Some(-4), None, &['a']);
assert_eq_slice(&db, &input, Some(-5), Some(-5), None, &[]);
assert_eq_slice(&db, &input, Some(-5), Some(-6), None, &[]);
assert_eq_slice(&db, &input, Some(-5), Some(-10), None, &[]);
assert_eq_slice(&input, Some(-4), None, None, &['b', 'c', 'd', 'e']);
assert_eq_slice(&input, Some(-4), Some(-1), None, &['b', 'c', 'd']);
assert_eq_slice(&input, Some(-4), Some(-3), None, &['b']);
assert_eq_slice(&input, Some(-4), Some(-4), None, &[]);
assert_eq_slice(&input, Some(-4), Some(-10), None, &[]);
assert_eq_slice(&db, &input, Some(-4), None, None, &['b', 'c', 'd', 'e']);
assert_eq_slice(&db, &input, Some(-4), Some(-1), None, &['b', 'c', 'd']);
assert_eq_slice(&db, &input, Some(-4), Some(-3), None, &['b']);
assert_eq_slice(&db, &input, Some(-4), Some(-4), None, &[]);
assert_eq_slice(&db, &input, Some(-4), Some(-10), None, &[]);
assert_eq_slice(&input, Some(-1), None, None, &['e']);
assert_eq_slice(&input, Some(-1), Some(-1), None, &[]);
assert_eq_slice(&input, Some(-1), Some(-10), None, &[]);
assert_eq_slice(&db, &input, Some(-1), None, None, &['e']);
assert_eq_slice(&db, &input, Some(-1), Some(-1), None, &[]);
assert_eq_slice(&db, &input, Some(-1), Some(-10), None, &[]);
assert_eq_slice(&input, None, Some(-1), None, &['a', 'b', 'c', 'd']);
assert_eq_slice(&input, None, Some(-4), None, &['a']);
assert_eq_slice(&input, None, Some(-5), None, &[]);
assert_eq_slice(&input, None, Some(-6), None, &[]);
assert_eq_slice(&db, &input, None, Some(-1), None, &['a', 'b', 'c', 'd']);
assert_eq_slice(&db, &input, None, Some(-4), None, &['a']);
assert_eq_slice(&db, &input, None, Some(-5), None, &[]);
assert_eq_slice(&db, &input, None, Some(-6), None, &[]);
}
#[test]
fn py_slice_mixed_positive_negative_indices() {
let db = setup_db();
let input = ['a', 'b', 'c', 'd', 'e'];
assert_eq_slice(&input, Some(0), Some(-1), None, &['a', 'b', 'c', 'd']);
assert_eq_slice(&input, Some(1), Some(-1), None, &['b', 'c', 'd']);
assert_eq_slice(&input, Some(3), Some(-1), None, &['d']);
assert_eq_slice(&input, Some(4), Some(-1), None, &[]);
assert_eq_slice(&input, Some(5), Some(-1), None, &[]);
assert_eq_slice(&db, &input, Some(0), Some(-1), None, &['a', 'b', 'c', 'd']);
assert_eq_slice(&db, &input, Some(1), Some(-1), None, &['b', 'c', 'd']);
assert_eq_slice(&db, &input, Some(3), Some(-1), None, &['d']);
assert_eq_slice(&db, &input, Some(4), Some(-1), None, &[]);
assert_eq_slice(&db, &input, Some(5), Some(-1), None, &[]);
assert_eq_slice(&input, Some(0), Some(-4), None, &['a']);
assert_eq_slice(&input, Some(1), Some(-4), None, &[]);
assert_eq_slice(&input, Some(3), Some(-4), None, &[]);
assert_eq_slice(&db, &input, Some(0), Some(-4), None, &['a']);
assert_eq_slice(&db, &input, Some(1), Some(-4), None, &[]);
assert_eq_slice(&db, &input, Some(3), Some(-4), None, &[]);
assert_eq_slice(&input, Some(0), Some(-5), None, &[]);
assert_eq_slice(&input, Some(1), Some(-5), None, &[]);
assert_eq_slice(&input, Some(3), Some(-5), None, &[]);
assert_eq_slice(&db, &input, Some(0), Some(-5), None, &[]);
assert_eq_slice(&db, &input, Some(1), Some(-5), None, &[]);
assert_eq_slice(&db, &input, Some(3), Some(-5), None, &[]);
assert_eq_slice(&input, Some(0), Some(-6), None, &[]);
assert_eq_slice(&input, Some(1), Some(-6), None, &[]);
assert_eq_slice(&db, &input, Some(0), Some(-6), None, &[]);
assert_eq_slice(&db, &input, Some(1), Some(-6), None, &[]);
assert_eq_slice(&input, Some(-6), Some(6), None, &['a', 'b', 'c', 'd', 'e']);
assert_eq_slice(&input, Some(-6), Some(5), None, &['a', 'b', 'c', 'd', 'e']);
assert_eq_slice(&input, Some(-6), Some(4), None, &['a', 'b', 'c', 'd']);
assert_eq_slice(&input, Some(-6), Some(1), None, &['a']);
assert_eq_slice(&input, Some(-6), Some(0), None, &[]);
assert_eq_slice(
&db,
&input,
Some(-6),
Some(6),
None,
&['a', 'b', 'c', 'd', 'e'],
);
assert_eq_slice(
&db,
&input,
Some(-6),
Some(5),
None,
&['a', 'b', 'c', 'd', 'e'],
);
assert_eq_slice(&db, &input, Some(-6), Some(4), None, &['a', 'b', 'c', 'd']);
assert_eq_slice(&db, &input, Some(-6), Some(1), None, &['a']);
assert_eq_slice(&db, &input, Some(-6), Some(0), None, &[]);
assert_eq_slice(&input, Some(-5), Some(6), None, &['a', 'b', 'c', 'd', 'e']);
assert_eq_slice(&input, Some(-5), Some(5), None, &['a', 'b', 'c', 'd', 'e']);
assert_eq_slice(&input, Some(-5), Some(4), None, &['a', 'b', 'c', 'd']);
assert_eq_slice(&input, Some(-5), Some(1), None, &['a']);
assert_eq_slice(&input, Some(-5), Some(0), None, &[]);
assert_eq_slice(
&db,
&input,
Some(-5),
Some(6),
None,
&['a', 'b', 'c', 'd', 'e'],
);
assert_eq_slice(
&db,
&input,
Some(-5),
Some(5),
None,
&['a', 'b', 'c', 'd', 'e'],
);
assert_eq_slice(&db, &input, Some(-5), Some(4), None, &['a', 'b', 'c', 'd']);
assert_eq_slice(&db, &input, Some(-5), Some(1), None, &['a']);
assert_eq_slice(&db, &input, Some(-5), Some(0), None, &[]);
assert_eq_slice(&input, Some(-4), Some(6), None, &['b', 'c', 'd', 'e']);
assert_eq_slice(&input, Some(-4), Some(5), None, &['b', 'c', 'd', 'e']);
assert_eq_slice(&input, Some(-4), Some(4), None, &['b', 'c', 'd']);
assert_eq_slice(&input, Some(-4), Some(2), None, &['b']);
assert_eq_slice(&input, Some(-4), Some(1), None, &[]);
assert_eq_slice(&input, Some(-4), Some(0), None, &[]);
assert_eq_slice(&db, &input, Some(-4), Some(6), None, &['b', 'c', 'd', 'e']);
assert_eq_slice(&db, &input, Some(-4), Some(5), None, &['b', 'c', 'd', 'e']);
assert_eq_slice(&db, &input, Some(-4), Some(4), None, &['b', 'c', 'd']);
assert_eq_slice(&db, &input, Some(-4), Some(2), None, &['b']);
assert_eq_slice(&db, &input, Some(-4), Some(1), None, &[]);
assert_eq_slice(&db, &input, Some(-4), Some(0), None, &[]);
assert_eq_slice(&input, Some(-1), Some(6), None, &['e']);
assert_eq_slice(&input, Some(-1), Some(5), None, &['e']);
assert_eq_slice(&input, Some(-1), Some(4), None, &[]);
assert_eq_slice(&input, Some(-1), Some(1), None, &[]);
assert_eq_slice(&db, &input, Some(-1), Some(6), None, &['e']);
assert_eq_slice(&db, &input, Some(-1), Some(5), None, &['e']);
assert_eq_slice(&db, &input, Some(-1), Some(4), None, &[]);
assert_eq_slice(&db, &input, Some(-1), Some(1), None, &[]);
}
#[test]
fn py_slice_step_forward() {
let db = setup_db();
// indices: 0 1 2 3 4 5 6
let input = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
// Step size zero is invalid:
assert!(matches!(
input.py_slice(None, None, Some(0)),
input.py_slice(&db, None, None, Some(0)),
Err(StepSizeZeroError)
));
assert!(matches!(
input.py_slice(Some(0), Some(5), Some(0)),
input.py_slice(&db, Some(0), Some(5), Some(0)),
Err(StepSizeZeroError)
));
assert!(matches!(
input.py_slice(Some(0), Some(0), Some(0)),
input.py_slice(&db, Some(0), Some(0), Some(0)),
Err(StepSizeZeroError)
));
assert_eq_slice(&input, Some(0), Some(8), Some(2), &['a', 'c', 'e', 'g']);
assert_eq_slice(&input, Some(0), Some(7), Some(2), &['a', 'c', 'e', 'g']);
assert_eq_slice(&input, Some(0), Some(6), Some(2), &['a', 'c', 'e']);
assert_eq_slice(&input, Some(0), Some(5), Some(2), &['a', 'c', 'e']);
assert_eq_slice(&input, Some(0), Some(4), Some(2), &['a', 'c']);
assert_eq_slice(&input, Some(0), Some(3), Some(2), &['a', 'c']);
assert_eq_slice(&input, Some(0), Some(2), Some(2), &['a']);
assert_eq_slice(&input, Some(0), Some(1), Some(2), &['a']);
assert_eq_slice(&input, Some(0), Some(0), Some(2), &[]);
assert_eq_slice(&input, Some(1), Some(5), Some(2), &['b', 'd']);
assert_eq_slice(
&db,
&input,
Some(0),
Some(8),
Some(2),
&['a', 'c', 'e', 'g'],
);
assert_eq_slice(
&db,
&input,
Some(0),
Some(7),
Some(2),
&['a', 'c', 'e', 'g'],
);
assert_eq_slice(&db, &input, Some(0), Some(6), Some(2), &['a', 'c', 'e']);
assert_eq_slice(&db, &input, Some(0), Some(5), Some(2), &['a', 'c', 'e']);
assert_eq_slice(&db, &input, Some(0), Some(4), Some(2), &['a', 'c']);
assert_eq_slice(&db, &input, Some(0), Some(3), Some(2), &['a', 'c']);
assert_eq_slice(&db, &input, Some(0), Some(2), Some(2), &['a']);
assert_eq_slice(&db, &input, Some(0), Some(1), Some(2), &['a']);
assert_eq_slice(&db, &input, Some(0), Some(0), Some(2), &[]);
assert_eq_slice(&db, &input, Some(1), Some(5), Some(2), &['b', 'd']);
assert_eq_slice(&input, Some(0), Some(7), Some(3), &['a', 'd', 'g']);
assert_eq_slice(&input, Some(0), Some(6), Some(3), &['a', 'd']);
assert_eq_slice(&db, &input, Some(0), Some(7), Some(3), &['a', 'd', 'g']);
assert_eq_slice(&db, &input, Some(0), Some(6), Some(3), &['a', 'd']);
assert_eq_slice(&input, Some(0), None, Some(10), &['a']);
assert_eq_slice(&db, &input, Some(0), None, Some(10), &['a']);
}
#[test]
fn py_slice_step_backward() {
let db = setup_db();
// indices: 0 1 2 3 4 5 6
let input = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
assert_eq_slice(&input, Some(7), Some(0), Some(-2), &['g', 'e', 'c']);
assert_eq_slice(&input, Some(6), Some(0), Some(-2), &['g', 'e', 'c']);
assert_eq_slice(&input, Some(5), Some(0), Some(-2), &['f', 'd', 'b']);
assert_eq_slice(&input, Some(4), Some(0), Some(-2), &['e', 'c']);
assert_eq_slice(&input, Some(3), Some(0), Some(-2), &['d', 'b']);
assert_eq_slice(&input, Some(2), Some(0), Some(-2), &['c']);
assert_eq_slice(&input, Some(1), Some(0), Some(-2), &['b']);
assert_eq_slice(&input, Some(0), Some(0), Some(-2), &[]);
assert_eq_slice(&db, &input, Some(7), Some(0), Some(-2), &['g', 'e', 'c']);
assert_eq_slice(&db, &input, Some(6), Some(0), Some(-2), &['g', 'e', 'c']);
assert_eq_slice(&db, &input, Some(5), Some(0), Some(-2), &['f', 'd', 'b']);
assert_eq_slice(&db, &input, Some(4), Some(0), Some(-2), &['e', 'c']);
assert_eq_slice(&db, &input, Some(3), Some(0), Some(-2), &['d', 'b']);
assert_eq_slice(&db, &input, Some(2), Some(0), Some(-2), &['c']);
assert_eq_slice(&db, &input, Some(1), Some(0), Some(-2), &['b']);
assert_eq_slice(&db, &input, Some(0), Some(0), Some(-2), &[]);
assert_eq_slice(&input, Some(7), None, Some(-2), &['g', 'e', 'c', 'a']);
assert_eq_slice(&input, None, None, Some(-2), &['g', 'e', 'c', 'a']);
assert_eq_slice(&input, None, Some(0), Some(-2), &['g', 'e', 'c']);
assert_eq_slice(&db, &input, Some(7), None, Some(-2), &['g', 'e', 'c', 'a']);
assert_eq_slice(&db, &input, None, None, Some(-2), &['g', 'e', 'c', 'a']);
assert_eq_slice(&db, &input, None, Some(0), Some(-2), &['g', 'e', 'c']);
assert_eq_slice(&input, Some(5), Some(1), Some(-2), &['f', 'd']);
assert_eq_slice(&input, Some(5), Some(2), Some(-2), &['f', 'd']);
assert_eq_slice(&input, Some(5), Some(3), Some(-2), &['f']);
assert_eq_slice(&input, Some(5), Some(4), Some(-2), &['f']);
assert_eq_slice(&input, Some(5), Some(5), Some(-2), &[]);
assert_eq_slice(&db, &input, Some(5), Some(1), Some(-2), &['f', 'd']);
assert_eq_slice(&db, &input, Some(5), Some(2), Some(-2), &['f', 'd']);
assert_eq_slice(&db, &input, Some(5), Some(3), Some(-2), &['f']);
assert_eq_slice(&db, &input, Some(5), Some(4), Some(-2), &['f']);
assert_eq_slice(&db, &input, Some(5), Some(5), Some(-2), &[]);
assert_eq_slice(&input, Some(6), None, Some(-3), &['g', 'd', 'a']);
assert_eq_slice(&input, Some(6), Some(0), Some(-3), &['g', 'd']);
assert_eq_slice(&db, &input, Some(6), None, Some(-3), &['g', 'd', 'a']);
assert_eq_slice(&db, &input, Some(6), Some(0), Some(-3), &['g', 'd']);
assert_eq_slice(&input, Some(7), None, Some(-10), &['g']);
assert_eq_slice(&db, &input, Some(7), None, Some(-10), &['g']);
assert_eq_slice(&input, Some(-6), Some(-9), Some(-1), &['b', 'a']);
assert_eq_slice(&input, Some(-6), Some(-8), Some(-1), &['b', 'a']);
assert_eq_slice(&input, Some(-6), Some(-7), Some(-1), &['b']);
assert_eq_slice(&input, Some(-6), Some(-6), Some(-1), &[]);
assert_eq_slice(&db, &input, Some(-6), Some(-9), Some(-1), &['b', 'a']);
assert_eq_slice(&db, &input, Some(-6), Some(-8), Some(-1), &['b', 'a']);
assert_eq_slice(&db, &input, Some(-6), Some(-7), Some(-1), &['b']);
assert_eq_slice(&db, &input, Some(-6), Some(-6), Some(-1), &[]);
assert_eq_slice(&input, Some(-7), Some(-9), Some(-1), &['a']);
assert_eq_slice(&db, &input, Some(-7), Some(-9), Some(-1), &['a']);
assert_eq_slice(&input, Some(-8), Some(-9), Some(-1), &[]);
assert_eq_slice(&input, Some(-9), Some(-9), Some(-1), &[]);
assert_eq_slice(&db, &input, Some(-8), Some(-9), Some(-1), &[]);
assert_eq_slice(&db, &input, Some(-9), Some(-9), Some(-1), &[]);
assert_eq_slice(&input, Some(-6), Some(-2), Some(-1), &[]);
assert_eq_slice(&input, Some(-9), Some(-6), Some(-1), &[]);
assert_eq_slice(&db, &input, Some(-6), Some(-2), Some(-1), &[]);
assert_eq_slice(&db, &input, Some(-9), Some(-6), Some(-1), &[]);
}
}