use std::rc::Rc;
use ibig::IBig;
use xee_name::Name;
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(
"fn:function-lookup($name as xs:QName, $arity as xs:integer, $arg as node()?) as function(*)?",
context_last_optional
)]
fn function_lookup(
context: &context::DynamicContext,
interpreter: &mut Interpreter,
name: Name,
arity: IBig,
arg: Option<xot::Node>,
) -> error::Result<Option<sequence::Item>> {
let arity: u8 = if let Ok(arity) = arity.try_into() {
arity
} else {
return Ok(None);
};
let static_function_id = context.static_context().function_id_by_name(&name, arity);
if let Some(static_function_id) = static_function_id {
let function = interpreter.create_static_closure_from_context(static_function_id, arg)?;
let item: sequence::Item = function.into();
Ok(Some(item))
} else {
Ok(None)
}
}
#[xpath_fn("fn:function-name($func as function(*)) as xs:QName?")]
fn function_name(interpreter: &Interpreter, func: sequence::Item) -> error::Result<Option<Name>> {
let function = func.to_function()?;
Ok(interpreter.function_name(&function))
}
#[xpath_fn("fn:function-arity($func as function(*)) as xs:integer")]
fn function_arity(interpreter: &Interpreter, func: sequence::Item) -> error::Result<IBig> {
let function = func.to_function()?;
Ok(interpreter.function_arity(&function).into())
}
#[xpath_fn("fn:for-each($seq as item()*, $action as function(item()) as item()*) as item()*")]
fn for_each(
interpreter: &mut Interpreter,
seq: &sequence::Sequence,
action: sequence::Item,
) -> error::Result<sequence::Sequence> {
let mut result: Vec<sequence::Item> = Vec::with_capacity(seq.len());
let function = action.to_function()?;
for item in seq.iter() {
let value = interpreter.call_function_with_arguments(&function, &[item.into()])?;
for item in value.iter() {
result.push(item.clone());
}
}
Ok(result.into())
}
#[xpath_fn("fn:filter($seq as item()*, $predicate as function(item()) as xs:boolean) as item()*")]
fn filter(
interpreter: &mut Interpreter,
seq: &sequence::Sequence,
predicate: sequence::Item,
) -> error::Result<sequence::Sequence> {
let mut result: Vec<sequence::Item> = Vec::new();
let function = predicate.to_function()?;
for item in seq.iter() {
let value = interpreter.call_function_with_arguments(&function, &[item.clone().into()])?;
let atom: atomic::Atomic = sequence::one(value.iter())?.to_atomic()?;
let value: bool = atom.try_into()?;
if value {
result.push(item.clone());
}
}
Ok(result.into())
}
#[xpath_fn("fn:fold-left($seq as item()*, $zero as item()*, $f as function(item()*, item()) as item()*) as item()*")]
fn fold_left(
interpreter: &mut Interpreter,
seq: &sequence::Sequence,
zero: &sequence::Sequence,
f: sequence::Item,
) -> error::Result<sequence::Sequence> {
let function = f.to_function()?;
let mut accumulator = zero.clone();
for item in seq.iter() {
accumulator =
interpreter.call_function_with_arguments(&function, &[accumulator, item.into()])?;
}
Ok(accumulator)
}
#[xpath_fn("fn:fold-right($seq as item()*, $zero as item()*, $f as function(item(), item()*) as item()*) as item()*")]
fn fold_right(
interpreter: &mut Interpreter,
seq: &sequence::Sequence,
zero: &sequence::Sequence,
f: sequence::Item,
) -> error::Result<sequence::Sequence> {
let function = f.to_function()?;
let mut accumulator = zero.clone();
let seq = seq.iter().collect::<Vec<_>>();
for item in seq.into_iter().rev() {
accumulator =
interpreter.call_function_with_arguments(&function, &[item.into(), accumulator])?;
}
Ok(accumulator)
}
#[xpath_fn("fn:for-each-pair($seq1 as item()*, $seq2 as item()*, $action as function(item(), item()) as item()*) as item()*")]
fn for_each_pair(
interpreter: &mut Interpreter,
seq1: &sequence::Sequence,
seq2: &sequence::Sequence,
action: sequence::Item,
) -> error::Result<sequence::Sequence> {
let mut result: Vec<sequence::Item> = Vec::with_capacity(seq1.len());
let function = action.to_function()?;
for (item1, item2) in seq1.iter().zip(seq2.iter()) {
let value =
interpreter.call_function_with_arguments(&function, &[item1.into(), item2.into()])?;
for item in value.iter() {
result.push(item.clone());
}
}
Ok(result.into())
}
#[xpath_fn("fn:sort($input as item()*) as item()*")]
fn sort1(
context: &context::DynamicContext,
interpreter: &Interpreter,
input: &sequence::Sequence,
) -> error::Result<sequence::Sequence> {
input.sorted(
context,
context.static_context().default_collation()?,
interpreter.xot(),
)
}
#[xpath_fn("fn:sort($input as item()*, $collation as xs:string?) as item()*")]
fn sort2(
context: &context::DynamicContext,
interpreter: &Interpreter,
input: &sequence::Sequence,
collation: Option<&str>,
) -> error::Result<sequence::Sequence> {
let collation = context.static_context().resolve_collation_str(collation)?;
input.sorted(context, collation, interpreter.xot())
}
#[xpath_fn("fn:sort($input as item()*, $collation as xs:string?, $key as function(item()) as xs:anyAtomicType*) as item()*")]
fn sort3(
context: &context::DynamicContext,
interpreter: &mut Interpreter,
input: &sequence::Sequence,
collation: Option<&str>,
key: sequence::Item,
) -> error::Result<sequence::Sequence> {
let collation = context.static_context().resolve_collation_str(collation)?;
let function = key.to_function()?;
input.sorted_by_key(context, collation, |item| {
let value = interpreter.call_function_with_arguments(&function, &[item.clone().into()])?;
Ok(value)
})
}
fn sort_without_key(
context: &context::DynamicContext,
interpreter: &Interpreter,
input: &sequence::Sequence,
collation: Rc<Collation>,
) -> error::Result<sequence::Sequence> {
sort_by_sequence(context, input, collation, |item| {
let seq: sequence::Sequence = item.clone().into();
let atoms = seq
.atomized(interpreter.xot())
.collect::<error::Result<Vec<_>>>()?;
Ok(atoms.into())
})
}
fn sort_by_sequence<F>(
context: &context::DynamicContext,
input: &sequence::Sequence,
collation: Rc<Collation>,
mut get: F,
) -> error::Result<sequence::Sequence>
where
F: FnMut(&sequence::Item) -> error::Result<sequence::Sequence>,
{
let mut keys_and_items = input
.clone()
.iter()
.map(|item| Ok((get(&item)?, item)))
.collect::<error::Result<Vec<_>>>()?;
keys_and_items.sort_by(|(a_key, _), (b_key, _)| {
a_key.compare(b_key, &collation, context.implicit_timezone())
});
for ((a_key, _), (b_key, _)) in keys_and_items.iter().zip(keys_and_items.iter().skip(1)) {
a_key.fallible_compare(b_key, &collation, context.implicit_timezone())?;
}
let items = keys_and_items
.into_iter()
.map(|(_, item)| item.clone())
.collect::<Vec<_>>();
Ok(items.into())
}
#[xpath_fn("fn:apply($function as function(*), $array as array(*)) as item()*")]
fn apply(
interpreter: &mut Interpreter,
function: sequence::Item,
array: function::Array,
) -> error::Result<sequence::Sequence> {
let function = function.to_function()?;
let arity = array.len();
if interpreter.function_arity(&function) != arity {
return Err(error::Error::FOAP0001);
}
interpreter.call_function_with_arguments(&function, &array.0)
}
pub(crate) fn static_function_descriptions() -> Vec<StaticFunctionDescription> {
vec![
wrap_xpath_fn!(function_lookup),
wrap_xpath_fn!(function_name),
wrap_xpath_fn!(function_arity),
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!(apply),
]
}