1use std::collections::VecDeque;
22use std::error::Error;
23use std::fmt::{self, Debug, Display, Formatter};
24use std::iter::FromIterator;
25
26#[derive(Clone, Debug, Default, PartialEq, Eq)]
28pub struct BiteQueue<T> {
29 items: VecDeque<T>,
30}
31
32impl<T> BiteQueue<T> {
33 pub const fn new() -> Self {
35 Self {
36 items: VecDeque::new(),
37 }
38 }
39
40 pub fn with_capacity(capacity: usize) -> Self {
42 Self {
43 items: VecDeque::with_capacity(capacity),
44 }
45 }
46
47 pub fn push(&mut self, item: T) {
49 self.items.push_back(item);
50 }
51
52 pub fn front(&self) -> Option<&T> {
54 self.items.front()
55 }
56
57 pub fn len(&self) -> usize {
59 self.items.len()
60 }
61
62 pub fn is_empty(&self) -> bool {
64 self.items.is_empty()
65 }
66
67 pub fn iter(&self) -> impl ExactSizeIterator<Item = &T> + DoubleEndedIterator {
69 self.items.iter()
70 }
71
72 pub fn bite(&mut self, maximum: usize) -> Result<Bite<'_, T>, BiteError> {
76 if maximum == 0 {
77 return Err(BiteError::ZeroSize);
78 }
79 let amount = maximum.min(self.items.len());
80 Ok(self.take_prefix(amount))
81 }
82
83 pub fn bite_exact(&mut self, amount: usize) -> Result<Bite<'_, T>, BiteError> {
85 if amount == 0 {
86 return Err(BiteError::ZeroSize);
87 }
88 if self.items.len() < amount {
89 return Err(BiteError::InsufficientItems {
90 requested: amount,
91 available: self.items.len(),
92 });
93 }
94 Ok(self.take_prefix(amount))
95 }
96
97 pub fn into_inner(self) -> VecDeque<T> {
99 self.items
100 }
101
102 fn take_prefix(&mut self, amount: usize) -> Bite<'_, T> {
103 let items = self.items.drain(..amount).collect();
104 Bite {
105 source: self,
106 items,
107 finished: false,
108 }
109 }
110}
111
112impl<T> Extend<T> for BiteQueue<T> {
113 fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
114 self.items.extend(iter);
115 }
116}
117
118impl<T> FromIterator<T> for BiteQueue<T> {
119 fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
120 Self {
121 items: iter.into_iter().collect(),
122 }
123 }
124}
125
126impl<T> IntoIterator for BiteQueue<T> {
127 type Item = T;
128 type IntoIter = std::collections::vec_deque::IntoIter<T>;
129
130 fn into_iter(self) -> Self::IntoIter {
131 self.items.into_iter()
132 }
133}
134
135impl<'a, T> IntoIterator for &'a BiteQueue<T> {
136 type Item = &'a T;
137 type IntoIter = std::collections::vec_deque::Iter<'a, T>;
138
139 fn into_iter(self) -> Self::IntoIter {
140 self.items.iter()
141 }
142}
143
144#[must_use = "an uncommitted bite automatically rolls back"]
149pub struct Bite<'a, T> {
150 source: &'a mut BiteQueue<T>,
151 items: VecDeque<T>,
152 finished: bool,
153}
154
155impl<T: Debug> Debug for Bite<'_, T> {
156 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
157 f.debug_struct("Bite")
158 .field("items", &self.items)
159 .field("finished", &self.finished)
160 .finish_non_exhaustive()
161 }
162}
163
164impl<T> Bite<'_, T> {
165 pub fn len(&self) -> usize {
167 self.items.len()
168 }
169
170 pub fn is_empty(&self) -> bool {
175 self.items.is_empty()
176 }
177
178 pub fn front(&self) -> Option<&T> {
180 self.items.front()
181 }
182
183 pub fn iter(&self) -> impl ExactSizeIterator<Item = &T> + DoubleEndedIterator {
185 self.items.iter()
186 }
187
188 pub fn commit(mut self) -> Vec<T> {
190 self.finished = true;
191 self.items.drain(..).collect()
192 }
193
194 pub fn rollback(mut self) {
199 self.restore();
200 }
201
202 fn restore(&mut self) {
203 while let Some(item) = self.items.pop_back() {
204 self.source.items.push_front(item);
205 }
206 self.finished = true;
207 }
208}
209
210impl<T> Drop for Bite<'_, T> {
211 fn drop(&mut self) {
212 if !self.finished {
213 self.restore();
214 }
215 }
216}
217
218impl<'a, T> IntoIterator for &'a Bite<'_, T> {
219 type Item = &'a T;
220 type IntoIter = std::collections::vec_deque::Iter<'a, T>;
221
222 fn into_iter(self) -> Self::IntoIter {
223 self.items.iter()
224 }
225}
226
227#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
229#[non_exhaustive]
230pub enum BiteError {
231 ZeroSize,
233 InsufficientItems {
235 requested: usize,
237 available: usize,
239 },
240}
241
242impl Display for BiteError {
243 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
244 match self {
245 Self::ZeroSize => f.write_str("bite size must be greater than zero"),
246 Self::InsufficientItems {
247 requested,
248 available,
249 } => write!(
250 f,
251 "requested {requested} items, but only {available} are available"
252 ),
253 }
254 }
255}
256
257impl Error for BiteError {}
258
259#[cfg(test)]
260mod tests {
261 use super::*;
262 use std::panic::{AssertUnwindSafe, catch_unwind};
263
264 fn values(queue: &BiteQueue<i32>) -> Vec<i32> {
265 queue.iter().copied().collect()
266 }
267
268 #[test]
269 fn commit_permanently_consumes_a_prefix() {
270 let mut queue = BiteQueue::from_iter([1, 2, 3, 4]);
271 let committed = queue.bite(2).unwrap().commit();
272 assert_eq!(committed, [1, 2]);
273 assert_eq!(values(&queue), [3, 4]);
274 }
275
276 #[test]
277 fn drop_rolls_back_in_original_order() {
278 let mut queue = BiteQueue::from_iter([1, 2, 3]);
279 {
280 let bite = queue.bite(2).unwrap();
281 assert_eq!(bite.iter().copied().collect::<Vec<_>>(), [1, 2]);
282 }
283 assert_eq!(values(&queue), [1, 2, 3]);
284 }
285
286 #[test]
287 fn explicit_rollback_restores_the_prefix() {
288 let mut queue = BiteQueue::from_iter([1, 2, 3]);
289 queue.bite(2).unwrap().rollback();
290 assert_eq!(values(&queue), [1, 2, 3]);
291 }
292
293 #[test]
294 fn panic_unwinding_rolls_back() {
295 let mut queue = BiteQueue::from_iter([1, 2, 3]);
296 let result = catch_unwind(AssertUnwindSafe(|| {
297 let _bite = queue.bite(2).unwrap();
298 panic!("processing failed");
299 }));
300 assert!(result.is_err());
301 assert_eq!(values(&queue), [1, 2, 3]);
302 }
303
304 #[test]
305 fn bite_uses_at_most_the_requested_size() {
306 let mut queue = BiteQueue::from_iter([1, 2]);
307 let bite = queue.bite(10).unwrap();
308 assert_eq!(bite.len(), 2);
309 }
310
311 #[test]
312 fn exact_failure_does_not_change_the_queue() {
313 let mut queue = BiteQueue::from_iter([1, 2]);
314 assert_eq!(
315 queue.bite_exact(3).unwrap_err(),
316 BiteError::InsufficientItems {
317 requested: 3,
318 available: 2,
319 }
320 );
321 assert_eq!(values(&queue), [1, 2]);
322 }
323
324 #[test]
325 fn zero_size_is_rejected() {
326 let mut queue = BiteQueue::<i32>::new();
327 assert_eq!(queue.bite(0).unwrap_err(), BiteError::ZeroSize);
328 assert_eq!(queue.bite_exact(0).unwrap_err(), BiteError::ZeroSize);
329 }
330}