use std::rc::Rc;
use url::Url;
use crate::item::{Item, Node, Sequence, SequenceTrait};
use crate::transform::Transform;
use crate::transform::context::{Context, StaticContext};
use crate::value::{Operator, Value};
use crate::xdmerror::{Error, ErrorKind};
thread_local! {
static TRUE_VALUE: Rc<Value> = Rc::new(Value::from(true));
static FALSE_VALUE: Rc<Value> = Rc::new(Value::from(false));
}
#[inline]
fn bool_seq<N: Node>(b: bool) -> Sequence<N> {
let v = if b {
TRUE_VALUE.with(|t| t.clone())
} else {
FALSE_VALUE.with(|f| f.clone())
};
vec![Item::Value(v)]
}
pub(crate) fn tr_or<
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>>,
) -> Result<Sequence<N>, Error> {
let mut b = false;
let mut i = 0;
while let Some(a) = v.get(i) {
if ctxt.dispatch(stctxt, a)?.to_bool() {
b = true;
break;
}
i += 1;
}
Ok(vec![Item::Value(Rc::new(Value::from(b)))])
}
pub(crate) fn tr_and<
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>>,
) -> Result<Sequence<N>, Error> {
let mut b = true;
let mut i = 0;
while let Some(a) = v.get(i) {
if !ctxt.dispatch(stctxt, a)?.to_bool() {
b = false;
break;
}
i += 1;
}
Ok(vec![Item::Value(Rc::new(Value::from(b)))])
}
pub(crate) fn general_comparison<
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>,
o: &Operator,
l: &Transform<N>,
r: &Transform<N>,
) -> Result<Sequence<N>, Error> {
let left = ctxt.dispatch(stctxt, l)?;
let right = ctxt.dispatch(stctxt, r)?;
let relational = matches!(
o,
Operator::LessThan
| Operator::LessThanEqual
| Operator::GreaterThan
| Operator::GreaterThanEqual
);
let mut b = false;
'outer: for i in &left {
for j in &right {
b = if relational {
let (x, y) = (i.to_double(), j.to_double());
match o {
Operator::LessThan => x < y,
Operator::LessThanEqual => x <= y,
Operator::GreaterThan => x > y,
Operator::GreaterThanEqual => x >= y,
_ => unreachable!(),
}
} else {
i.compare(j, *o)?
};
if b {
break 'outer;
}
}
}
Ok(bool_seq(b))
}
pub(crate) fn value_comparison<
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>,
o: &Operator,
l: &Transform<N>,
r: &Transform<N>,
) -> Result<Sequence<N>, Error> {
let left = ctxt.dispatch(stctxt, l)?;
if left.len() != 1 {
return Err(Error::new(
ErrorKind::TypeError,
String::from("left-hand sequence is not a singleton sequence"),
));
}
let right = ctxt.dispatch(stctxt, r)?;
if right.len() != 1 {
return Err(Error::new(
ErrorKind::TypeError,
String::from("right-hand sequence is not a singleton sequence"),
));
}
Ok(vec![Item::Value(Rc::new(Value::from(
left[0].compare(&right[0], *o)?,
)))])
}
pub(crate) fn union<
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>,
branches: &Vec<Transform<N>>,
) -> Result<Sequence<N>, Error> {
let mut result = vec![];
for b in branches {
let mut c = ctxt.dispatch(stctxt, b)?;
if c.iter().any(|f| !f.is_node()) {
return Err(Error::new(
ErrorKind::TypeError,
"all operands must be nodes",
));
}
result.append(&mut c)
}
Ok(result)
}