use hurl_core::ast::{IntegerValue, Placeholder, SourceInfo};
use crate::runner::{Number, RunnerError, RunnerErrorKind, Value, VariableSet, expr};
pub fn eval_nth(
value: &Value,
n: &IntegerValue,
variables: &VariableSet,
source_info: SourceInfo,
assert: bool,
) -> Result<Option<Value>, RunnerError> {
let n = eval_integer_value(n, variables)?;
match value {
Value::List(values) => match try_nth(values, n) {
Ok(value) => Ok(Some(value.clone())),
Err(err) => {
let kind = RunnerErrorKind::FilterInvalidInputValue(err);
Err(RunnerError::new(source_info, kind, assert))
}
},
v => {
let kind = RunnerErrorKind::FilterInvalidInputType {
actual: v.kind().to_string(),
expected: "list".to_string(),
};
Err(RunnerError::new(source_info, kind, assert))
}
}
}
fn try_nth<U>(items: &[U], index: i64) -> Result<&U, String> {
let len = items.len() as i64;
let value = if index >= 0 && index < len {
&items[index as usize]
} else if index < 0 && len - index.abs() >= 0 {
&items[(len - index.abs()) as usize]
} else {
let error = format!("out of bound - size is {len}");
return Err(error);
};
Ok(value)
}
fn eval_integer_value(n: &IntegerValue, variables: &VariableSet) -> Result<i64, RunnerError> {
match n {
IntegerValue::Literal(value) => Ok(value.as_i64()),
IntegerValue::Placeholder(Placeholder { expr, .. }) => match expr::eval(expr, variables)? {
Value::Number(Number::Integer(value)) => Ok(value),
v => {
let kind = RunnerErrorKind::ExpressionInvalidType {
value: v.repr(),
expecting: "integer".to_string(),
};
Err(RunnerError::new(expr.source_info, kind, false))
}
},
}
}
#[cfg(test)]
mod tests {
use hurl_core::ast::{Filter, FilterValue, I64, IntegerValue, SourceInfo, Whitespace};
use hurl_core::reader::Pos;
use hurl_core::types::ToSource;
use crate::runner::filter::eval::eval_filter;
use crate::runner::filter::nth::try_nth;
use crate::runner::{Number, RunnerError, RunnerErrorKind, Value, VariableSet};
#[test]
fn eval_filter_nth_positive() {
let variables = VariableSet::new();
let filter = Filter {
source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)),
value: FilterValue::Nth {
n: IntegerValue::Literal(I64::new(2, "2".to_source())),
space0: Whitespace {
value: String::new(),
source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
},
},
};
assert_eq!(
eval_filter(
&filter,
&Value::List(vec![
Value::Number(Number::Integer(0)),
Value::Number(Number::Integer(1)),
Value::Number(Number::Integer(2)),
Value::Number(Number::Integer(3))
]),
&variables,
false
)
.unwrap()
.unwrap(),
Value::Number(Number::Integer(2))
);
assert_eq!(
eval_filter(
&filter,
&Value::List(vec![
Value::Number(Number::Integer(0)),
Value::Number(Number::Integer(1))
]),
&variables,
false
)
.err()
.unwrap(),
RunnerError::new(
SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)),
RunnerErrorKind::FilterInvalidInputValue("out of bound - size is 2".to_string()),
false
)
);
}
#[test]
fn eval_filter_nth_negative() {
let variables = VariableSet::new();
let filter = Filter {
source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)),
value: FilterValue::Nth {
n: IntegerValue::Literal(I64::new(-4, "-4".to_source())),
space0: Whitespace {
value: String::new(),
source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
},
},
};
assert_eq!(
eval_filter(
&filter,
&Value::List(vec![
Value::Number(Number::Integer(0)),
Value::Number(Number::Integer(1)),
Value::Number(Number::Integer(2)),
Value::Number(Number::Integer(3))
]),
&variables,
false
)
.unwrap()
.unwrap(),
Value::Number(Number::Integer(0))
);
assert_eq!(
eval_filter(
&filter,
&Value::List(vec![Value::Number(Number::Integer(0))]),
&variables,
false
)
.err()
.unwrap(),
RunnerError::new(
SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)),
RunnerErrorKind::FilterInvalidInputValue("out of bound - size is 1".to_string()),
false
)
);
}
#[test]
fn test_try_nth() {
let values = [12, 4, 5];
assert_eq!(try_nth(&values, 0).unwrap(), &12);
assert_eq!(try_nth(&values, 2).unwrap(), &5);
assert_eq!(try_nth(&values, 3).unwrap_err(), "out of bound - size is 3");
assert_eq!(try_nth(&values, -1).unwrap(), &5);
assert_eq!(try_nth(&values, -3).unwrap(), &12);
assert_eq!(try_nth(&values, 4).unwrap_err(), "out of bound - size is 3");
}
}