use crate::distinct_functions::normalize_collection_result;
use crate::evaluator::EvaluationContext;
use helios_fhirpath_support::EvaluationError;
use helios_fhirpath_support::EvaluationResult;
use rust_decimal::prelude::ToPrimitive;
pub fn skip_function(
invocation_base: &EvaluationResult,
num_arg: &EvaluationResult,
context: &EvaluationContext,
) -> Result<EvaluationResult, EvaluationError> {
let num_to_skip = match num_arg {
EvaluationResult::Integer(i, _, _) => {
if *i < 0 { 0 } else { *i as usize } }
EvaluationResult::Decimal(d, _, _) if d.is_integer() && d.is_sign_positive() => {
d.to_usize().unwrap_or(0) }
_ => {
return Err(EvaluationError::InvalidArgument(
"skip argument must be a non-negative integer".to_string(),
));
}
};
let (items, input_was_unordered) = match invocation_base {
EvaluationResult::Collection {
items,
has_undefined_order,
..
} => {
if *has_undefined_order && context.check_ordered_functions {
return Err(EvaluationError::SemanticError(
"skip() operation on collection with undefined order is not allowed when checkOrderedFunctions is true.".to_string()
));
}
(items.clone(), *has_undefined_order)
}
EvaluationResult::Empty => (vec![], false), single_item => (vec![single_item.clone()], false), };
Ok(if num_to_skip >= items.len() {
EvaluationResult::Empty
} else {
let skipped_items = items[num_to_skip..].to_vec();
normalize_collection_result(skipped_items, input_was_unordered)
})
}
pub fn tail_function(
invocation_base: &EvaluationResult,
context: &EvaluationContext,
) -> Result<EvaluationResult, EvaluationError> {
if let EvaluationResult::Collection {
has_undefined_order,
..
} = invocation_base
{
if *has_undefined_order && context.check_ordered_functions {
return Err(EvaluationError::SemanticError(
"tail() operation on collection with undefined order is not allowed when checkOrderedFunctions is true.".to_string()
));
}
}
let input_was_unordered = if let EvaluationResult::Collection {
has_undefined_order,
..
} = invocation_base
{
*has_undefined_order
} else {
false
};
Ok(
if let EvaluationResult::Collection { items, .. } = invocation_base {
if items.len() > 1 {
EvaluationResult::Collection {
items: items[1..].to_vec(), has_undefined_order: input_was_unordered,
type_info: None,
}
} else {
EvaluationResult::Empty }
} else {
EvaluationResult::Empty },
)
}
pub fn take_function(
invocation_base: &EvaluationResult,
num_arg: &EvaluationResult,
context: &EvaluationContext,
) -> Result<EvaluationResult, EvaluationError> {
let num_to_take = match num_arg {
EvaluationResult::Integer(i, _, _) => {
if *i <= 0 { 0 } else { *i as usize } }
EvaluationResult::Decimal(d, _, _) if d.is_integer() && d.is_sign_positive() => {
d.to_usize().unwrap_or(0) }
_ => {
return Err(EvaluationError::InvalidArgument(
"take argument must be a non-negative integer".to_string(),
));
}
};
if num_to_take == 0 {
return Ok(EvaluationResult::Empty);
}
let (items, input_was_unordered) = match invocation_base {
EvaluationResult::Collection {
items,
has_undefined_order,
..
} => {
if *has_undefined_order && context.check_ordered_functions {
return Err(EvaluationError::SemanticError(
"take() operation on collection with undefined order is not allowed when checkOrderedFunctions is true.".to_string()
));
}
(items.clone(), *has_undefined_order)
}
EvaluationResult::Empty => (vec![], false), single_item => (vec![single_item.clone()], false), };
let taken_items: Vec<EvaluationResult> = items.into_iter().take(num_to_take).collect();
Ok(normalize_collection_result(
taken_items,
input_was_unordered,
))
}
#[cfg(test)]
mod tests {
use super::*;
use rust_decimal::Decimal;
#[test]
fn test_skip_basic() {
let collection = EvaluationResult::Collection {
items: vec![
EvaluationResult::integer(1),
EvaluationResult::integer(2),
EvaluationResult::integer(3),
],
has_undefined_order: false,
type_info: None,
};
let context = EvaluationContext::new_empty_with_default_version();
let num = EvaluationResult::integer(1);
let result = skip_function(&collection, &num, &context).unwrap();
let expected = EvaluationResult::Collection {
items: vec![EvaluationResult::integer(2), EvaluationResult::integer(3)],
has_undefined_order: false,
type_info: None,
};
assert_eq!(result, expected);
}
#[test]
fn test_skip_zero() {
let collection = EvaluationResult::Collection {
items: vec![
EvaluationResult::integer(1),
EvaluationResult::integer(2),
EvaluationResult::integer(3),
],
has_undefined_order: false,
type_info: None,
};
let context = EvaluationContext::new_empty_with_default_version();
let num = EvaluationResult::integer(0);
let result = skip_function(&collection, &num, &context).unwrap();
assert_eq!(result, collection);
}
#[test]
fn test_skip_negative() {
let collection = EvaluationResult::Collection {
items: vec![
EvaluationResult::integer(1),
EvaluationResult::integer(2),
EvaluationResult::integer(3),
],
has_undefined_order: false,
type_info: None,
};
let context = EvaluationContext::new_empty_with_default_version();
let num = EvaluationResult::integer(-1);
let result = skip_function(&collection, &num, &context).unwrap();
assert_eq!(result, collection);
}
#[test]
fn test_skip_all() {
let collection = EvaluationResult::Collection {
items: vec![
EvaluationResult::integer(1),
EvaluationResult::integer(2),
EvaluationResult::integer(3),
],
has_undefined_order: false,
type_info: None,
};
let context = EvaluationContext::new_empty_with_default_version();
let num = EvaluationResult::integer(3);
let result = skip_function(&collection, &num, &context).unwrap();
assert_eq!(result, EvaluationResult::Empty);
}
#[test]
fn test_skip_beyond() {
let collection = EvaluationResult::Collection {
items: vec![
EvaluationResult::integer(1),
EvaluationResult::integer(2),
EvaluationResult::integer(3),
],
has_undefined_order: false,
type_info: None,
};
let context = EvaluationContext::new_empty_with_default_version();
let num = EvaluationResult::integer(4);
let result = skip_function(&collection, &num, &context).unwrap();
assert_eq!(result, EvaluationResult::Empty);
}
#[test]
fn test_skip_single_item() {
let single = EvaluationResult::integer(42);
let context = EvaluationContext::new_empty_with_default_version();
let num = EvaluationResult::integer(1);
let result = skip_function(&single, &num, &context).unwrap();
assert_eq!(result, EvaluationResult::Empty);
let num = EvaluationResult::integer(0);
let result = skip_function(&single, &num, &context).unwrap();
let expected = EvaluationResult::integer(42);
assert_eq!(result, expected);
}
#[test]
fn test_skip_empty() {
let empty = EvaluationResult::Empty;
let context = EvaluationContext::new_empty_with_default_version();
let num = EvaluationResult::integer(1);
let result = skip_function(&empty, &num, &context).unwrap();
assert_eq!(result, EvaluationResult::Empty);
}
#[test]
fn test_skip_decimal() {
let collection = EvaluationResult::Collection {
items: vec![
EvaluationResult::integer(1),
EvaluationResult::integer(2),
EvaluationResult::integer(3),
],
has_undefined_order: false,
type_info: None,
};
let context = EvaluationContext::new_empty_with_default_version();
let num = EvaluationResult::decimal(Decimal::from(2));
let result = skip_function(&collection, &num, &context).unwrap();
assert_eq!(result, EvaluationResult::integer(3));
}
#[test]
fn test_skip_invalid_arg() {
let collection = EvaluationResult::Collection {
items: vec![
EvaluationResult::integer(1),
EvaluationResult::integer(2),
EvaluationResult::integer(3),
],
has_undefined_order: false,
type_info: None,
};
let context = EvaluationContext::new_empty_with_default_version();
let num = EvaluationResult::string("not a number".to_string());
let result = skip_function(&collection, &num, &context);
assert!(result.is_err());
if let Err(EvaluationError::InvalidArgument(msg)) = result {
assert!(msg.contains("non-negative integer"));
} else {
panic!("Expected InvalidArgument error");
}
}
#[test]
fn test_tail_basic() {
let collection = EvaluationResult::Collection {
items: vec![
EvaluationResult::integer(1),
EvaluationResult::integer(2),
EvaluationResult::integer(3),
],
has_undefined_order: false,
type_info: None,
};
let context = EvaluationContext::new_empty_with_default_version();
let result = tail_function(&collection, &context).unwrap();
let expected = EvaluationResult::Collection {
items: vec![EvaluationResult::integer(2), EvaluationResult::integer(3)],
has_undefined_order: false,
type_info: None,
};
assert_eq!(result, expected);
}
#[test]
fn test_tail_single_item() {
let collection = EvaluationResult::Collection {
items: vec![EvaluationResult::integer(1)],
has_undefined_order: false,
type_info: None,
};
let context = EvaluationContext::new_empty_with_default_version();
let result = tail_function(&collection, &context).unwrap();
assert_eq!(result, EvaluationResult::Empty);
}
#[test]
fn test_tail_empty() {
let empty = EvaluationResult::Empty;
let context = EvaluationContext::new_empty_with_default_version();
let result = tail_function(&empty, &context).unwrap();
assert_eq!(result, EvaluationResult::Empty);
}
#[test]
fn test_tail_single_value() {
let single = EvaluationResult::integer(42);
let context = EvaluationContext::new_empty_with_default_version();
let result = tail_function(&single, &context).unwrap();
assert_eq!(result, EvaluationResult::Empty);
}
#[test]
fn test_take_basic() {
let collection = EvaluationResult::Collection {
items: vec![
EvaluationResult::integer(1),
EvaluationResult::integer(2),
EvaluationResult::integer(3),
],
has_undefined_order: false,
type_info: None,
};
let context = EvaluationContext::new_empty_with_default_version();
let num = EvaluationResult::integer(2);
let result = take_function(&collection, &num, &context).unwrap();
let expected = EvaluationResult::Collection {
items: vec![EvaluationResult::integer(1), EvaluationResult::integer(2)],
has_undefined_order: false,
type_info: None,
};
assert_eq!(result, expected);
}
#[test]
fn test_take_zero() {
let collection = EvaluationResult::Collection {
items: vec![
EvaluationResult::integer(1),
EvaluationResult::integer(2),
EvaluationResult::integer(3),
],
has_undefined_order: false,
type_info: None,
};
let context = EvaluationContext::new_empty_with_default_version();
let num = EvaluationResult::integer(0);
let result = take_function(&collection, &num, &context).unwrap();
assert_eq!(result, EvaluationResult::Empty);
}
#[test]
fn test_take_negative() {
let collection = EvaluationResult::Collection {
items: vec![
EvaluationResult::integer(1),
EvaluationResult::integer(2),
EvaluationResult::integer(3),
],
has_undefined_order: false,
type_info: None,
};
let context = EvaluationContext::new_empty_with_default_version();
let num = EvaluationResult::integer(-1);
let result = take_function(&collection, &num, &context).unwrap();
assert_eq!(result, EvaluationResult::Empty);
}
#[test]
fn test_take_all() {
let collection = EvaluationResult::Collection {
items: vec![
EvaluationResult::integer(1),
EvaluationResult::integer(2),
EvaluationResult::integer(3),
],
has_undefined_order: false,
type_info: None,
};
let context = EvaluationContext::new_empty_with_default_version();
let num = EvaluationResult::integer(3);
let result = take_function(&collection, &num, &context).unwrap();
assert_eq!(result, collection);
}
#[test]
fn test_take_beyond() {
let collection = EvaluationResult::Collection {
items: vec![
EvaluationResult::integer(1),
EvaluationResult::integer(2),
EvaluationResult::integer(3),
],
has_undefined_order: false,
type_info: None,
};
let context = EvaluationContext::new_empty_with_default_version();
let num = EvaluationResult::integer(4);
let result = take_function(&collection, &num, &context).unwrap();
assert_eq!(result, collection);
}
#[test]
fn test_take_single_item() {
let single = EvaluationResult::integer(42);
let context = EvaluationContext::new_empty_with_default_version();
let num = EvaluationResult::integer(1);
let result = take_function(&single, &num, &context).unwrap();
assert_eq!(result, single);
let num = EvaluationResult::integer(0);
let result = take_function(&single, &num, &context).unwrap();
assert_eq!(result, EvaluationResult::Empty);
}
#[test]
fn test_take_empty() {
let empty = EvaluationResult::Empty;
let context = EvaluationContext::new_empty_with_default_version();
let num = EvaluationResult::integer(1);
let result = take_function(&empty, &num, &context).unwrap();
assert_eq!(result, EvaluationResult::Empty);
}
#[test]
fn test_take_decimal() {
let collection = EvaluationResult::Collection {
items: vec![
EvaluationResult::integer(1),
EvaluationResult::integer(2),
EvaluationResult::integer(3),
],
has_undefined_order: false,
type_info: None,
};
let context = EvaluationContext::new_empty_with_default_version();
let num = EvaluationResult::decimal(Decimal::from(2));
let result = take_function(&collection, &num, &context).unwrap();
let expected = EvaluationResult::Collection {
items: vec![EvaluationResult::integer(1), EvaluationResult::integer(2)],
has_undefined_order: false,
type_info: None,
};
assert_eq!(result, expected);
}
#[test]
fn test_take_invalid_arg() {
let collection = EvaluationResult::Collection {
items: vec![
EvaluationResult::integer(1),
EvaluationResult::integer(2),
EvaluationResult::integer(3),
],
has_undefined_order: false,
type_info: None,
};
let context = EvaluationContext::new_empty_with_default_version();
let num = EvaluationResult::string("not a number".to_string());
let result = take_function(&collection, &num, &context);
assert!(result.is_err());
if let Err(EvaluationError::InvalidArgument(msg)) = result {
assert!(msg.contains("non-negative integer"));
} else {
panic!("Expected InvalidArgument error");
}
}
}