use std::collections::HashSet;
use crate::types::value::XmlValue;
use super::error::XPathError;
use super::item_set::{ItemSet, XPathComparer, XPathEqualityComparer};
use super::iterator::XmlItem;
use super::DomNavigator;
pub fn union_nodes<N: DomNavigator + Clone>(
left: Vec<XmlItem<N>>,
right: Vec<XmlItem<N>>,
) -> Result<Vec<XmlItem<N>>, XPathError> {
let mut result: ItemSet<XmlItem<N>> = ItemSet::with_capacity(left.len() + right.len());
let eq_comparer = XPathEqualityComparer::new();
for item in left {
if !matches!(item, XmlItem::Node(_)) {
return Err(XPathError::type_mismatch(
"node()*".to_string(),
"atomic value".to_string(),
));
}
let is_duplicate = result
.iter()
.any(|existing| eq_comparer.equals(existing, &item));
if !is_duplicate {
result.add(item);
}
}
for item in right {
if !matches!(item, XmlItem::Node(_)) {
return Err(XPathError::type_mismatch(
"node()*".to_string(),
"atomic value".to_string(),
));
}
let is_duplicate = result
.iter()
.any(|existing| eq_comparer.equals(existing, &item));
if !is_duplicate {
result.add(item);
}
}
let comparer = XPathComparer::new();
result.sort_with(&comparer);
Ok(result.into_iter().collect())
}
pub fn intersect_nodes<N: DomNavigator + Clone>(
left: Vec<XmlItem<N>>,
right: Vec<XmlItem<N>>,
) -> Result<Vec<XmlItem<N>>, XPathError> {
for item in &left {
if !matches!(item, XmlItem::Node(_)) {
return Err(XPathError::type_mismatch(
"node()*".to_string(),
"atomic value".to_string(),
));
}
}
for item in &right {
if !matches!(item, XmlItem::Node(_)) {
return Err(XPathError::type_mismatch(
"node()*".to_string(),
"atomic value".to_string(),
));
}
}
let eq_comparer = XPathEqualityComparer::new();
let mut result: ItemSet<XmlItem<N>> = ItemSet::new();
for left_item in left {
let in_right = right.iter().any(|r| eq_comparer.equals(&left_item, r));
if in_right {
let already_added = result
.iter()
.any(|existing| eq_comparer.equals(existing, &left_item));
if !already_added {
result.add(left_item);
}
}
}
let comparer = XPathComparer::new();
result.sort_with(&comparer);
Ok(result.into_iter().collect())
}
pub fn except_nodes<N: DomNavigator + Clone>(
left: Vec<XmlItem<N>>,
right: Vec<XmlItem<N>>,
) -> Result<Vec<XmlItem<N>>, XPathError> {
for item in &left {
if !matches!(item, XmlItem::Node(_)) {
return Err(XPathError::type_mismatch(
"node()*".to_string(),
"atomic value".to_string(),
));
}
}
for item in &right {
if !matches!(item, XmlItem::Node(_)) {
return Err(XPathError::type_mismatch(
"node()*".to_string(),
"atomic value".to_string(),
));
}
}
let eq_comparer = XPathEqualityComparer::new();
let mut result: ItemSet<XmlItem<N>> = ItemSet::new();
for left_item in left {
let in_right = right.iter().any(|r| eq_comparer.equals(&left_item, r));
if !in_right {
let already_added = result
.iter()
.any(|existing| eq_comparer.equals(existing, &left_item));
if !already_added {
result.add(left_item);
}
}
}
let comparer = XPathComparer::new();
result.sort_with(&comparer);
Ok(result.into_iter().collect())
}
pub fn union_atomic_values(left: Vec<XmlValue>, right: Vec<XmlValue>) -> Vec<XmlValue> {
let mut seen = HashSet::new();
let mut result = Vec::with_capacity(left.len() + right.len());
for item in left.into_iter().chain(right) {
let key = item.to_string_value();
if !seen.contains(&key) {
seen.insert(key);
result.push(item);
}
}
result
}
pub fn intersect_atomic_values(left: Vec<XmlValue>, right: Vec<XmlValue>) -> Vec<XmlValue> {
let right_set: HashSet<String> = right.iter().map(|v| v.to_string_value()).collect();
let mut seen = HashSet::new();
let mut result = Vec::new();
for item in left {
let key = item.to_string_value();
if right_set.contains(&key) && !seen.contains(&key) {
seen.insert(key);
result.push(item);
}
}
result
}
pub fn except_atomic_values(left: Vec<XmlValue>, right: Vec<XmlValue>) -> Vec<XmlValue> {
let right_set: HashSet<String> = right.iter().map(|v| v.to_string_value()).collect();
let mut seen = HashSet::new();
let mut result = Vec::new();
for item in left {
let key = item.to_string_value();
if !right_set.contains(&key) && !seen.contains(&key) {
seen.insert(key);
result.push(item);
}
}
result
}
pub fn is_empty(values: &[XmlValue]) -> bool {
values.is_empty()
}
pub fn is_singleton(values: &[XmlValue]) -> bool {
values.len() == 1
}
pub fn head(values: &[XmlValue]) -> Option<&XmlValue> {
values.first()
}
pub fn tail(values: &[XmlValue]) -> &[XmlValue] {
if values.is_empty() {
&[]
} else {
&values[1..]
}
}
pub fn reverse(values: Vec<XmlValue>) -> Vec<XmlValue> {
let mut result = values;
result.reverse();
result
}
pub fn count(values: &[XmlValue]) -> usize {
values.len()
}
pub fn contains_value(values: &[XmlValue], target: &XmlValue) -> bool {
let target_str = target.to_string_value();
values.iter().any(|v| v.to_string_value() == target_str)
}
pub fn insert_before(values: Vec<XmlValue>, position: usize, item: XmlValue) -> Vec<XmlValue> {
let mut result = values;
let pos = position.min(result.len());
result.insert(pos, item);
result
}
pub fn remove_at(values: Vec<XmlValue>, position: usize) -> Vec<XmlValue> {
let mut result = values;
if position < result.len() {
result.remove(position);
}
result
}
pub fn subsequence(values: &[XmlValue], start: usize, length: Option<usize>) -> Vec<XmlValue> {
if start == 0 || start > values.len() {
return Vec::new();
}
let start_idx = start - 1; let end_idx = match length {
Some(len) => (start_idx + len).min(values.len()),
None => values.len(),
};
values[start_idx..end_idx].to_vec()
}
pub fn distinct_values(values: Vec<XmlValue>) -> Vec<XmlValue> {
let mut seen = HashSet::new();
let mut result = Vec::with_capacity(values.len());
for item in values {
let key = item.to_string_value();
if !seen.contains(&key) {
seen.insert(key);
result.push(item);
}
}
result
}
pub fn index_of(values: &[XmlValue], target: &XmlValue) -> usize {
let target_str = target.to_string_value();
for (i, v) in values.iter().enumerate() {
if v.to_string_value() == target_str {
return i + 1; }
}
0
}
pub fn deep_equal(left: &[XmlValue], right: &[XmlValue]) -> bool {
if left.len() != right.len() {
return false;
}
for (l, r) in left.iter().zip(right.iter()) {
if l.to_string_value() != r.to_string_value() {
return false;
}
}
true
}
#[deprecated(note = "Use union_atomic_values for atomic sequences or union_nodes for nodes")]
pub fn union_values(left: Vec<XmlValue>, right: Vec<XmlValue>) -> Vec<XmlValue> {
union_atomic_values(left, right)
}
#[deprecated(
note = "Use intersect_atomic_values for atomic sequences or intersect_nodes for nodes"
)]
pub fn intersect_values(left: Vec<XmlValue>, right: Vec<XmlValue>) -> Vec<XmlValue> {
intersect_atomic_values(left, right)
}
#[deprecated(note = "Use except_atomic_values for atomic sequences or except_nodes for nodes")]
pub fn except_values(left: Vec<XmlValue>, right: Vec<XmlValue>) -> Vec<XmlValue> {
except_atomic_values(left, right)
}
#[cfg(test)]
mod tests {
use super::*;
use num_bigint::BigInt;
fn int_values(nums: &[i32]) -> Vec<XmlValue> {
nums.iter()
.map(|&n| XmlValue::integer(BigInt::from(n)))
.collect()
}
fn str_values(strs: &[&str]) -> Vec<XmlValue> {
strs.iter().map(|&s| XmlValue::string(s)).collect()
}
#[test]
fn test_union_atomic() {
let left = int_values(&[1, 2, 3]);
let right = int_values(&[2, 3, 4]);
let result = union_atomic_values(left, right);
assert_eq!(count(&result), 4);
}
#[test]
fn test_intersect_atomic() {
let left = int_values(&[1, 2, 3]);
let right = int_values(&[2, 3, 4]);
let result = intersect_atomic_values(left, right);
assert_eq!(count(&result), 2);
}
#[test]
fn test_except_atomic() {
let left = int_values(&[1, 2, 3]);
let right = int_values(&[2, 3, 4]);
let result = except_atomic_values(left, right);
assert_eq!(count(&result), 1);
}
#[test]
fn test_is_empty() {
assert!(is_empty(&[]));
assert!(!is_empty(&int_values(&[1])));
}
#[test]
fn test_is_singleton() {
assert!(!is_singleton(&[]));
assert!(is_singleton(&int_values(&[1])));
assert!(!is_singleton(&int_values(&[1, 2])));
}
#[test]
fn test_head_tail() {
let values = int_values(&[1, 2, 3]);
assert_eq!(head(&values).unwrap().to_string_value(), "1");
assert_eq!(tail(&values).len(), 2);
let empty: Vec<XmlValue> = Vec::new();
assert!(head(&empty).is_none());
assert!(tail(&empty).is_empty());
}
#[test]
fn test_reverse() {
let values = int_values(&[1, 2, 3]);
let reversed = reverse(values);
assert_eq!(reversed[0].to_string_value(), "3");
assert_eq!(reversed[2].to_string_value(), "1");
}
#[test]
fn test_subsequence() {
let values = int_values(&[1, 2, 3, 4, 5]);
let sub = subsequence(&values, 2, Some(3));
assert_eq!(count(&sub), 3);
let sub = subsequence(&values, 3, None);
assert_eq!(count(&sub), 3);
let sub = subsequence(&values, 10, None);
assert!(is_empty(&sub));
}
#[test]
fn test_distinct_values() {
let values = str_values(&["a", "b", "a", "c", "b"]);
let distinct = distinct_values(values);
assert_eq!(count(&distinct), 3);
}
#[test]
fn test_index_of() {
let values = str_values(&["a", "b", "c"]);
assert_eq!(index_of(&values, &XmlValue::string("b")), 2);
assert_eq!(index_of(&values, &XmlValue::string("z")), 0);
}
#[test]
fn test_deep_equal() {
let a = int_values(&[1, 2, 3]);
let b = int_values(&[1, 2, 3]);
let c = int_values(&[1, 2, 4]);
let d = int_values(&[1, 2]);
assert!(deep_equal(&a, &b));
assert!(!deep_equal(&a, &c));
assert!(!deep_equal(&a, &d));
}
#[test]
fn test_insert_before() {
let values = int_values(&[1, 3]);
let result = insert_before(values, 1, XmlValue::integer(BigInt::from(2)));
assert_eq!(count(&result), 3);
assert_eq!(result[1].to_string_value(), "2");
}
#[test]
fn test_remove_at() {
let values = int_values(&[1, 2, 3]);
let result = remove_at(values, 1);
assert_eq!(count(&result), 2);
}
}