#![deny(missing_docs)]
use thiserror::Error;
type ChangeFunction<T> = Box<dyn Fn(&T, &T) -> bool + Send + Sync + 'static>;
pub trait Sender<T> {
fn send_response(self, data: T);
}
pub fn equality_change_function<T: PartialEq>() -> ChangeFunction<T> {
Box::new(|old: &T, new: &T| old != new)
}
pub struct WatchHandler<T, ST> {
watch_responder: Option<ST>,
current_value: Option<T>,
last_sent_value: Option<T>,
change_function: ChangeFunction<T>,
}
impl<T, ST> WatchHandler<T, ST>
where
T: Clone + PartialEq + 'static,
ST: Sender<T> + 'static,
{
pub fn create(initial_value: Option<T>) -> Self {
Self::create_with_change_fn(equality_change_function(), initial_value)
}
}
impl<T, ST> WatchHandler<T, ST>
where
T: Clone + 'static,
ST: Sender<T> + 'static,
{
pub fn create_with_change_fn(
change_function: ChangeFunction<T>,
initial_value: Option<T>,
) -> Self {
Self {
watch_responder: None,
last_sent_value: None,
current_value: initial_value,
change_function: change_function,
}
}
pub fn watch(&mut self, responder: ST) -> Result<(), WatchError<ST>> {
if let None = self.watch_responder {
self.watch_responder = Some(responder);
self.send_if_needed();
Ok(())
} else {
Err(WatchError { responder })
}
}
pub fn set_change_function(&mut self, change_function: ChangeFunction<T>) {
self.change_function = change_function;
self.send_if_needed();
}
pub fn set_value(&mut self, new_value: T) {
self.current_value = Some(new_value);
self.send_if_needed();
}
fn send_if_needed(&mut self) {
let value_to_send = match (self.last_sent_value.as_ref(), self.current_value.as_ref()) {
(Some(last), Some(current)) if (self.change_function)(last, current) => Some(current),
(Some(_), Some(_)) => None,
(None, Some(current)) => Some(current),
(_, None) => None,
};
if let Some(value) = value_to_send {
if let Some(responder) = self.watch_responder.take() {
responder.send_response(value.clone());
self.last_sent_value = Some(value.clone());
}
}
}
}
#[derive(Error)]
#[error("Inconsistent state; existing handler in state")]
pub struct WatchError<ST> {
pub responder: ST,
}
impl<ST> std::fmt::Debug for WatchError<ST> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
write!(f, "{}", self)
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::cell::RefCell;
use std::rc::Rc;
const ID_INVALID: i32 = 0;
const ID1: i32 = 1;
const ID2: i32 = 2;
const ID3: i32 = 3;
const ID4: i32 = 4;
#[derive(Clone, PartialEq)]
struct TestStruct {
id: i32,
}
#[derive(Debug, PartialEq)]
struct TestSender {
sent_value: Rc<RefCell<i32>>,
}
impl Sender<TestStruct> for TestSender {
fn send_response(self, data: TestStruct) {
self.sent_value.replace(data.id);
}
}
#[test]
fn test_watch() {
let mut handler =
WatchHandler::<TestStruct, TestSender>::create(Some(TestStruct { id: ID1 }));
let sent_value = Rc::new(RefCell::new(ID_INVALID));
handler.watch(TestSender { sent_value: sent_value.clone() }).unwrap();
assert_eq!((*sent_value.borrow()), ID1);
let sent_value = Rc::new(RefCell::new(ID_INVALID));
handler.watch(TestSender { sent_value: sent_value.clone() }).unwrap();
assert_eq!((*sent_value.borrow()), ID_INVALID);
handler.set_value(TestStruct { id: ID2 });
assert_eq!((*sent_value.borrow()), ID2);
handler.set_value(TestStruct { id: ID3 });
let sent_value = Rc::new(RefCell::new(ID_INVALID));
handler.watch(TestSender { sent_value: sent_value.clone() }).unwrap();
assert_eq!((*sent_value.borrow()), ID3);
}
#[test]
fn test_watch_no_initial() {
let mut handler = WatchHandler::<TestStruct, TestSender>::create(None);
let sent_value = Rc::new(RefCell::new(ID_INVALID));
handler.watch(TestSender { sent_value: sent_value.clone() }).unwrap();
assert_eq!((*sent_value.borrow()), ID_INVALID);
handler.set_value(TestStruct { id: ID1 });
assert_eq!((*sent_value.borrow()), ID1);
let mut handler = WatchHandler::<TestStruct, TestSender>::create(None);
handler.set_value(TestStruct { id: ID2 });
let sent_value = Rc::new(RefCell::new(ID_INVALID));
handler.watch(TestSender { sent_value: sent_value.clone() }).unwrap();
assert_eq!((*sent_value.borrow()), ID2);
}
#[test]
fn test_watch_fails() {
let mut handler =
WatchHandler::<TestStruct, TestSender>::create(Some(TestStruct { id: ID1 }));
let sent_value = Rc::new(RefCell::new(ID_INVALID));
handler.watch(TestSender { sent_value: sent_value.clone() }).unwrap();
let sent_value = Rc::new(RefCell::new(ID_INVALID));
handler.watch(TestSender { sent_value: sent_value.clone() }).unwrap();
let sent_value = Rc::new(RefCell::new(ID4));
let result = handler.watch(TestSender { sent_value: sent_value.clone() });
assert_eq!(result.unwrap_err().responder, TestSender { sent_value: sent_value.clone() });
}
#[test]
fn test_watch_with_change_function() {
let mut handler = WatchHandler::<TestStruct, TestSender>::create_with_change_fn(
equality_change_function(),
Some(TestStruct { id: ID1 }),
);
let sent_value = Rc::new(RefCell::new(ID_INVALID));
handler.set_change_function(Box::new(|_old, _new| false));
handler.watch(TestSender { sent_value: sent_value.clone() }).unwrap();
assert_eq!((*sent_value.borrow()), ID1);
let sent_value = Rc::new(RefCell::new(ID_INVALID));
handler.set_change_function(Box::new(|old, new| new.id - old.id > 1));
handler.watch(TestSender { sent_value: sent_value.clone() }).unwrap();
handler.set_value(TestStruct { id: ID2 });
assert_eq!((*sent_value.borrow()), ID_INVALID);
handler.set_value(TestStruct { id: ID3 });
assert_eq!((*sent_value.borrow()), ID3);
let sent_value = Rc::new(RefCell::new(ID_INVALID));
handler.set_change_function(Box::new(|_old, _new| false));
handler.watch(TestSender { sent_value: sent_value.clone() }).unwrap();
handler.set_value(TestStruct { id: ID4 });
assert_eq!((*sent_value.borrow()), ID_INVALID);
handler.set_change_function(Box::new(|_old, _new| true));
assert_eq!((*sent_value.borrow()), ID4);
}
#[test]
fn test_watch_with_change_fn_no_initial() {
let mut handler = WatchHandler::<TestStruct, TestSender>::create_with_change_fn(
Box::new(|_old, _new| false),
None,
);
let sent_value = Rc::new(RefCell::new(ID_INVALID));
handler.watch(TestSender { sent_value: sent_value.clone() }).unwrap();
assert_eq!((*sent_value.borrow()), ID_INVALID);
handler.set_value(TestStruct { id: ID1 });
assert_eq!((*sent_value.borrow()), ID1);
let mut handler = WatchHandler::<TestStruct, TestSender>::create_with_change_fn(
Box::new(|_old, _new| false),
None,
);
handler.set_value(TestStruct { id: ID2 });
let sent_value = Rc::new(RefCell::new(ID_INVALID));
handler.watch(TestSender { sent_value: sent_value.clone() }).unwrap();
assert_eq!((*sent_value.borrow()), ID2);
}
}