cynic_parser/common/
id_range.rs1use std::{cmp::Ordering, iter::FusedIterator};
2
3#[derive(Clone, Copy)]
4pub 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}