#[derive(Debug, Clone)]
pub struct DirtyState {
generation: u64,
save_point: u64,
}
impl DirtyState {
#[must_use]
pub fn new() -> Self {
Self {
generation: 0,
save_point: 0,
}
}
#[inline]
pub fn mark_dirty(&mut self) {
self.generation += 1;
tracing::trace!(generation = self.generation, "marked dirty");
}
#[inline]
pub fn mark_clean(&mut self) {
self.save_point = self.generation;
tracing::debug!(save_point = self.save_point, "marked clean");
}
#[must_use]
#[inline]
pub fn is_dirty(&self) -> bool {
self.generation != self.save_point
}
#[must_use]
#[inline]
pub fn is_clean(&self) -> bool {
!self.is_dirty()
}
#[must_use]
#[inline]
pub fn generation(&self) -> u64 {
self.generation
}
#[must_use]
#[inline]
pub fn save_point(&self) -> u64 {
self.save_point
}
#[must_use]
#[inline]
pub fn changes_since_save(&self) -> u64 {
self.generation.saturating_sub(self.save_point)
}
pub fn reset(&mut self) {
self.generation = 0;
self.save_point = 0;
tracing::debug!("dirty state reset");
}
}
impl Default for DirtyState {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn new_is_clean() {
let state = DirtyState::new();
assert!(state.is_clean());
assert!(!state.is_dirty());
assert_eq!(state.changes_since_save(), 0);
}
#[test]
fn mark_dirty() {
let mut state = DirtyState::new();
state.mark_dirty();
assert!(state.is_dirty());
assert_eq!(state.generation(), 1);
assert_eq!(state.changes_since_save(), 1);
}
#[test]
fn mark_clean_after_dirty() {
let mut state = DirtyState::new();
state.mark_dirty();
state.mark_dirty();
state.mark_clean();
assert!(state.is_clean());
assert_eq!(state.changes_since_save(), 0);
}
#[test]
fn dirty_after_save_then_edit() {
let mut state = DirtyState::new();
state.mark_dirty();
state.mark_clean();
assert!(state.is_clean());
state.mark_dirty();
assert!(state.is_dirty());
assert_eq!(state.changes_since_save(), 1);
}
#[test]
fn multiple_edits_between_saves() {
let mut state = DirtyState::new();
for _ in 0..10 {
state.mark_dirty();
}
assert_eq!(state.changes_since_save(), 10);
state.mark_clean();
assert_eq!(state.changes_since_save(), 0);
}
#[test]
fn reset() {
let mut state = DirtyState::new();
state.mark_dirty();
state.mark_dirty();
state.mark_clean();
state.mark_dirty();
state.reset();
assert!(state.is_clean());
assert_eq!(state.generation(), 0);
assert_eq!(state.save_point(), 0);
}
#[test]
fn default_is_clean() {
let state = DirtyState::default();
assert!(state.is_clean());
}
}