use std::collections::HashMap;
use std::rc::Rc;
use url::Url;
use crate::item::{Node, Sequence, SequenceTrait};
use crate::pattern::Pattern;
use crate::transform::context::{Context, ContextBuilder, StaticContext};
use crate::transform::{Grouping, Order, Transform, do_sort};
use crate::value::{Operator, Value};
use crate::xdmerror::{Error, ErrorKind};
pub(crate) fn tr_loop<
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>,
v: &Vec<(String, Transform<N>)>,
b: &Transform<N>,
) -> Result<Sequence<N>, Error> {
if v.is_empty() {
return Ok(vec![]);
}
let mut result = vec![];
for i in ctxt.dispatch(stctxt, &v[0].1)? {
let lctxt = ContextBuilder::from(ctxt)
.variable(v[0].0.clone(), vec![i.clone()])
.build();
let mut t = lctxt.dispatch(stctxt, b)?;
result.append(&mut t);
}
Ok(result)
}
pub(crate) fn switch<
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>,
v: &Vec<(Transform<N>, Transform<N>)>,
o: &Transform<N>,
) -> Result<Sequence<N>, Error> {
let mut candidate = ctxt.dispatch(stctxt, o)?;
for (t, w) in v {
let r = ctxt.dispatch(stctxt, t)?;
if r.to_bool() {
candidate = ctxt.dispatch(stctxt, w)?;
break;
}
}
Ok(candidate)
}
pub fn for_each<
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>,
g: &Option<Grouping<N>>,
s: &Transform<N>,
body: &Transform<N>,
o: &Vec<(Order, Transform<N>)>,
) -> Result<Sequence<N>, Error> {
match g {
None => {
let mut result: Sequence<N> = Vec::new();
let mut seq = ctxt.dispatch(stctxt, s)?;
do_sort(&mut seq, o, ctxt, stctxt)?;
for i in seq {
let mut v = ContextBuilder::from(ctxt)
.context(vec![i.clone()])
.context_item(Some(i))
.build()
.dispatch(stctxt, body)?;
result.append(&mut v);
}
Ok(result)
}
Some(Grouping::By(b)) => group_by(ctxt, stctxt, b, s, body, o),
Some(Grouping::Adjacent(a)) => group_adjacent(ctxt, stctxt, a, s, body, o),
Some(Grouping::StartingWith(v)) => group_starting_with(ctxt, stctxt, v, s, body, o),
Some(Grouping::EndingWith(v)) => group_ending_with(ctxt, stctxt, v, s, body, o),
}
}
fn group_by<
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>,
by: &Vec<Transform<N>>,
s: &Transform<N>,
body: &Transform<N>,
o: &Vec<(Order, Transform<N>)>,
) -> Result<Sequence<N>, Error> {
let t = by[0].clone();
let mut groups = HashMap::new();
ctxt.dispatch(stctxt, s)?.iter().try_for_each(|i| {
ContextBuilder::from(ctxt)
.context(vec![i.clone()])
.context_item(Some(i.clone()))
.build()
.dispatch(stctxt, &t)?
.iter()
.for_each(|k| {
let e: &mut Sequence<N> = groups.entry(k.to_string()).or_default();
e.push(i.clone());
});
Ok(())
})?;
if !o.is_empty() {
let mut gr_vec: Vec<(String, Sequence<N>)> =
groups.iter().map(|(k, v)| (k.clone(), v.clone())).collect();
gr_vec.sort_by_cached_key(|(k, v)| {
let key_seq = ContextBuilder::from(ctxt)
.context(v.clone())
.current_grouping_key(Rc::new(Value::from(k.clone())))
.current_group(v.clone())
.build()
.dispatch(stctxt, &o[0].1)
.expect("unable to determine key value");
key_seq.to_string()
});
if o[0].0 == Order::Descending {
gr_vec.reverse();
}
gr_vec.iter().try_fold(vec![], |mut result, (k, v)| {
let mut r = ContextBuilder::from(ctxt)
.current_grouping_key(Rc::new(Value::from(k.clone())))
.current_group(v.clone())
.build()
.dispatch(stctxt, body)?;
result.append(&mut r);
Ok(result)
})
} else {
groups.iter().try_fold(vec![], |mut result, (k, v)| {
let mut r = ContextBuilder::from(ctxt)
.current_grouping_key(Rc::new(Value::from(k.clone())))
.current_group(v.clone())
.build()
.dispatch(stctxt, body)?;
result.append(&mut r);
Ok(result)
})
}
}
fn group_adjacent<
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>,
adj: &Vec<Transform<N>>,
s: &Transform<N>,
body: &Transform<N>,
o: &Vec<(Order, Transform<N>)>,
) -> Result<Sequence<N>, Error> {
let t = adj[0].clone();
let mut groups = Vec::new();
let sel = ctxt.dispatch(stctxt, s)?;
if sel.is_empty() {
return Ok(vec![]);
} else {
let mut curgrp = vec![sel[0].clone()];
let mut curkey = ContextBuilder::from(ctxt)
.context(vec![sel[1].clone()])
.build()
.dispatch(stctxt, &t)?;
if curkey.len() != 1 {
return Err(Error::new(
ErrorKind::Unknown,
String::from("group-adjacent attribute must evaluate to a single item"),
));
}
sel.iter().skip(1).try_for_each(|i| {
let thiskey = ContextBuilder::from(ctxt)
.context(vec![i.clone()])
.context_item(Some(i.clone()))
.build()
.dispatch(stctxt, &t)?;
if thiskey.len() == 1 {
if curkey[0].compare(&thiskey[0], Operator::Equal)? {
curgrp.push(i.clone())
} else {
groups.push((curkey.to_string(), curgrp.clone()));
curgrp = vec![i.clone()];
curkey = thiskey;
}
Ok(())
} else {
Err(Error::new(
ErrorKind::Unknown,
String::from("group-adjacent attribute must evaluate to a single item"),
))
}
})?;
groups.push((curkey.to_string(), curgrp))
}
if !o.is_empty() {
let mut gr_vec: Vec<(String, Sequence<N>)> =
groups.iter().map(|(k, v)| (k.clone(), v.clone())).collect();
gr_vec.sort_by_cached_key(|(k, v)| {
let key_seq = ContextBuilder::from(ctxt)
.context(v.clone())
.current_grouping_key(Rc::new(Value::from(k.clone())))
.current_group(v.clone())
.build()
.dispatch(stctxt, &o[0].1)
.expect("unable to determine key value");
key_seq.to_string()
});
if o[0].0 == Order::Descending {
gr_vec.reverse();
}
gr_vec.iter().try_fold(vec![], |mut result, (k, v)| {
let mut r = ContextBuilder::from(ctxt)
.current_grouping_key(Rc::new(Value::from(k.clone())))
.current_group(v.clone())
.build()
.dispatch(stctxt, body)?;
result.append(&mut r);
Ok(result)
})
} else {
groups.iter().try_fold(vec![], |mut result, (k, v)| {
let mut r = ContextBuilder::from(ctxt)
.current_grouping_key(Rc::new(Value::from(k.clone())))
.current_group(v.clone())
.build()
.dispatch(stctxt, body)?;
result.append(&mut r);
Ok(result)
})
}
}
fn group_starting_with<
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>,
pat: &Pattern<N>,
s: &Transform<N>,
body: &Transform<N>,
o: &Vec<(Order, Transform<N>)>,
) -> Result<Sequence<N>, Error> {
let mut groups;
let population = ctxt.dispatch(stctxt, s)?;
let mut sit = population.iter();
if let Some(i) = sit.next() {
groups = vec![vec![i.clone()]];
} else {
return Ok(vec![]);
}
sit.try_for_each(|i| {
if pat.matches(ctxt, stctxt, i) {
groups.push(vec![i.clone()]);
} else {
groups.last_mut().unwrap().push(i.clone());
}
Ok(())
})?;
if !o.is_empty() {
groups.sort_by_cached_key(|v| {
let key_seq = ContextBuilder::from(ctxt)
.context(v.clone())
.current_group(v.clone())
.build()
.dispatch(stctxt, &o[0].1)
.expect("unable to determine key value");
key_seq.to_string()
});
if o[0].0 == Order::Descending {
groups.reverse();
}
groups.iter().try_fold(vec![], |mut result, v| {
let mut r = ContextBuilder::from(ctxt)
.current_group(v.clone())
.build()
.dispatch(stctxt, body)?;
result.append(&mut r);
Ok(result)
})
} else {
groups.iter().try_fold(vec![], |mut result, v| {
let mut r = ContextBuilder::from(ctxt)
.current_group(v.clone())
.build()
.dispatch(stctxt, body)?;
result.append(&mut r);
Ok(result)
})
}
}
pub fn group_ending_with<
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>,
_pat: &Pattern<N>,
_s: &Transform<N>,
_body: &Transform<N>,
_o: &Vec<(Order, Transform<N>)>,
) -> Result<Sequence<N>, Error> {
Err(Error::new(
ErrorKind::NotImplemented,
String::from("not implemented"),
))
}