use std::ops::{DerefMut, Deref}; /// A change tracker. This tracks changes of the data it owns. /// Tracking changes can cause false positives since it actually tracks calls to `deref_mut()` /// If you mutably dereference this, make sure its only when you change the data. pub struct Ct { data: T, changed: bool, } impl Ct { pub fn new(data: T) -> Self { Self { data, changed: false, } } /// Create a new change tracker with data pub fn new_true(data: T) -> Self { Self { data, changed: true, } } /// Returns true if there was a change. Will reset back to false after this is called. pub fn changed(&mut self) -> bool { let before = self.changed; self.reset(); before } /// Returns true if there was a change, will NOT reset back to false. pub fn peek_changed(&self) -> bool { self.changed } /// Resets the changed tracker to be false pub fn reset(&mut self) { self.changed = false; } /// Triggers the change tracker to be true pub fn trigger(&mut self) { self.changed = true; } /// Silently mutate the inner data, this wont be tracked pub fn silently_mutate(&mut self) -> &mut T { &mut self.data } /// Consumes self and returns the data stored inside. pub fn take(self) -> T { self.data } } impl Deref for Ct { type Target = T; fn deref(&self) -> &Self::Target { &self.data } } impl DerefMut for Ct { fn deref_mut(&mut self) -> &mut Self::Target { self.changed = true; &mut self.data } } #[cfg(test)] mod tests { use super::Ct; #[test] fn new() { let mut c = Ct::new(100); assert!(!c.changed()); let mut c = Ct::new_true(100); assert!(c.changed()); assert!(!c.changed()); // it should've reset itself } #[test] fn change_tracking() { let mut c = Ct::new(100); assert!(!c.changed()); *c = 10; assert!(c.changed()); assert!(!c.changed()); // should now be not changed } #[test] fn silent_mutate() { let mut c = Ct::new(100); let a = c.silently_mutate(); *a = 10; assert!(!c.changed()); } #[test] fn reset_trigger() { let mut c = Ct::new(100); *c = 10; c.reset(); assert!(!c.changed()); // should not be changed because of reset let mut c = Ct::new(100); c.trigger(); assert!(c.changed()); // should changed because of trigger assert!(!c.changed()); // should've reset itself } }