use crate::{change_detection::MaybeLocation, change_detection::Tick};
use alloc::borrow::ToOwned;
use core::mem;
pub trait DetectChanges {
fn is_added(&self) -> bool;
fn is_changed(&self) -> bool;
fn last_changed(&self) -> Tick;
fn added(&self) -> Tick;
fn changed_by(&self) -> MaybeLocation;
}
pub trait DetectChangesMut: DetectChanges {
type Inner: ?Sized;
fn set_changed(&mut self);
fn set_added(&mut self);
fn set_last_changed(&mut self, last_changed: Tick);
fn set_last_added(&mut self, last_added: Tick);
fn bypass_change_detection(&mut self) -> &mut Self::Inner;
#[inline]
#[track_caller]
fn set_if_neq(&mut self, value: Self::Inner) -> bool
where
Self::Inner: Sized + PartialEq,
{
let old = self.bypass_change_detection();
if *old != value {
*old = value;
self.set_changed();
true
} else {
false
}
}
#[inline]
#[must_use = "If you don't need to handle the previous value, use `set_if_neq` instead."]
fn replace_if_neq(&mut self, value: Self::Inner) -> Option<Self::Inner>
where
Self::Inner: Sized + PartialEq,
{
let old = self.bypass_change_detection();
if *old != value {
let previous = mem::replace(old, value);
self.set_changed();
Some(previous)
} else {
None
}
}
fn clone_from_if_neq<T>(&mut self, value: &T) -> bool
where
T: ToOwned<Owned = Self::Inner> + ?Sized,
Self::Inner: PartialEq<T>,
{
let old = self.bypass_change_detection();
if old != value {
value.clone_into(old);
self.set_changed();
true
} else {
false
}
}
}
macro_rules! change_detection_impl {
($name:ident < $( $generics:tt ),+ >, $target:ty, $($traits:ident)?) => {
impl<$($generics),* : ?Sized $(+ $traits)?> DetectChanges for $name<$($generics),*> {
#[inline]
fn is_added(&self) -> bool {
self.ticks
.added
.is_newer_than(self.ticks.last_run, self.ticks.this_run)
}
#[inline]
fn is_changed(&self) -> bool {
self.ticks
.changed
.is_newer_than(self.ticks.last_run, self.ticks.this_run)
}
#[inline]
fn last_changed(&self) -> Tick {
*self.ticks.changed
}
#[inline]
fn added(&self) -> Tick {
*self.ticks.added
}
#[inline]
fn changed_by(&self) -> MaybeLocation {
self.ticks.changed_by.copied()
}
}
impl<$($generics),*: ?Sized $(+ $traits)?> Deref for $name<$($generics),*> {
type Target = $target;
#[inline]
fn deref(&self) -> &Self::Target {
self.value
}
}
impl<$($generics),* $(: $traits)?> AsRef<$target> for $name<$($generics),*> {
#[inline]
fn as_ref(&self) -> &$target {
self.deref()
}
}
}
}
pub(crate) use change_detection_impl;
macro_rules! change_detection_mut_impl {
($name:ident < $( $generics:tt ),+ >, $target:ty, $($traits:ident)?) => {
impl<$($generics),* : ?Sized $(+ $traits)?> DetectChangesMut for $name<$($generics),*> {
type Inner = $target;
#[inline]
#[track_caller]
fn set_changed(&mut self) {
*self.ticks.changed = self.ticks.this_run;
self.ticks.changed_by.assign(MaybeLocation::caller());
}
#[inline]
#[track_caller]
fn set_added(&mut self) {
*self.ticks.changed = self.ticks.this_run;
*self.ticks.added = self.ticks.this_run;
self.ticks.changed_by.assign(MaybeLocation::caller());
}
#[inline]
#[track_caller]
fn set_last_changed(&mut self, last_changed: Tick) {
*self.ticks.changed = last_changed;
self.ticks.changed_by.assign(MaybeLocation::caller());
}
#[inline]
#[track_caller]
fn set_last_added(&mut self, last_added: Tick) {
*self.ticks.added = last_added;
*self.ticks.changed = last_added;
self.ticks.changed_by.assign(MaybeLocation::caller());
}
#[inline]
fn bypass_change_detection(&mut self) -> &mut Self::Inner {
self.value
}
}
impl<$($generics),* : ?Sized $(+ $traits)?> DerefMut for $name<$($generics),*> {
#[inline]
#[track_caller]
fn deref_mut(&mut self) -> &mut Self::Target {
self.set_changed();
self.ticks.changed_by.assign(MaybeLocation::caller());
self.value
}
}
impl<$($generics),* $(: $traits)?> AsMut<$target> for $name<$($generics),*> {
#[inline]
fn as_mut(&mut self) -> &mut $target {
self.deref_mut()
}
}
};
}
pub(crate) use change_detection_mut_impl;
macro_rules! impl_methods {
($name:ident < $( $generics:tt ),+ >, $target:ty, $($traits:ident)?) => {
impl<$($generics),* : ?Sized $(+ $traits)?> $name<$($generics),*> {
#[inline]
pub fn into_inner(mut self) -> &'w mut $target {
self.set_changed();
self.value
}
#[doc = stringify!($name)]
pub fn reborrow(&mut self) -> Mut<'_, $target> {
Mut {
value: self.value,
ticks: ComponentTicksMut {
added: self.ticks.added,
changed: self.ticks.changed,
changed_by: self.ticks.changed_by.as_deref_mut(),
last_run: self.ticks.last_run,
this_run: self.ticks.this_run,
},
}
}
pub fn map_unchanged<U: ?Sized>(self, f: impl FnOnce(&mut $target) -> &mut U) -> Mut<'w, U> {
Mut {
value: f(self.value),
ticks: self.ticks,
}
}
pub fn filter_map_unchanged<U: ?Sized>(self, f: impl FnOnce(&mut $target) -> Option<&mut U>) -> Option<Mut<'w, U>> {
let value = f(self.value);
value.map(|value| Mut {
value,
ticks: self.ticks,
})
}
pub fn try_map_unchanged<U: ?Sized, E>(self, f: impl FnOnce(&mut $target) -> Result<&mut U, E>) -> Result<Mut<'w, U>, E> {
let value = f(self.value);
value.map(|value| Mut {
value,
ticks: self.ticks,
})
}
pub fn as_deref_mut(&mut self) -> Mut<'_, <$target as Deref>::Target>
where $target: DerefMut
{
self.reborrow().map_unchanged(|v| v.deref_mut())
}
}
};
}
pub(crate) use impl_methods;
macro_rules! impl_debug {
($name:ident < $( $generics:tt ),+ >, $($traits:ident)?) => {
impl<$($generics),* : ?Sized $(+ $traits)?> core::fmt::Debug for $name<$($generics),*>
where T: core::fmt::Debug
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_tuple(stringify!($name))
.field(&self.value)
.finish()
}
}
};
}
pub(crate) use impl_debug;