use std::cell::Cell;
use std::mem;
use std::thread_local;
use std::vec::Vec;
use super::Unfit;
const CAP: usize = 15;
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct Skips {
pub kept: Vec<Unfit>,
pub dropped: usize,
}
impl Skips {
pub fn is_empty(&self) -> bool {
self.kept.is_empty() && self.dropped == 0
}
pub fn total(&self) -> usize {
self.kept.len().saturating_add(self.dropped)
}
fn note(&mut self, unfit: Unfit) {
match self.kept.len() < CAP {
true => self.kept.push(unfit),
false => self.dropped = self.dropped.saturating_add(1),
}
}
}
thread_local! {
static SLOT: Cell<Option<Skips>> = const { Cell::new(None) };
}
fn exchange(next: Option<Skips>) -> Option<Skips> {
SLOT.try_with(|slot| slot.replace(next)).ok().flatten()
}
fn with<T>(f: impl FnOnce(&mut Skips) -> T) -> Option<T> {
let mut skips = exchange(None)?;
let out = f(&mut skips);
exchange(Some(skips));
Some(out)
}
pub(super) fn note(unfit: Unfit) {
with(|s| s.note(unfit));
}
#[derive(Clone, Copy)]
pub(super) struct Mark {
kept: usize,
dropped: usize,
}
pub(super) fn mark() -> Mark {
with(|s| Mark {
kept: s.kept.len(),
dropped: s.dropped,
})
.unwrap_or(Mark {
kept: 0,
dropped: 0,
})
}
pub(super) fn lead(mark: Mark, step: impl Fn(Unfit) -> Unfit) {
with(|s| {
for kept in s.kept.iter_mut().skip(mark.kept) {
let unfit = mem::replace(kept, Unfit::absent());
*kept = step(unfit);
}
});
}
#[derive(Default)]
pub(super) struct Taken(Skips);
pub(super) fn take(mark: Mark) -> Taken {
with(|s| {
let kept = s.kept.split_off(mark.kept.min(s.kept.len()));
let dropped = mem::replace(&mut s.dropped, mark.dropped).saturating_sub(mark.dropped);
Taken(Skips { kept, dropped })
})
.unwrap_or_default()
}
pub(super) fn restore(taken: Taken) {
with(|s| {
taken.0.kept.into_iter().for_each(|u| s.note(u));
s.dropped = s.dropped.saturating_add(taken.0.dropped);
});
}
struct Previous(Option<Skips>);
impl Drop for Previous {
fn drop(&mut self) {
exchange(self.0.take());
}
}
pub(super) fn collect<T, E>(
read: impl FnOnce() -> Result<T, E>,
lead: impl Fn(Unfit) -> Unfit,
) -> Result<(T, Skips), E> {
let _previous = Previous(exchange(Some(Skips::default())));
let value = read()?;
let mut skips = exchange(None).unwrap_or_default();
skips.kept = skips.kept.into_iter().map(lead).collect();
Ok((value, skips))
}
impl std::fmt::Display for Skips {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for (i, unfit) in self.kept.iter().enumerate() {
let sep = if i == 0 { "" } else { "; " };
write!(f, "{sep}{unfit}")?;
}
match self.dropped {
0 => Ok(()),
n => write!(f, " and {n} more"),
}
}
}