use alloc::vec::Vec;
pub(crate) struct DropInReverse<'a, T>(pub(crate) &'a mut Vec<T>);
impl<T> Drop for DropInReverse<'_, T> {
fn drop(&mut self) {
while let Some(item) = self.0.pop() {
drop(item);
}
}
}
#[inline]
pub(crate) fn pop_all<T>(vec: &mut Vec<T>, mut f: impl FnMut(T)) {
let guard = DropInReverse(vec);
while let Some(item) = guard.0.pop() {
f(item);
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::rc::Rc;
use core::cell::RefCell;
use std::panic::{AssertUnwindSafe, catch_unwind};
struct Item {
index: usize,
panics: bool,
log: Rc<RefCell<Vec<usize>>>,
}
impl Drop for Item {
fn drop(&mut self) {
self.log.borrow_mut().push(self.index);
if self.panics {
panic!("item {} panics", self.index);
}
}
}
fn items(log: &Rc<RefCell<Vec<usize>>>, panics: Option<usize>) -> Vec<Item> {
(0..5)
.map(|index| Item {
index,
panics: panics == Some(index),
log: log.clone(),
})
.collect()
}
#[test]
fn test_pop_all() {
let log = Rc::new(RefCell::new(Vec::new()));
let mut vec = items(&log, None);
pop_all(&mut vec, drop);
assert_eq!(*log.borrow(), [4, 3, 2, 1, 0]);
assert!(vec.is_empty());
}
#[test]
fn test_pop_all_panics() {
for panics in 0..5 {
let log = Rc::new(RefCell::new(Vec::new()));
let mut vec = items(&log, Some(panics));
let rv = catch_unwind(AssertUnwindSafe(|| pop_all(&mut vec, drop)));
assert!(rv.is_err());
assert_eq!(*log.borrow(), [4, 3, 2, 1, 0]);
assert!(vec.is_empty());
}
}
}