cynic_parser/common/
id_range.rs

1use std::{cmp::Ordering, iter::FusedIterator};
2
3#[derive(Clone, Copy)]
4/// A half open range of Ids.
5pub struct IdRange<Id> {
6    pub(crate) start: Id,
7    pub(crate) end: Id,
8}
9
10impl<Id> IdRange<Id>
11where
12    Id: IdOperations,
13{
14    pub fn is_empty(&self) -> bool {
15        self.len() == 0
16    }
17
18    pub fn len(&self) -> usize {
19        IdOperations::distance(self.start, self.end)
20    }
21}
22
23pub trait IdOperations: Copy {
24    fn empty_range() -> IdRange<Self>;
25    fn forward(self) -> Option<Self>;
26    fn back(self) -> Option<Self>;
27    fn cmp(self, other: Self) -> Ordering;
28    fn distance(lhs: Self, rhs: Self) -> usize;
29}
30
31impl<Id> IdRange<Id> {
32    pub(crate) fn new(start: Id, end: Id) -> Self {
33        IdRange { start, end }
34    }
35}
36
37impl<Id> Default for IdRange<Id>
38where
39    Id: IdOperations,
40{
41    fn default() -> Self {
42        Id::empty_range()
43    }
44}
45
46impl<Id> IntoIterator for IdRange<Id>
47where
48    Id: IdOperations,
49{
50    type Item = Id;
51
52    type IntoIter = IdRangeIter<Id>;
53
54    fn into_iter(self) -> Self::IntoIter {
55        IdRangeIter(self)
56    }
57}
58
59#[derive(Clone)]
60pub struct IdRangeIter<Id>(IdRange<Id>);
61
62impl<Id> IdRangeIter<Id> {
63    pub fn current_range(&self) -> IdRange<Id>
64    where
65        Id: Clone,
66    {
67        self.0.clone()
68    }
69}
70
71impl<Id> Iterator for IdRangeIter<Id>
72where
73    Id: IdOperations,
74{
75    type Item = Id;
76
77    fn next(&mut self) -> Option<Self::Item> {
78        if IdOperations::cmp(self.0.start, self.0.end).is_eq() {
79            return None;
80        }
81        let current = self.0.start;
82        self.0.start = self.0.start.forward()?;
83        Some(current)
84    }
85
86    fn size_hint(&self) -> (usize, Option<usize>) {
87        let size = IdOperations::distance(self.0.start, self.0.end);
88        (size, Some(size))
89    }
90}
91
92impl<Id> DoubleEndedIterator for IdRangeIter<Id>
93where
94    Id: IdOperations,
95{
96    fn next_back(&mut self) -> Option<Self::Item> {
97        if IdOperations::cmp(self.0.start, self.0.end).is_eq() {
98            return None;
99        }
100        let current = self.0.end.back()?;
101        self.0.end = current;
102        Some(current)
103    }
104}
105
106impl<Id> ExactSizeIterator for IdRangeIter<Id> where Id: IdOperations {}
107impl<Id> FusedIterator for IdRangeIter<Id> where Id: IdOperations {}
108
109#[cfg(test)]
110mod tests {
111    use super::{IdOperations, IdRange};
112
113    #[derive(Debug, Clone, Copy, Eq, PartialEq)]
114    struct TestId(u32);
115
116    #[test]
117    fn test_id_range() {
118        let default = IdRange::<TestId>::default();
119        assert_eq!(default.len(), 0);
120        assert_eq!(default.into_iter().len(), 0);
121        assert_eq!(default.into_iter().next(), None);
122
123        let range = IdRange::new(TestId(0), TestId(3));
124        assert_eq!(
125            range.into_iter().collect::<Vec<_>>(),
126            vec![TestId(0), TestId(1), TestId(2)]
127        );
128
129        assert_eq!(
130            range.into_iter().rev().collect::<Vec<_>>(),
131            vec![TestId(2), TestId(1), TestId(0)]
132        );
133    }
134
135    impl IdOperations for TestId {
136        fn empty_range() -> IdRange<Self> {
137            IdRange::new(TestId(0), TestId(0))
138        }
139
140        fn forward(self) -> Option<Self> {
141            Some(Self(self.0 + 1))
142        }
143
144        fn back(self) -> Option<Self> {
145            Some(Self(self.0 - 1))
146        }
147
148        fn cmp(self, other: Self) -> std::cmp::Ordering {
149            self.0.cmp(&other.0)
150        }
151
152        fn distance(lhs: Self, rhs: Self) -> usize {
153            rhs.0.saturating_sub(lhs.0) as usize
154        }
155    }
156}