use std::rc::Rc;
use ibig::IBig;
use xee_xpath_macros::xpath_fn;
use crate::atomic;
use crate::context;
use crate::error;
use crate::function;
use crate::function::StaticFunctionDescription;
use crate::interpreter::Interpreter;
use crate::sequence;
use crate::string::Collation;
use crate::wrap_xpath_fn;
#[xpath_fn("array:get($array as array(*), $position as xs:integer) as item()*")]
fn get(array: function::Array, position: IBig) -> error::Result<sequence::Sequence> {
let position = convert_position(position)?;
let item = array
.index(position as usize)
.ok_or(error::Error::FOAY0001)?;
Ok(item.clone())
}
#[xpath_fn("array:size($array as array(*)) as xs:integer")]
fn size(array: function::Array) -> IBig {
array.len().into()
}
#[xpath_fn(
"array:put($array as array(*), $position as xs:integer, $member as item()*) as array(*)"
)]
fn put(
array: function::Array,
position: IBig,
member: &sequence::Sequence,
) -> error::Result<function::Array> {
let position = convert_position(position)?;
array.put(position, member).ok_or(error::Error::FOAY0001)
}
#[xpath_fn("array:append($array as array(*), $appendage as item()*) as array(*)")]
fn append(array: function::Array, appendage: &sequence::Sequence) -> function::Array {
array.append(appendage)
}
#[xpath_fn("array:subarray($array as array(*), $start as xs:integer) as array(*)")]
fn subarray2(array: function::Array, start: IBig) -> error::Result<function::Array> {
let start = convert_position(start)?;
let length = array.len() - start;
array.subarray(start, length).ok_or(error::Error::FOAY0001)
}
#[xpath_fn(
"array:subarray($array as array(*), $start as xs:integer, $length as xs:integer) as array(*)"
)]
fn subarray3(array: function::Array, start: IBig, length: IBig) -> error::Result<function::Array> {
let start = convert_position(start)?;
let length = convert_length(length)?;
array.subarray(start, length).ok_or(error::Error::FOAY0001)
}
#[xpath_fn("array:remove($array as array(*), $positions as xs:integer*) as array(*)")]
fn remove(
array: function::Array,
positions: impl Iterator<Item = error::Result<IBig>>,
) -> error::Result<function::Array> {
let positions = positions
.map(|position| convert_position(position?.clone()))
.collect::<error::Result<Vec<usize>>>()?;
array
.remove_positions(&positions)
.ok_or(error::Error::FOAY0001)
}
#[xpath_fn("array:insert-before($array as array(*), $position as xs:integer, $member as item()*) as array(*)")]
fn insert_before(
array: function::Array,
position: IBig,
member: &sequence::Sequence,
) -> error::Result<function::Array> {
let position = convert_position(position)?;
array
.insert_before(position, member)
.ok_or(error::Error::FOAY0001)
}
#[xpath_fn("array:head($array as array(*)) as item()*")]
fn head(array: function::Array) -> error::Result<sequence::Sequence> {
let item = array.index(0).ok_or(error::Error::FOAY0001)?;
Ok(item.clone())
}
#[xpath_fn("array:tail($array as array(*)) as item()*")]
fn tail(array: function::Array) -> error::Result<function::Array> {
if !array.is_empty() {
array
.subarray(1, array.len() - 1)
.ok_or(error::Error::FOAY0001)
} else {
Err(error::Error::FOAY0001)
}
}
#[xpath_fn("array:reverse($array as array(*)) as array(*)")]
fn reverse(array: function::Array) -> function::Array {
array.reversed()
}
#[xpath_fn("array:join($arrays as array(*)*) as array(*)")]
fn join(
arrays: impl Iterator<Item = error::Result<function::Array>>,
) -> error::Result<function::Array> {
let arrays = arrays.collect::<error::Result<Vec<function::Array>>>()?;
Ok(function::Array::join(&arrays))
}
#[xpath_fn(
"array:for-each($array as array(*), $action as function(item()*) as item()*) as array(*)"
)]
fn for_each(
interpreter: &mut Interpreter,
array: function::Array,
action: sequence::Item,
) -> error::Result<function::Array> {
let function = action.to_function()?;
let mut result = Vec::with_capacity(array.len());
for sequence in array.iter() {
let sequence = interpreter.call_function_with_arguments(&function, &[sequence.clone()])?;
result.push(sequence);
}
Ok(function::Array::new(result))
}
#[xpath_fn(
"array:filter($array as array(*), $function as function(item()*) as xs:boolean) as array(*)"
)]
fn filter(
interpreter: &mut Interpreter,
array: function::Array,
function: sequence::Item,
) -> error::Result<function::Array> {
let function = function.to_function()?;
let mut result = Vec::with_capacity(array.len());
for sequence in array.iter() {
let include = interpreter.call_function_with_arguments(&function, &[sequence.clone()])?;
let include: atomic::Atomic = sequence::one(include.iter())?.to_atomic()?;
let include: bool = include.try_into()?;
if include {
result.push(sequence.clone());
}
}
Ok(function::Array::new(result))
}
#[xpath_fn("array:fold-left($array as array(*), $zero as item()*, $function as function(item()*, item()*) as item()*) as item()*")]
fn fold_left(
interpreter: &mut Interpreter,
array: function::Array,
zero: &sequence::Sequence,
function: sequence::Item,
) -> error::Result<sequence::Sequence> {
let function = function.to_function()?;
let mut accumulator = zero.clone();
for sequence in array.iter() {
accumulator = interpreter
.call_function_with_arguments(&function, &[accumulator, sequence.clone()])?;
}
Ok(accumulator)
}
#[xpath_fn("array:fold-right($array as array(*), $zero as item()*, $function as function(item()*, item()*) as item()*) as item()*")]
fn fold_right(
interpreter: &mut Interpreter,
array: function::Array,
zero: &sequence::Sequence,
function: sequence::Item,
) -> error::Result<sequence::Sequence> {
let function = function.to_function()?;
let mut accumulator = zero.clone();
for sequence in array.iter().rev() {
accumulator = interpreter
.call_function_with_arguments(&function, &[sequence.clone(), accumulator])?;
}
Ok(accumulator)
}
#[xpath_fn("array:for-each-pair($array1 as array(*), $array2 as array(*), $function as function(item()*, item()*) as item()*) as array(*)")]
fn for_each_pair(
interpreter: &mut Interpreter,
array1: function::Array,
array2: function::Array,
function: sequence::Item,
) -> error::Result<function::Array> {
let function = function.to_function()?;
let mut result = Vec::with_capacity(array1.len().min(array2.len()));
for (sequence1, sequence2) in array1.iter().zip(array2.iter()) {
let sequence = interpreter
.call_function_with_arguments(&function, &[sequence1.clone(), sequence2.clone()])?;
result.push(sequence);
}
Ok(function::Array::new(result))
}
#[xpath_fn("array:sort($array as array(*)) as array(*)")]
fn sort1(
context: &context::DynamicContext,
interpreter: &Interpreter,
input: function::Array,
) -> error::Result<function::Array> {
let collation = context.static_context().default_collation()?;
sort_without_key(context, interpreter, input, collation)
}
#[xpath_fn("array:sort($array as array(*), $collation as xs:string?) as array(*)")]
fn sort2(
context: &context::DynamicContext,
interpreter: &Interpreter,
input: function::Array,
collation: Option<&str>,
) -> error::Result<function::Array> {
let collation = context.static_context().resolve_collation_str(collation)?;
sort_without_key(context, interpreter, input, collation)
}
#[xpath_fn("array:sort($array as array(*), $collation as xs:string?, $key as function(item()*) as xs:anyAtomicType*) as array(*)")]
fn sort3(
context: &context::DynamicContext,
interpreter: &mut Interpreter,
input: function::Array,
collation: Option<&str>,
key: sequence::Item,
) -> error::Result<function::Array> {
let collation = context.static_context().resolve_collation_str(collation)?;
let function = key.to_function()?;
sort_by_sequence(context, input, collation, |sequence| {
let new_sequence =
interpreter.call_function_with_arguments(&function, &[sequence.clone()])?;
Ok(new_sequence)
})
}
fn sort_without_key(
context: &context::DynamicContext,
interpreter: &Interpreter,
input: function::Array,
collation: Rc<Collation>,
) -> error::Result<function::Array> {
sort_by_sequence(context, input, collation, |sequence| {
let atoms = sequence
.atomized(interpreter.xot())
.collect::<error::Result<Vec<_>>>()?;
Ok(atoms.into())
})
}
fn sort_by_sequence<F>(
context: &context::DynamicContext,
input: function::Array,
collation: Rc<Collation>,
mut get: F,
) -> error::Result<function::Array>
where
F: FnMut(&sequence::Sequence) -> error::Result<sequence::Sequence>,
{
let sequences = input.iter().collect::<Vec<_>>();
let keys = sequences
.iter()
.map(|sequence| get(sequence))
.collect::<error::Result<Vec<_>>>()?;
let mut keys_and_sequences = keys.into_iter().zip(sequences).collect::<Vec<_>>();
keys_and_sequences.sort_by(|(a_key, _), (b_key, _)| {
a_key.compare(b_key, &collation, context.implicit_timezone())
});
for ((a_key, _), (b_key, _)) in keys_and_sequences
.iter()
.zip(keys_and_sequences.iter().skip(1))
{
a_key.fallible_compare(b_key, &collation, context.implicit_timezone())?;
}
let sequences = keys_and_sequences
.into_iter()
.map(|(_, sequence)| sequence.clone())
.collect::<Vec<_>>();
Ok(function::Array::new(sequences))
}
#[xpath_fn("array:flatten($input as item()*) as item()*")]
fn flatten(input: &sequence::Sequence) -> error::Result<sequence::Sequence> {
input.flatten()
}
fn convert_position(position: IBig) -> error::Result<usize> {
let position: i64 = position.try_into()?;
let position = position - 1;
if position < 0 {
return Err(error::Error::FOAY0001);
}
Ok(position as usize)
}
fn convert_length(length: IBig) -> error::Result<usize> {
let length: i64 = length.try_into()?;
if length < 0 {
return Err(error::Error::FOAY0002);
}
let length = length as usize;
Ok(length)
}
pub(crate) fn static_function_descriptions() -> Vec<StaticFunctionDescription> {
vec![
wrap_xpath_fn!(get),
wrap_xpath_fn!(size),
wrap_xpath_fn!(put),
wrap_xpath_fn!(append),
wrap_xpath_fn!(subarray2),
wrap_xpath_fn!(subarray3),
wrap_xpath_fn!(remove),
wrap_xpath_fn!(insert_before),
wrap_xpath_fn!(head),
wrap_xpath_fn!(tail),
wrap_xpath_fn!(reverse),
wrap_xpath_fn!(join),
wrap_xpath_fn!(for_each),
wrap_xpath_fn!(filter),
wrap_xpath_fn!(fold_left),
wrap_xpath_fn!(fold_right),
wrap_xpath_fn!(for_each_pair),
wrap_xpath_fn!(sort1),
wrap_xpath_fn!(sort2),
wrap_xpath_fn!(sort3),
wrap_xpath_fn!(flatten),
]
}