Crate raii_change_tracker [−] [src]
Track changes to data and notify listeners.
The main API feature is the DataTracker
struct, which takes ownership of a value.
To listen for changes to an instance of the DataTracker
struct, the
function
DataTracker::add_listener()
returns a futures::Stream
. This Stream
is supplied with previous and new
values immediately after each change and integrates with the
tokio ecosystem.
The principle of operation is that
DataTracker::as_tracked_mut()
returns a mutable Modifier
. Modifier
is a RAII
scoped guard with two key properties:
- It implements the
DerefMut
trait to allow ergonomic access to the underlying data. - It implements the
Drop
trait which checks if the underlying data was changed and, if so, notifies the listeners.
Futhermore,
DataTracker::as_ref()
returns a
(non-mutable) reference to the data for cases when only read-only access to
the data is needed.
To implement tracking, when Modifier
is created,
a copy of the original data is made and when Modifier
is dropped, an
equality check is performed. If the original and the new data are not equal,
the callbacks are called with references to the old and the new values.
example
extern crate futures; extern crate tokio_core; extern crate raii_change_tracker; use futures::{Future, IntoFuture, Stream}; use tokio_core::reactor::{Core, Timeout}; use std::time::Duration; use std::rc::Rc; use std::cell::RefCell; use raii_change_tracker::DataTracker; #[derive(Clone,PartialEq,Debug)] struct StoreType { val: i32, } fn main() { // Create our DataTracker instance. let data_store = DataTracker::new(StoreType { val: 123 }); // Wrap it so we can clone it. let data_store_rc = Rc::new(RefCell::new(data_store)); // Create a listener futures::Stream to receive all changes. let rx = data_store_rc.borrow_mut().add_listener(); // For each change notification, do this. let rx_printer = rx.for_each(|(old_value, new_value)| { // In this example, we just verify things work. assert!(old_value.val == 123); assert!(new_value.val == 124); futures::future::err(()) // return error to abort stream }); // Create an instance of a tokio reactor. let mut core = Core::new().unwrap(); // Clone our DataTracker instance. let dsclone = data_store_rc.clone(); // Create a timeout and then, when it fires, update the data store. let cause_change = Timeout::new(Duration::from_millis(0), &core.handle()) .into_future() .flatten() .and_then(move |_| { { let mut data_store = dsclone.borrow_mut(); let mut scoped_store = data_store.as_tracked_mut(); assert!((*scoped_store).val == 123); (*scoped_store).val += 1; } Ok(()) }) .map_err(|_| ()); // Run our futures in the tokio event loop. core.handle().spawn(cause_change); match core.run(rx_printer) { Ok(_) => unreachable!(), Err(()) => (), } // Check that the value was incremented. assert!(data_store_rc.borrow().as_ref().val == 124); }
Structs
DataTracker |
Tracks changes to data and notifies listeners. |
Modifier |
Allow viewing and modifying data owned by |