use crate::{
Error, MAX_RECORDS, Record, Store,
parser::{AppId, MAX_APP, Report},
};
#[derive(Clone, Copy, Debug)]
#[non_exhaustive]
pub enum Change<'a> {
Set {
record: Record<'a>,
effective_app: Option<AppId<'a>>,
},
Remove(Record<'a>),
}
#[derive(Clone, Copy, Eq, PartialEq)]
struct AppSnapshot {
bytes: [u8; MAX_APP],
}
impl AppSnapshot {
fn new(app: AppId<'_>) -> Self {
let value = app.as_str().as_bytes();
let mut snapshot = Self {
bytes: [0; MAX_APP],
};
snapshot.bytes[..value.len()].copy_from_slice(value);
snapshot
}
fn matches(&self, app: AppId<'_>) -> bool {
self == &Self::new(app)
}
}
impl Store<'_> {
pub fn apply_with_changes(
&mut self,
report: Report<'_>,
mut on_change: impl FnMut(Change<'_>),
) -> Result<(), Error> {
let mut apps = [None; MAX_RECORDS];
self.snapshot_apps(&mut apps);
let changed = self.apply_inner(report, |record| on_change(Change::Remove(record)), true)?;
self.notify_sets(&apps, changed, &mut on_change);
Ok(())
}
pub fn on_prompt_with_changes(&mut self, mut on_change: impl FnMut(Change<'_>)) {
let mut apps = [None; MAX_RECORDS];
self.snapshot_apps(&mut apps);
self.remove_active(&mut |record| on_change(Change::Remove(record)));
self.notify_sets(&apps, None, &mut on_change);
}
pub fn on_process_exit_with_changes(&mut self, on_change: impl FnMut(Change<'_>)) {
self.on_prompt_with_changes(on_change);
}
pub fn on_full_reset_with_changes(&mut self, mut on_change: impl FnMut(Change<'_>)) {
self.remove_matching(|_| true, &mut |record| on_change(Change::Remove(record)));
}
fn snapshot_apps(&self, apps: &mut [Option<AppSnapshot>; MAX_RECORDS]) {
for (index, slot) in self.slots.iter().enumerate() {
apps[index] = slot
.record
.as_ref()
.and_then(|record| self.effective_app(record.view().id()))
.map(AppSnapshot::new);
}
}
fn notify_sets(
&self,
apps: &[Option<AppSnapshot>; MAX_RECORDS],
changed: Option<usize>,
on_change: &mut impl FnMut(Change<'_>),
) {
for (index, slot) in self.slots.iter().enumerate() {
let Some(record) = slot.record.as_ref().map(super::record::StoredRecord::view) else {
continue;
};
let effective_app = self.effective_app(record.id());
let same_app = match (&apps[index], effective_app) {
(Some(previous), Some(app)) => previous.matches(app),
(None, None) => true,
_ => false,
};
if changed == Some(index) || !same_app {
on_change(Change::Set {
record,
effective_app,
});
}
}
}
}