Skip to main content

xsd_schema/xpath/
item_set.rs

1// Microsoft Public License (Ms-PL)
2// See the file License.rtf or License.txt for the license details.
3
4// Copyright (c) 2011, Semyon A. Chertkov (semyonc@gmail.com)
5// All rights reserved.
6
7//! ItemSet - A dynamic collection for XPath items with sorting support.
8//!
9//! This module provides a collection type similar to C#'s ItemSet class,
10//! designed to store and manage items with efficient sorting capabilities.
11
12use std::cmp::Ordering;
13use std::ops::{Index, IndexMut};
14use std::slice;
15
16use super::error::XPathError;
17use super::iterator::XmlItem;
18use super::timsort::{timsort_slice_with_comparer, IComparer};
19use super::{DomNavigator, XmlNodeOrder};
20
21/// A dynamic, resizable collection for storing items with sorting support.
22///
23/// `ItemSet<T>` is a Vec-backed collection that provides:
24/// - Dynamic array storage with automatic capacity management
25/// - Efficient sorting using TimSort with custom comparers
26/// - Iterator support for traversing items
27/// - A completion flag for tracking collection state
28///
29/// This is a Rust port of the C# ItemSet class used in XPath 2.0 operations.
30#[derive(Debug, Clone)]
31pub struct ItemSet<T> {
32    items: Vec<T>,
33    completed: bool,
34}
35
36impl<T> Default for ItemSet<T> {
37    fn default() -> Self {
38        Self::new()
39    }
40}
41
42impl<T> ItemSet<T> {
43    /// Creates a new empty `ItemSet`.
44    pub fn new() -> Self {
45        ItemSet {
46            items: Vec::new(),
47            completed: false,
48        }
49    }
50
51    /// Creates a new `ItemSet` with the specified capacity.
52    pub fn with_capacity(capacity: usize) -> Self {
53        ItemSet {
54            items: Vec::with_capacity(capacity),
55            completed: false,
56        }
57    }
58
59    /// Returns the number of items in the collection.
60    #[inline]
61    pub fn len(&self) -> usize {
62        self.items.len()
63    }
64
65    /// Returns `true` if the collection is empty.
66    #[inline]
67    pub fn is_empty(&self) -> bool {
68        self.items.is_empty()
69    }
70
71    /// Returns the current capacity of the collection.
72    #[inline]
73    pub fn capacity(&self) -> usize {
74        self.items.capacity()
75    }
76
77    /// Sets the capacity of the collection.
78    ///
79    /// # Panics
80    ///
81    /// Panics if `capacity` is less than the current length.
82    pub fn set_capacity(&mut self, capacity: usize) {
83        if capacity < self.items.len() {
84            panic!("Capacity cannot be less than the current length");
85        }
86        if capacity > self.items.capacity() {
87            self.items.reserve(capacity - self.items.capacity());
88        }
89    }
90
91    /// Ensures the collection has at least the specified capacity.
92    fn ensure_capacity(&mut self, min: usize) {
93        if self.items.capacity() < min {
94            let new_capacity = if self.items.capacity() == 0 {
95                4
96            } else {
97                self.items.capacity() * 2
98            };
99            let new_capacity = new_capacity.max(min);
100            self.items.reserve(new_capacity - self.items.capacity());
101        }
102    }
103
104    /// Returns `true` if the collection has been marked as completed.
105    #[inline]
106    pub fn completed(&self) -> bool {
107        self.completed
108    }
109
110    /// Sets the completion flag.
111    #[inline]
112    pub fn set_completed(&mut self, value: bool) {
113        self.completed = value;
114    }
115
116    /// Returns a reference to the item at the specified index.
117    #[inline]
118    pub fn get(&self, index: usize) -> Option<&T> {
119        self.items.get(index)
120    }
121
122    /// Returns a mutable reference to the item at the specified index.
123    #[inline]
124    pub fn get_mut(&mut self, index: usize) -> Option<&mut T> {
125        self.items.get_mut(index)
126    }
127
128    /// Adds an item to the end of the collection.
129    pub fn add(&mut self, item: T) {
130        self.ensure_capacity(self.items.len() + 1);
131        self.items.push(item);
132    }
133
134    /// Removes all items from the collection.
135    pub fn clear(&mut self) {
136        self.items.clear();
137    }
138
139    /// Returns an iterator over the items.
140    #[inline]
141    pub fn iter(&self) -> ItemSetIter<'_, T> {
142        ItemSetIter {
143            inner: self.items.iter(),
144        }
145    }
146
147    /// Returns a mutable iterator over the items.
148    #[inline]
149    pub fn iter_mut(&mut self) -> ItemSetIterMut<'_, T> {
150        ItemSetIterMut {
151            inner: self.items.iter_mut(),
152        }
153    }
154
155    /// Returns the underlying items as a slice.
156    #[inline]
157    pub fn as_slice(&self) -> &[T] {
158        &self.items
159    }
160
161    /// Returns the underlying items as a mutable slice.
162    #[inline]
163    pub fn as_mut_slice(&mut self) -> &mut [T] {
164        &mut self.items
165    }
166}
167
168impl<T: Clone> ItemSet<T> {
169    /// Sorts the items using the provided comparer.
170    ///
171    /// Uses TimSort algorithm which is stable and efficient for partially sorted data.
172    pub fn sort_with<C: IComparer<T>>(&mut self, comparer: &C) {
173        timsort_slice_with_comparer(&mut self.items, comparer);
174    }
175}
176
177impl<T> Index<usize> for ItemSet<T> {
178    type Output = T;
179
180    fn index(&self, index: usize) -> &Self::Output {
181        &self.items[index]
182    }
183}
184
185impl<T> IndexMut<usize> for ItemSet<T> {
186    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
187        &mut self.items[index]
188    }
189}
190
191impl<T> IntoIterator for ItemSet<T> {
192    type Item = T;
193    type IntoIter = std::vec::IntoIter<T>;
194
195    fn into_iter(self) -> Self::IntoIter {
196        self.items.into_iter()
197    }
198}
199
200impl<'a, T> IntoIterator for &'a ItemSet<T> {
201    type Item = &'a T;
202    type IntoIter = ItemSetIter<'a, T>;
203
204    fn into_iter(self) -> Self::IntoIter {
205        self.iter()
206    }
207}
208
209impl<'a, T> IntoIterator for &'a mut ItemSet<T> {
210    type Item = &'a mut T;
211    type IntoIter = ItemSetIterMut<'a, T>;
212
213    fn into_iter(self) -> Self::IntoIter {
214        self.iter_mut()
215    }
216}
217
218impl<T> FromIterator<T> for ItemSet<T> {
219    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
220        let items: Vec<T> = iter.into_iter().collect();
221        ItemSet {
222            items,
223            completed: false,
224        }
225    }
226}
227
228impl<T> Extend<T> for ItemSet<T> {
229    fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
230        self.items.extend(iter);
231    }
232}
233
234/// An iterator over the items of an `ItemSet`.
235#[derive(Debug, Clone)]
236pub struct ItemSetIter<'a, T> {
237    inner: slice::Iter<'a, T>,
238}
239
240impl<'a, T> Iterator for ItemSetIter<'a, T> {
241    type Item = &'a T;
242
243    #[inline]
244    fn next(&mut self) -> Option<Self::Item> {
245        self.inner.next()
246    }
247
248    #[inline]
249    fn size_hint(&self) -> (usize, Option<usize>) {
250        self.inner.size_hint()
251    }
252}
253
254impl<T> ExactSizeIterator for ItemSetIter<'_, T> {}
255
256/// A mutable iterator over the items of an `ItemSet`.
257#[derive(Debug)]
258pub struct ItemSetIterMut<'a, T> {
259    inner: slice::IterMut<'a, T>,
260}
261
262impl<'a, T> Iterator for ItemSetIterMut<'a, T> {
263    type Item = &'a mut T;
264
265    #[inline]
266    fn next(&mut self) -> Option<Self::Item> {
267        self.inner.next()
268    }
269
270    #[inline]
271    fn size_hint(&self) -> (usize, Option<usize>) {
272        self.inner.size_hint()
273    }
274}
275
276impl<T> ExactSizeIterator for ItemSetIterMut<'_, T> {}
277
278// ============================================================================
279// XPath-specific Comparers
280// ============================================================================
281
282/// A comparer that sorts XPath nodes in document order.
283///
284/// This comparer implements the XPath document order comparison for nodes.
285/// It is used to sort node sequences returned by XPath operations like
286/// union, intersect, and except.
287///
288/// # Panics
289///
290/// Panics if either item is not a node (i.e., is an atomic value).
291#[derive(Debug, Clone, Copy, Default)]
292pub struct XPathComparer;
293
294impl XPathComparer {
295    /// Creates a new `XPathComparer`.
296    pub fn new() -> Self {
297        XPathComparer
298    }
299
300    /// Fallible comparison of two XmlItems in document order.
301    ///
302    /// Returns an error if either item is not a node.
303    pub fn try_compare<N: DomNavigator>(
304        &self,
305        x: &XmlItem<N>,
306        y: &XmlItem<N>,
307    ) -> Result<Ordering, XPathError> {
308        match (x, y) {
309            (XmlItem::Node(nav1), XmlItem::Node(nav2)) => match nav1.compare_position(nav2) {
310                XmlNodeOrder::Before => Ok(Ordering::Less),
311                XmlNodeOrder::After => Ok(Ordering::Greater),
312                XmlNodeOrder::Same => Ok(Ordering::Equal),
313                XmlNodeOrder::Unknown => Ok(Ordering::Equal),
314            },
315            _ => Err(XPathError::XPTY0004 {
316                expected: "node".to_string(),
317                found: "atomic value".to_string(),
318            }),
319        }
320    }
321}
322
323impl<N: DomNavigator> IComparer<XmlItem<N>> for XPathComparer {
324    fn compare(&self, x: &XmlItem<N>, y: &XmlItem<N>) -> Ordering {
325        match (x, y) {
326            (XmlItem::Node(nav1), XmlItem::Node(nav2)) => {
327                match nav1.compare_position(nav2) {
328                    XmlNodeOrder::Before => Ordering::Less,
329                    XmlNodeOrder::After => Ordering::Greater,
330                    XmlNodeOrder::Same => Ordering::Equal,
331                    XmlNodeOrder::Unknown => {
332                        // Different documents - compare by some stable ordering
333                        // In C#, this compares hash codes of document roots.
334                        // For now, we treat unknown as equal to maintain stability.
335                        // This could be enhanced to use document identifiers if available.
336                        Ordering::Equal
337                    }
338                }
339            }
340            _ => panic!("Cannot compare non-node items in document order (XPTY0004)"),
341        }
342    }
343}
344
345/// A comparer for checking XPath node equality.
346///
347/// This comparer checks if two nodes are at the same position in the document.
348#[derive(Debug, Clone, Copy, Default)]
349pub struct XPathEqualityComparer;
350
351impl XPathEqualityComparer {
352    /// Creates a new `XPathEqualityComparer`.
353    pub fn new() -> Self {
354        XPathEqualityComparer
355    }
356
357    /// Checks if two XmlItems are equal (same node position).
358    ///
359    /// # Panics
360    ///
361    /// Panics if either item is not a node.
362    pub fn equals<N: DomNavigator>(&self, x: &XmlItem<N>, y: &XmlItem<N>) -> bool {
363        match (x, y) {
364            (XmlItem::Node(nav1), XmlItem::Node(nav2)) => nav1.is_same_position(nav2),
365            _ => panic!("Cannot compare non-node items for position equality (XPTY0004)"),
366        }
367    }
368
369    /// Fallible equality check for two XmlItems.
370    ///
371    /// Returns an error if either item is not a node.
372    pub fn try_equals<N: DomNavigator>(
373        &self,
374        x: &XmlItem<N>,
375        y: &XmlItem<N>,
376    ) -> Result<bool, XPathError> {
377        match (x, y) {
378            (XmlItem::Node(nav1), XmlItem::Node(nav2)) => Ok(nav1.is_same_position(nav2)),
379            _ => Err(XPathError::XPTY0004 {
380                expected: "node".to_string(),
381                found: "atomic value".to_string(),
382            }),
383        }
384    }
385}
386
387#[cfg(test)]
388mod tests {
389    use super::*;
390    use crate::xpath::timsort::{OrdComparer, ReverseComparer};
391
392    #[test]
393    fn test_new() {
394        let set: ItemSet<i32> = ItemSet::new();
395        assert!(set.is_empty());
396        assert_eq!(set.len(), 0);
397    }
398
399    #[test]
400    fn test_with_capacity() {
401        let set: ItemSet<i32> = ItemSet::with_capacity(10);
402        assert!(set.is_empty());
403        assert!(set.capacity() >= 10);
404    }
405
406    #[test]
407    fn test_add() {
408        let mut set = ItemSet::new();
409        set.add(1);
410        set.add(2);
411        set.add(3);
412        assert_eq!(set.len(), 3);
413        assert_eq!(set[0], 1);
414        assert_eq!(set[1], 2);
415        assert_eq!(set[2], 3);
416    }
417
418    #[test]
419    fn test_clear() {
420        let mut set = ItemSet::new();
421        set.add(1);
422        set.add(2);
423        set.clear();
424        assert!(set.is_empty());
425    }
426
427    #[test]
428    fn test_get() {
429        let mut set = ItemSet::new();
430        set.add(42);
431        assert_eq!(set.get(0), Some(&42));
432        assert_eq!(set.get(1), None);
433    }
434
435    #[test]
436    fn test_get_mut() {
437        let mut set = ItemSet::new();
438        set.add(42);
439        if let Some(item) = set.get_mut(0) {
440            *item = 100;
441        }
442        assert_eq!(set[0], 100);
443    }
444
445    #[test]
446    fn test_completed() {
447        let mut set: ItemSet<i32> = ItemSet::new();
448        assert!(!set.completed());
449        set.set_completed(true);
450        assert!(set.completed());
451    }
452
453    #[test]
454    fn test_sort_with_ord_comparer() {
455        let mut set = ItemSet::new();
456        set.add(3);
457        set.add(1);
458        set.add(4);
459        set.add(1);
460        set.add(5);
461
462        let comparer = OrdComparer::<i32>::new();
463        set.sort_with(&comparer);
464
465        assert_eq!(set[0], 1);
466        assert_eq!(set[1], 1);
467        assert_eq!(set[2], 3);
468        assert_eq!(set[3], 4);
469        assert_eq!(set[4], 5);
470    }
471
472    #[test]
473    fn test_sort_with_reverse_comparer() {
474        let mut set = ItemSet::new();
475        set.add(1);
476        set.add(2);
477        set.add(3);
478
479        let comparer = ReverseComparer::new(OrdComparer::<i32>::new());
480        set.sort_with(&comparer);
481
482        assert_eq!(set[0], 3);
483        assert_eq!(set[1], 2);
484        assert_eq!(set[2], 1);
485    }
486
487    #[test]
488    fn test_iter() {
489        let mut set = ItemSet::new();
490        set.add(1);
491        set.add(2);
492        set.add(3);
493
494        let collected: Vec<_> = set.iter().cloned().collect();
495        assert_eq!(collected, vec![1, 2, 3]);
496    }
497
498    #[test]
499    fn test_into_iter() {
500        let mut set = ItemSet::new();
501        set.add(1);
502        set.add(2);
503        set.add(3);
504
505        let collected: Vec<_> = set.into_iter().collect();
506        assert_eq!(collected, vec![1, 2, 3]);
507    }
508
509    #[test]
510    fn test_from_iter() {
511        let set: ItemSet<i32> = vec![1, 2, 3].into_iter().collect();
512        assert_eq!(set.len(), 3);
513        assert_eq!(set[0], 1);
514        assert_eq!(set[1], 2);
515        assert_eq!(set[2], 3);
516    }
517
518    #[test]
519    fn test_extend() {
520        let mut set = ItemSet::new();
521        set.add(1);
522        set.extend(vec![2, 3, 4]);
523        assert_eq!(set.len(), 4);
524    }
525
526    #[test]
527    fn test_index() {
528        let mut set = ItemSet::new();
529        set.add(42);
530        assert_eq!(set[0], 42);
531    }
532
533    #[test]
534    fn test_index_mut() {
535        let mut set = ItemSet::new();
536        set.add(42);
537        set[0] = 100;
538        assert_eq!(set[0], 100);
539    }
540
541    #[test]
542    fn test_default() {
543        let set: ItemSet<i32> = ItemSet::default();
544        assert!(set.is_empty());
545    }
546}