use sxd_document::QName;
use std::collections::HashMap;
use std::iter;
use crate::function;
use crate::nodeset::{Node, OrderedNodes};
use crate::{OwnedQName, Value};
type Functions = HashMap<OwnedQName, Box<dyn function::Function + 'static>>;
type Variables<'d> = HashMap<OwnedQName, Value<'d>>;
type Namespaces = HashMap<String, String>;
pub struct Context<'d> {
functions: Functions,
variables: Variables<'d>,
namespaces: Namespaces,
}
impl<'d> Context<'d> {
pub fn new() -> Self {
let mut context = Self::without_core_functions();
function::register_core_functions(&mut context);
context
}
pub fn without_core_functions() -> Self {
Context {
functions: Default::default(),
variables: Default::default(),
namespaces: Default::default(),
}
}
pub fn set_function<N, F>(&mut self, name: N, function: F)
where
N: Into<OwnedQName>,
F: function::Function + 'static,
{
self.functions.insert(name.into(), Box::new(function));
}
pub fn set_variable<N, V>(&mut self, name: N, value: V)
where
N: Into<OwnedQName>,
V: Into<Value<'d>>,
{
self.variables.insert(name.into(), value.into());
}
pub fn set_namespace(&mut self, prefix: &str, uri: &str) {
self.namespaces.insert(prefix.into(), uri.into());
}
}
impl<'d> Default for Context<'d> {
fn default() -> Self {
Context::new()
}
}
#[derive(Copy, Clone)]
pub struct Evaluation<'c, 'd> {
pub node: Node<'d>,
pub position: usize,
pub size: usize,
functions: &'c Functions,
variables: &'c Variables<'d>,
namespaces: &'c Namespaces,
}
impl<'c, 'd> Evaluation<'c, 'd> {
pub fn new(context: &'c Context<'d>, node: Node<'d>) -> Evaluation<'c, 'd> {
Evaluation {
node,
functions: &context.functions,
variables: &context.variables,
namespaces: &context.namespaces,
position: 1,
size: 1,
}
}
pub fn new_context_for<N>(&self, node: N) -> Evaluation<'c, 'd>
where
N: Into<Node<'d>>,
{
Evaluation {
node: node.into(),
..*self
}
}
pub fn function_for_name(&self, name: QName<'_>) -> Option<&'c dyn function::Function> {
let name = name.into();
self.functions.get(&name).map(AsRef::as_ref)
}
pub fn value_of(&self, name: QName<'_>) -> Option<&Value<'d>> {
let name = name.into();
self.variables.get(&name)
}
pub fn namespace_for(&self, prefix: &str) -> Option<&str> {
self.namespaces.get(prefix).map(String::as_str)
}
pub fn new_contexts_for(self, nodes: OrderedNodes<'d>) -> EvaluationNodesetIter<'c, 'd> {
let sz = nodes.size();
EvaluationNodesetIter {
parent: self,
nodes: Vec::from(nodes).into_iter().enumerate(),
size: sz,
}
}
}
pub struct EvaluationNodesetIter<'c, 'd> {
parent: Evaluation<'c, 'd>,
nodes: iter::Enumerate<::std::vec::IntoIter<Node<'d>>>,
size: usize,
}
impl<'c, 'd> Iterator for EvaluationNodesetIter<'c, 'd> {
type Item = Evaluation<'c, 'd>;
fn next(&mut self) -> Option<Evaluation<'c, 'd>> {
self.nodes.next().map(|(idx, node)| Evaluation {
node,
position: idx + 1,
size: self.size,
..self.parent
})
}
}