use crate::values::{Value, ValueError};
fn convert_index_aux(
len: i32,
v1: Option<Value>,
default: i32,
min: i32,
max: i32,
) -> anyhow::Result<i32> {
if let Some(v) = v1 {
if v.is_none() {
Ok(default)
} else {
match v.to_int() {
Ok(x) => {
let i = if x < 0 { len + x } else { x };
if i < min {
Ok(min)
} else if i > max {
Ok(max)
} else {
Ok(i)
}
}
Err(..) => Err(ValueError::IncorrectParameterTypeWithExpected(
"none or int".to_owned(),
v.get_type().to_owned(),
)
.into()),
}
}
} else {
Ok(default)
}
}
pub(crate) fn convert_index(v: Value, len: i32) -> anyhow::Result<i32> {
match v.to_int() {
Ok(x) => {
let i = if x < 0 {
len.checked_add(x).ok_or(ValueError::IntegerOverflow)?
} else {
x
};
if i < 0 || i >= len {
Err(ValueError::IndexOutOfBound(i).into())
} else {
Ok(i)
}
}
Err(..) => Err(ValueError::IncorrectParameterTypeWithExpected(
"int".to_owned(),
v.get_type().to_owned(),
)
.into()),
}
}
pub(crate) fn convert_slice_indices(
len: i32,
start: Option<Value>,
stop: Option<Value>,
stride: Option<Value>,
) -> anyhow::Result<(i32, i32, i32)> {
let stride = match stride {
None => 1,
Some(v) if v.is_none() => 1,
Some(v) => v.to_int().map_err(|_| {
ValueError::IncorrectParameterTypeWithExpected(
"int or None".to_owned(),
v.get_type().to_owned(),
)
})?,
};
match stride {
0 => Err(ValueError::IndexOutOfBound(0).into()),
stride => {
let def_start = if stride < 0 { len - 1 } else { 0 };
let def_end = if stride < 0 { -1 } else { len };
let clamp = if stride < 0 { -1 } else { 0 };
let start = convert_index_aux(len, start, def_start, clamp, len + clamp);
let stop = convert_index_aux(len, stop, def_end, clamp, len + clamp);
match (start, stop) {
(Ok(s1), Ok(s2)) => Ok((s1, s2, stride)),
(Err(x), ..) => Err(x),
(Ok(..), Err(x)) => Err(x),
}
}
}
}
pub(crate) fn apply_slice<T: Copy>(
xs: &[T],
start: Option<Value>,
stop: Option<Value>,
stride: Option<Value>,
) -> anyhow::Result<Vec<T>> {
let (start, stop, stride) = convert_slice_indices(xs.len() as i32, start, stop, stride)?;
if stride == 1 {
if start >= stop {
return Ok(Vec::new());
} else {
return Ok(xs[start as usize..stop as usize].to_vec());
}
}
let (start, stop) = if stride < 0 {
(stop + 1, start + 1)
} else {
(start, stop)
};
if start >= stop {
return Ok(Vec::new());
}
let mut res = xs[start as usize..stop as usize].to_vec();
if stride == -1 {
res.reverse();
return Ok(res);
}
if stride < 0 {
res.reverse();
}
let astride = stride.abs();
let res = res
.into_iter()
.enumerate()
.filter_map(|x| {
if 0 == (x.0 as i32 % astride) {
Some(x.1)
} else {
None
}
})
.collect();
Ok(res)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::values::Heap;
#[test]
fn test_convert_index() {
let heap = Heap::new();
assert_eq!(Some(6), convert_index(Value::new_int(6), 7).ok());
assert_eq!(Some(6), convert_index(Value::new_int(-1), 7).ok());
assert_eq!(
Some((6, 7, 1)),
convert_slice_indices(7, Some(Value::new_int(6)), None, None).ok()
);
assert_eq!(
Some((6, -1, -1)),
convert_slice_indices(7, Some(Value::new_int(-1)), None, Some(Value::new_int(-1))).ok()
);
assert_eq!(
Some((6, 7, 1)),
convert_slice_indices(7, Some(Value::new_int(-1)), Some(Value::new_int(10)), None).ok()
);
assert!(convert_index(heap.alloc("a"), 7).is_err());
assert!(convert_index(Value::new_int(8), 7).is_err()); assert!(convert_index(Value::new_int(-8), 7).is_err()); }
#[test]
fn test_apply_slice() {
let s = &[0, 1, 2, 3, 4, 5, 6];
let x = apply_slice(s, Some(Value::new_int(-1)), None, Some(Value::new_int(-1))).unwrap();
assert_eq!(x, &[6, 5, 4, 3, 2, 1, 0]);
let x = apply_slice(s, Some(Value::new_int(-1)), None, Some(Value::new_int(-1))).unwrap();
assert_eq!(x, &[6, 5, 4, 3, 2, 1, 0]);
let x = apply_slice(
s,
Some(Value::new_int(0)),
Some(Value::new_int(3)),
Some(Value::new_int(2)),
)
.unwrap();
assert_eq!(x, &[0, 2]);
let x = apply_slice(
s,
Some(Value::new_int(5)),
Some(Value::new_int(2)),
Some(Value::new_int(-2)),
)
.unwrap();
assert_eq!(x, &[5, 3]);
let x = apply_slice(
s,
Some(Value::new_int(-1)),
Some(Value::new_int(-5)),
Some(Value::new_int(-1)),
)
.unwrap();
assert_eq!(x, &[6, 5, 4, 3]);
let x = apply_slice(
s,
Some(Value::new_int(-1)),
Some(Value::new_int(0)),
Some(Value::new_int(-1)),
)
.unwrap();
assert_eq!(x, &[6, 5, 4, 3, 2, 1]);
let x = apply_slice(
&[1, 2, 3],
Some(Value::new_int(0)),
Some(Value::new_int(-2)),
Some(Value::new_int(-1)),
)
.unwrap();
assert_eq!(x, &[] as &[i32]);
}
}