use std::cmp::max;
use std::cmp::min;
use std::cmp::Ordering;
use num_traits::ToPrimitive;
use crate::spec::function::FunctionRegistry;
use crate::spec::query::Queryable;
use crate::ConcreteVariantArray;
use crate::LocatedNode;
use crate::NormalizedPath;
use crate::VariantValue;
#[derive(Debug, PartialEq, Eq, Default, Clone, Copy)]
pub struct Slice {
start: Option<i64>,
end: Option<i64>,
step: Option<i64>,
}
impl std::fmt::Display for Slice {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if let Some(start) = self.start {
write!(f, "{start}")?;
}
write!(f, ":")?;
if let Some(end) = self.end {
write!(f, "{end}")?;
}
write!(f, ":")?;
if let Some(step) = self.step {
write!(f, "{step}")?;
}
Ok(())
}
}
fn bounds(start: i64, end: i64, step: i64, len: i64) -> (i64, i64) {
fn normalize(i: i64, len: i64) -> i64 {
if i < 0 {
len + i
} else {
i
}
}
let start = normalize(start, len);
let end = normalize(end, len);
if step >= 0 {
let lower = min(max(start, 0), len);
let upper = min(max(end, 0), len);
(lower, upper)
} else {
let upper = min(max(start, -1), len - 1);
let lower = min(max(end, -1), len - 1);
(lower, upper)
}
}
impl Slice {
pub fn new(start: Option<i64>, end: Option<i64>, step: Option<i64>) -> Self {
Self { start, end, step }
}
fn select<'b, T, N, F>(&self, current: &'b T, make_node: F) -> Vec<N>
where
T: VariantValue,
N: 'b,
F: Fn(usize, &'b T) -> N,
{
let vec = match current.as_array() {
Some(vec) => vec,
None => return vec![],
};
let (start, end, step) = (self.start, self.end, self.step.unwrap_or(1));
if step == 0 {
return vec![];
}
let len = vec.len().to_i64().unwrap_or(i64::MAX);
let (start, end) = if step >= 0 {
match (start, end) {
(Some(start), Some(end)) => (start, end),
(Some(start), None) => (start, len),
(None, Some(end)) => (0, end),
(None, None) => (0, len),
}
} else {
match (start, end) {
(Some(start), Some(end)) => (start, end),
(Some(start), None) => (start, -len - 1),
(None, Some(end)) => (len - 1, end),
(None, None) => (len - 1, -len - 1),
}
};
let (lower, upper) = bounds(start, end, step, len);
let mut selected = vec![];
match step.cmp(&0) {
Ordering::Greater => {
let mut i = lower;
while i < upper {
let node = vec.get(i as usize).unwrap();
selected.push(make_node(i as usize, node));
i += step;
}
}
Ordering::Less => {
let mut i = upper;
while lower < i {
let node = vec.get(i as usize).unwrap();
selected.push(make_node(i as usize, node));
i += step;
}
}
Ordering::Equal => unreachable!("step is guaranteed not zero here"),
}
selected
}
}
impl Queryable for Slice {
fn query<'b, T: VariantValue, Registry: FunctionRegistry<Value = T>>(
&self,
current: &'b T,
_root: &'b T,
_registry: &Registry,
) -> Vec<&'b T> {
self.select(current, |_, node| node)
}
fn query_located<'b, T: VariantValue, Registry: FunctionRegistry<Value = T>>(
&self,
current: &'b T,
_root: &'b T,
_registry: &Registry,
parent: NormalizedPath<'b>,
) -> Vec<LocatedNode<'b, T>> {
self.select(current, |i, node| {
LocatedNode::new(parent.clone_and_push(i), node)
})
}
}