use std::{
fmt,
ops::{Deref, DerefMut},
};
use crate::{DefaultOwner, WatchedCellCore, WatchedCore};
#[derive(Default)]
pub struct Watched<T: ?Sized> {
inner: WatchedCore<'static, T, DefaultOwner>,
}
impl<T> Watched<T> {
pub fn new(value: T) -> Self {
Watched {
inner: WatchedCore::new(value),
}
}
pub fn into_inner(this: Self) -> T {
this.inner.into_inner()
}
pub fn replace(this: &mut Self, value: T) -> T {
std::mem::replace(&mut *this, value)
}
}
impl<T: ?Sized> Watched<T> {
pub fn get_unwatched(this: &Self) -> &T {
this.inner.get_unwatched()
}
}
impl<T: Default> Watched<T> {
pub fn take(this: &mut Self) -> T {
std::mem::take(&mut *this)
}
}
impl<T: PartialEq> Watched<T> {
pub fn set_if_neq(wrapper: &mut Watched<T>, value: T) {
wrapper.inner.set_if_neq_auto(value);
}
}
impl<T: ?Sized> Deref for Watched<T> {
type Target = T;
fn deref(&self) -> &T {
self.inner.get_auto()
}
}
impl<T: ?Sized> DerefMut for Watched<T> {
fn deref_mut(&mut self) -> &mut T {
self.inner.get_mut_auto()
}
}
impl<T: fmt::Debug + ?Sized> fmt::Debug for Watched<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(self.inner.get_auto(), f)
}
}
mod watched_ops {
use std::cmp::Ordering;
use std::ops::*;
use super::Watched;
macro_rules! watched_unop {
(impl $imp:ident, $method:ident) => {
impl<'a, T: ?Sized> $imp for &'a Watched<T>
where
&'a T: $imp,
{
type Output = <&'a T as $imp>::Output;
fn $method(self) -> <&'a T as $imp>::Output {
$imp::$method(self.inner.get_auto())
}
}
};
}
macro_rules! watched_binop {
(impl $imp:ident, $method:ident) => {
impl<'a, T, U> $imp<U> for &'a Watched<T>
where
T: ?Sized,
&'a T: $imp<U>,
{
type Output = <&'a T as $imp<U>>::Output;
fn $method(self, other: U) -> <&'a T as $imp<U>>::Output {
$imp::$method(self.inner.get_auto(), other)
}
}
};
}
macro_rules! watched_binop_assign {
(impl $imp:ident, $method:ident) => {
impl<T, U> $imp<U> for Watched<T>
where
T: $imp<U> + ?Sized,
{
fn $method(&mut self, rhs: U) {
$imp::$method(self.inner.get_mut_auto(), rhs);
}
}
};
}
watched_unop!(impl Neg, neg);
watched_unop!(impl Not, not);
watched_binop!(impl Add, add);
watched_binop!(impl BitAnd, bitand);
watched_binop!(impl BitOr, bitor);
watched_binop!(impl BitXor, bitxor);
watched_binop!(impl Div, div);
watched_binop!(impl Mul, mul);
watched_binop!(impl Rem, rem);
watched_binop!(impl Shl, shl);
watched_binop!(impl Shr, shr);
watched_binop!(impl Sub, sub);
watched_binop_assign!(impl AddAssign, add_assign);
watched_binop_assign!(impl BitAndAssign, bitand_assign);
watched_binop_assign!(impl BitOrAssign, bitor_assign);
watched_binop_assign!(impl BitXorAssign, bitxor_assign);
watched_binop_assign!(impl DivAssign, div_assign);
watched_binop_assign!(impl MulAssign, mul_assign);
watched_binop_assign!(impl RemAssign, rem_assign);
watched_binop_assign!(impl ShlAssign, shl_assign);
watched_binop_assign!(impl ShrAssign, shr_assign);
watched_binop_assign!(impl SubAssign, sub_assign);
impl<T, U> PartialEq<U> for Watched<T>
where
T: PartialEq<U> + ?Sized,
U: ?Sized,
{
fn eq(&self, other: &U) -> bool {
PartialEq::eq(self.inner.get_auto(), other)
}
#[allow(clippy::partialeq_ne_impl)]
fn ne(&self, other: &U) -> bool {
PartialEq::ne(self.inner.get_auto(), other)
}
}
impl<T, U> PartialOrd<U> for Watched<T>
where
T: PartialOrd<U> + ?Sized,
U: ?Sized,
{
fn partial_cmp(&self, other: &U) -> Option<Ordering> {
PartialOrd::partial_cmp(self.inner.get_auto(), other)
}
fn lt(&self, other: &U) -> bool {
PartialOrd::lt(self.inner.get_auto(), other)
}
fn le(&self, other: &U) -> bool {
PartialOrd::le(self.inner.get_auto(), other)
}
fn ge(&self, other: &U) -> bool {
PartialOrd::ge(self.inner.get_auto(), other)
}
fn gt(&self, other: &U) -> bool {
PartialOrd::gt(self.inner.get_auto(), other)
}
}
}
#[derive(Default)]
pub struct WatchedCell<T: ?Sized> {
inner: WatchedCellCore<'static, T, DefaultOwner>,
}
impl<T: ?Sized> WatchedCell<T> {
pub fn get_mut(&mut self) -> &mut T {
self.inner.get_mut_auto()
}
pub fn watched(&self) {
self.inner.watched_auto();
}
}
impl<T> WatchedCell<T> {
pub fn new(value: T) -> Self {
Self {
inner: WatchedCellCore::new(value),
}
}
pub fn set(&self, value: T) {
self.inner.set_auto(value);
}
pub fn into_inner(self) -> T {
self.inner.into_inner()
}
pub fn replace(&self, value: T) -> T {
self.inner.replace_auto(value)
}
}
impl<T: Copy> WatchedCell<T> {
pub fn get(&self) -> T {
self.inner.get_auto()
}
}
impl<T: Default> WatchedCell<T> {
pub fn take(&self) -> T {
self.inner.take_auto()
}
}
impl<T: Copy> Clone for WatchedCell<T> {
fn clone(&self) -> Self {
Self::new(self.get())
}
}
impl<T> From<T> for WatchedCell<T> {
fn from(value: T) -> Self {
Self::new(value)
}
}
#[cfg(test)]
mod tests {
use std::{cell::RefCell, rc::Rc};
use crate::*;
#[test]
fn watched_add() {
let left = Watched::new(587);
assert_eq!(&left + 13, 600);
}
#[test]
fn add_to_watched() {
struct Content {
dest: u32,
source: Watched<u32>,
}
impl Watcher<'static> for Content {
fn init(mut init: impl WatcherInit<'static, Self>) {
init.watch(|root| {
root.dest = *root.source;
});
}
}
let content = Rc::new(RefCell::new(Content {
dest: 0,
source: Watched::new(587),
}));
let weak = Rc::downgrade(&content);
let mut ctx = WatchContext::new();
ctx.add_watcher(&weak);
assert_eq!(content.borrow().dest, 587);
content.borrow_mut().source += 13;
ctx.update();
assert_eq!(content.borrow().dest, 600);
}
#[test]
fn watched_xor() {
#[derive(Default)]
struct Content {
dest: u32,
source: Watched<u32>,
}
impl Watcher<'static> for Content {
fn init(mut init: impl WatcherInit<'static, Self>) {
init.watch(|root| {
root.dest = &root.source ^ 0xffffffff;
});
}
}
let content = Rc::new(RefCell::new(Content::default()));
let weak = Rc::downgrade(&content);
let mut ctx = WatchContext::new();
ctx.add_watcher(&weak);
*content.borrow_mut().source = 960294194;
ctx.update();
assert_eq!(content.borrow().dest, 3334673101);
}
#[test]
fn watched_reasonably_sized() {
assert_eq!(
std::mem::size_of::<Watched<usize>>(),
2 * std::mem::size_of::<usize>(),
);
}
}