use xot::Xot;
use crate::{
atomic::{self, AtomicCompare},
context, error, function,
string::Collation,
xml,
};
use super::{
comparison,
item::Item,
iter::{self, AtomizedIter, NodeIter},
};
pub(crate) type BoxedItemIter<'a> = Box<dyn Iterator<Item = Item> + 'a>;
pub trait SequenceCore<'a, I>
where
I: Iterator<Item = Item>,
{
fn is_empty(&self) -> bool;
fn len(&self) -> usize;
fn get(&self, index: usize) -> Option<Item>;
fn iter(&'a self) -> I;
fn one(self) -> error::Result<Item>;
fn option(self) -> error::Result<Option<Item>>;
fn effective_boolean_value(&'a self) -> error::Result<bool>;
fn string_value(&'a self, xot: &Xot) -> error::Result<String>;
}
pub trait SequenceExt<'a, I>: SequenceCore<'a, I>
where
I: Iterator<Item = Item> + 'a,
{
fn nodes(&'a self) -> impl Iterator<Item = error::Result<xot::Node>> + 'a {
NodeIter::new(self.iter())
}
fn atomized(
&'a self,
xot: &'a Xot,
) -> impl Iterator<Item = error::Result<atomic::Atomic>> + 'a {
AtomizedIter::new(xot, self.iter())
}
fn atomized_one(&'a self, xot: &'a Xot) -> error::Result<atomic::Atomic> {
iter::one(self.atomized(xot))?
}
fn atomized_option(&'a self, xot: &'a Xot) -> error::Result<Option<atomic::Atomic>> {
iter::option(self.atomized(xot))?.transpose()
}
fn unboxed_atomized<T: 'a>(
&'a self,
xot: &'a Xot,
extract: impl Fn(atomic::Atomic) -> error::Result<T> + 'a,
) -> impl Iterator<Item = error::Result<T>> + 'a {
self.atomized(xot).map(move |a| extract(a?))
}
fn map_iter(&'a self) -> impl Iterator<Item = error::Result<function::Map>> {
self.iter().map(|item| item.to_map())
}
fn array_iter(&'a self) -> impl Iterator<Item = error::Result<function::Array>> {
self.iter().map(|item| item.to_array())
}
fn elements(
&'a self,
xot: &'a Xot,
) -> error::Result<impl Iterator<Item = error::Result<xot::Node>>> {
Ok(self.nodes().map(|n| match n {
Ok(n) => {
if xot.is_element(n) {
Ok(n)
} else {
Err(error::Error::XPTY0004)
}
}
Err(n) => Err(n),
}))
}
fn to_array(&'a self) -> error::Result<function::Array> {
let mut array = Vec::with_capacity(self.len());
for item in self.iter() {
array.push(item.into());
}
Ok(array.into())
}
}
pub(crate) trait SequenceCompare<'a, I>: SequenceExt<'a, I>
where
I: Iterator<Item = Item> + 'a,
{
fn general_comparison<O, J>(
&'a self,
other: &'a impl SequenceExt<'a, J>,
op: O,
context: &context::DynamicContext,
xot: &'a Xot,
) -> error::Result<bool>
where
O: AtomicCompare,
J: Iterator<Item = Item> + 'a,
{
let a_atomized = self.atomized(xot);
let b_atomized = other.atomized(xot);
let (a_lower_bound, _) = a_atomized.size_hint();
let (b_lower_bound, _) = b_atomized.size_hint();
if a_lower_bound < b_lower_bound {
comparison::general_comparison(b_atomized, a_atomized, context, O::arguments_inverted())
} else {
comparison::general_comparison(a_atomized, b_atomized, context, op)
}
}
fn value_compare<O, J>(
&'a self,
other: &'a impl SequenceExt<'a, J>,
_op: O,
collation: &Collation,
timezone: chrono::FixedOffset,
xot: &'a Xot,
) -> error::Result<bool>
where
O: AtomicCompare,
J: Iterator<Item = Item> + 'a,
{
let a = self.atomized_one(xot)?;
let b = other.atomized_one(xot)?;
O::atomic_compare(a, b, |a: &str, b: &str| collation.compare(a, b), timezone)
}
}
pub(crate) trait SequenceOrder<'a, I>: SequenceCore<'a, I>
where
I: Iterator<Item = Item>,
{
fn one_node(&self) -> error::Result<xot::Node>;
fn is<J>(&'a self, other: &'a impl SequenceOrder<'a, J>) -> error::Result<bool>
where
J: Iterator<Item = Item> + 'a,
{
let a = self.one_node()?;
let b = other.one_node()?;
Ok(a == b)
}
fn precedes<J>(
&'a self,
other: &'a impl SequenceOrder<'a, J>,
annotations: xml::DocumentOrderAccess,
) -> error::Result<bool>
where
J: Iterator<Item = Item> + 'a,
{
let a = self.one_node()?;
let b = other.one_node()?;
let a_order = annotations.get(a);
let b_order = annotations.get(b);
Ok(a_order < b_order)
}
fn follows<J>(
&'a self,
other: &'a impl SequenceOrder<'a, J>,
annotations: xml::DocumentOrderAccess,
) -> error::Result<bool>
where
J: Iterator<Item = Item> + 'a,
{
let a = self.one_node()?;
let b = other.one_node()?;
let a_order = annotations.get(a);
let b_order = annotations.get(b);
Ok(a_order > b_order)
}
}