use crate::Item;
use crate::item::{Node, NodeType, Sequence, SequenceTrait};
use crate::transform::context::{Context, ContextBuilder, StaticContext};
use crate::transform::{Axis, NodeMatch, Transform};
use crate::value::ValueData;
use crate::xdmerror::{Error, ErrorKind};
use qualname::{NcName, QName};
use std::cmp::Ordering;
use url::Url;
pub(crate) fn root<N: Node>(ctxt: &Context<N>) -> Result<Sequence<N>, Error> {
ctxt.context_item.as_ref().map_or(
Err(Error::new(
ErrorKind::ContextNotNode,
String::from("no context"),
)),
|i| match &i {
Item::Node(n) => match n.node_type() {
NodeType::Document => Ok(vec![Item::Node(n.clone())]),
_ => n
.ancestor_iter()
.last()
.map_or(Ok(vec![]), |m| Ok(vec![Item::Node(m)])),
},
_ => Err(Error::new(
ErrorKind::ContextNotNode,
String::from("context item is not a node"),
)),
},
)
}
pub(crate) fn context<N: Node>(ctxt: &Context<N>) -> Result<Sequence<N>, Error> {
ctxt.context_item.as_ref().map_or(
Err(Error::new(
ErrorKind::DynamicAbsent,
String::from("no context"),
)),
|i| Ok(vec![i.clone()]),
)
}
pub(crate) fn compose<
N: Node,
F: FnMut(&str) -> Result<(), Error>,
G: FnMut(&str) -> Result<N, Error>,
H: FnMut(&Url) -> Result<String, Error>,
>(
ctxt: &Context<N>,
stctxt: &mut StaticContext<N, F, G, H>,
steps: &Vec<Transform<N>>,
) -> Result<Sequence<N>, Error> {
let mut context = ctxt.clone();
let mut it = steps.iter();
while let Some(t) = it.next() {
let previous = ctxt.context.clone();
let new = context.dispatch(stctxt, t)?;
let new_ctxt = ContextBuilder::from(&context)
.context(new.clone())
.current(previous)
.build();
context = new_ctxt;
}
Ok(context.context)
}
pub(crate) fn step<N: Node>(ctxt: &Context<N>, nm: &NodeMatch) -> Result<Sequence<N>, Error> {
match ctxt.context.iter().try_fold(vec![], |mut acc, i| {
match i {
Item::Node(n) => {
match nm.axis {
Axis::SelfAxis => {
if nm.matches(n) {
acc.push(i.clone());
Ok(acc)
} else {
Ok(acc)
}
}
Axis::SelfDocument => {
if n.node_type() == NodeType::Document {
acc.push(i.clone());
Ok(acc)
} else {
Ok(acc)
}
}
Axis::Child => {
let mut s = n.child_iter().filter(|c| nm.matches(c)).fold(
Sequence::new(),
|mut c, a| {
c.push_node(&a);
c
},
);
acc.append(&mut s);
Ok(acc)
}
Axis::Parent => match n.parent() {
Some(p) => {
acc.push_node(&p);
Ok(acc)
}
None => Ok(acc),
},
Axis::ParentDocument => {
match n.node_type() {
NodeType::Document => {
acc.push(i.clone());
Ok(acc)
}
_ => Ok(acc),
}
}
Axis::Descendant => {
n.descend_iter()
.filter(|c| nm.matches(c))
.for_each(|c| acc.push_node(&c));
Ok(acc)
}
Axis::DescendantOrSelf => {
if nm.matches(n) {
acc.push(i.clone())
}
n.descend_iter()
.filter(|c| nm.matches(c))
.for_each(|c| acc.push_node(&c));
Ok(acc)
}
Axis::DescendantOrSelfOrRoot => {
acc.push_node(&n.owner_document());
if nm.matches(n) {
acc.push(i.clone())
}
n.descend_iter()
.filter(|c| nm.matches(c))
.for_each(|c| acc.push_node(&c));
Ok(acc)
}
Axis::Ancestor => {
n.ancestor_iter()
.filter(|c| nm.matches(c))
.for_each(|c| acc.push_node(&c));
Ok(acc)
}
Axis::AncestorOrSelf => {
n.ancestor_iter()
.filter(|c| nm.matches(c))
.for_each(|c| acc.push_node(&c));
if nm.matches(n) {
acc.push(i.clone())
}
Ok(acc)
}
Axis::FollowingSibling => {
n.next_iter()
.filter(|c| nm.matches(c))
.for_each(|c| acc.push_node(&c));
Ok(acc)
}
Axis::PrecedingSibling => {
n.prev_iter()
.filter(|c| nm.matches(c))
.for_each(|c| acc.push_node(&c));
Ok(acc)
}
Axis::Following => {
let mut bcc = vec![];
n.next_iter().for_each(|a| {
bcc.push(a.clone());
a.descend_iter().for_each(|b| bcc.push(b.clone()));
});
n.ancestor_iter().for_each(|a| {
a.next_iter().for_each(|b| {
bcc.push(b.clone());
b.descend_iter().for_each(|c| bcc.push(c.clone()));
})
});
bcc.iter().filter(|e| nm.matches(*e)).for_each(|g| {
acc.push_node(g);
});
Ok(acc)
}
Axis::Preceding => {
let mut bcc = vec![];
n.prev_iter().for_each(|a| {
bcc.push(a.clone());
a.descend_iter().for_each(|b| bcc.push(b.clone()));
});
n.ancestor_iter().for_each(|a| {
a.prev_iter().for_each(|b| {
bcc.push(b.clone());
b.descend_iter().for_each(|c| bcc.push(c.clone()));
})
});
bcc.iter().filter(|e| nm.matches(*e)).for_each(|g| {
acc.push_node(g);
});
Ok(acc)
}
Axis::Attribute => {
n.attribute_iter()
.filter(|a| nm.matches(a))
.for_each(|a| acc.push_node(&a));
Ok(acc)
}
Axis::SelfAttribute => {
if n.node_type() == NodeType::Attribute {
acc.push_node(n)
}
Ok(acc)
}
_ => Err(Error::new(
ErrorKind::NotImplemented,
String::from("coming soon"),
)),
}
}
_ => Err(Error::new_with_code(
ErrorKind::DynamicAbsent,
"context item is not a node",
Some(QName::from_local_name(
NcName::try_from("XTTE0510").unwrap(),
)),
)),
}
}) {
Ok(mut r) => {
r.sort_unstable_by(|a, b| {
get_node_unchecked(a).cmp_document_order(get_node_unchecked(b))
});
r.dedup_by(|a, b| {
get_node(a).map_or(false, |aa| get_node(b).is_ok_and(|bb| aa.is_same(bb)))
});
Ok(r)
}
Err(err) => Err(err),
}
}
fn get_node_unchecked<N: Node>(i: &Item<N>) -> &N {
match i {
Item::Node(n) => n,
_ => panic!("not a node"),
}
}
fn get_node<N: Node>(i: &Item<N>) -> Result<&N, Error> {
match i {
Item::Node(n) => Ok(n),
_ => Err(Error::new(ErrorKind::Unknown, String::from("not a node"))),
}
}
pub(crate) fn filter<
N: Node,
F: FnMut(&str) -> Result<(), Error>,
G: FnMut(&str) -> Result<N, Error>,
H: FnMut(&Url) -> Result<String, Error>,
>(
ctxt: &Context<N>,
stctxt: &mut StaticContext<N, F, G, H>,
predicate: &Transform<N>,
) -> Result<Sequence<N>, Error> {
let seq = ctxt.context.clone();
let current = ctxt.current.clone();
let current_item = ctxt.current_item.clone();
filter_seq(ctxt, stctxt, seq, predicate, current, current_item)
}
pub(crate) fn filter_seq<
N: Node,
F: FnMut(&str) -> Result<(), Error>,
G: FnMut(&str) -> Result<N, Error>,
H: FnMut(&Url) -> Result<String, Error>,
>(
ctxt: &Context<N>,
stctxt: &mut StaticContext<N, F, G, H>,
seq: Sequence<N>,
predicate: &Transform<N>,
current: Sequence<N>,
current_item: Option<Item<N>>,
) -> Result<Sequence<N>, Error> {
let len = seq.len();
seq.iter().enumerate().try_fold(vec![], |mut acc, (j, i)| {
let result = ContextBuilder::from(ctxt)
.context(vec![i.clone()])
.context_item(Some(i.clone()))
.current(current.clone())
.current_item(current_item.clone())
.index(j)
.last(len)
.build()
.dispatch(stctxt, predicate)?;
let keep = match predicate_number(&result) {
Some(n) => n == (j as f64 + 1.0),
None => result.to_bool(),
};
if keep {
acc.push(i.clone());
}
Ok(acc)
})
}
fn predicate_number<N: Node>(seq: &Sequence<N>) -> Option<f64> {
if seq.len() != 1 {
return None;
}
match &seq[0] {
Item::Value(v) => match v.value_ref() {
ValueData::Decimal(_)
| ValueData::Float(_)
| ValueData::Double(_)
| ValueData::Integer(_)
| ValueData::NonPositiveInteger(_)
| ValueData::NegativeInteger(_)
| ValueData::Long(_)
| ValueData::Int(_)
| ValueData::Short(_)
| ValueData::Byte(_)
| ValueData::NonNegativeInteger(_)
| ValueData::UnsignedLong(_)
| ValueData::UnsignedInt(_)
| ValueData::UnsignedShort(_)
| ValueData::UnsignedByte(_)
| ValueData::PositiveInteger(_)
| ValueData::Numeric => Some(v.to_double()),
_ => None,
},
_ => None,
}
}
pub(crate) fn step_predicated<
N: Node,
F: FnMut(&str) -> Result<(), Error>,
G: FnMut(&str) -> Result<N, Error>,
H: FnMut(&Url) -> Result<String, Error>,
>(
ctxt: &Context<N>,
stctxt: &mut StaticContext<N, F, G, H>,
nm: &NodeMatch,
predicates: &[Transform<N>],
) -> Result<Sequence<N>, Error> {
let reverse = matches!(
nm.axis,
Axis::Ancestor
| Axis::AncestorOrSelf
| Axis::AncestorOrSelfOrRoot
| Axis::Preceding
| Axis::PrecedingSibling
);
let mut acc: Sequence<N> = Vec::new();
for it in ctxt.context.iter() {
let single = ContextBuilder::from(ctxt).context(vec![it.clone()]).build();
let mut nodes = step(&single, nm)?;
nodes.sort_by(|a, b| {
let o = match (a, b) {
(Item::Node(x), Item::Node(y)) => x.cmp_document_order(y),
_ => Ordering::Equal,
};
if reverse {
o.reverse()
} else {
o
}
});
for p in predicates {
nodes = filter_seq(ctxt, stctxt, nodes, p, vec![it.clone()], Some(it.clone()))?;
}
acc.extend(nodes);
}
acc.sort_by(|a, b| match (a, b) {
(Item::Node(x), Item::Node(y)) => x.cmp_document_order(y),
_ => Ordering::Equal,
});
acc.dedup_by(|a, b| match (a, b) {
(Item::Node(x), Item::Node(y)) => x.is_same(y),
_ => false,
});
Ok(acc)
}