use std::marker::PhantomData;
use std::mem::MaybeUninit;
use std::num::NonZero;
use crate::Observe;
use crate::general::{DebugHandler, GeneralHandler, GeneralObserver, ReplaceHandler};
use crate::helper::{AsDeref, AsDerefMut, ObserverState, Unsigned, Zero};
use crate::observe::RefObserve;
pub type SnapshotObserver<'ob, S, D = Zero> = GeneralObserver<'ob, SnapshotHandler<<S as AsDeref<D>>::Target>, S, D>;
pub trait Snapshot {
type Snapshot;
fn to_snapshot(&self) -> Self::Snapshot;
fn eq_snapshot(&self, snapshot: &Self::Snapshot) -> bool;
}
pub struct SnapshotHandler<T: Snapshot + ?Sized> {
snapshot: MaybeUninit<T::Snapshot>,
phantom: PhantomData<T>,
}
impl<T: Snapshot + ?Sized> ObserverState for SnapshotHandler<T> {
type Target = T;
fn invalidate(_: &mut Self, _: &T) {}
}
impl<T: Snapshot + ?Sized> GeneralHandler for SnapshotHandler<T> {
fn observe(value: &T) -> Self {
Self {
snapshot: MaybeUninit::new(value.to_snapshot()),
phantom: PhantomData,
}
}
}
impl<T: Snapshot + ?Sized> ReplaceHandler for SnapshotHandler<T> {
unsafe fn is_replace(&self, value: &T) -> bool {
!value.eq_snapshot(unsafe { self.snapshot.assume_init_ref() })
}
}
impl<T: Snapshot + ?Sized> DebugHandler for SnapshotHandler<T> {
const NAME: &'static str = "SnapshotObserver";
}
pub struct SnapshotSpec;
macro_rules! impl_snapshot_observe {
($($ty:ty),* $(,)?) => {
$(
impl Snapshot for $ty {
type Snapshot = Self;
fn to_snapshot(&self) -> Self {
*self
}
fn eq_snapshot(&self, snapshot: &Self) -> bool {
self == snapshot
}
}
impl Observe for $ty {
type Observer<'ob, S, D>
= SnapshotObserver<'ob, S, D>
where
Self: 'ob,
D: Unsigned,
S: AsDerefMut<D, Target = Self> + ?Sized + 'ob;
type Spec = SnapshotSpec;
}
impl RefObserve for $ty {
type Observer<'ob, S, D>
= SnapshotObserver<'ob, S, D>
where
Self: 'ob,
D: Unsigned,
S: AsDeref<D, Target = Self> + ?Sized + 'ob;
type Spec = SnapshotSpec;
}
)*
};
}
impl_snapshot_observe! {
(), usize, u8, u16, u32, u64, u128, isize, i8, i16, i32, i64, i128, f32, f64, bool, char,
NonZero<usize>, NonZero<u8>, NonZero<u16>, NonZero<u32>, NonZero<u64>, NonZero<u128>,
NonZero<isize>, NonZero<i8>, NonZero<i16>, NonZero<i32>, NonZero<i64>, NonZero<i128>,
core::net::IpAddr, core::net::Ipv4Addr, core::net::Ipv6Addr,
core::net::SocketAddr, core::net::SocketAddrV4, core::net::SocketAddrV6,
core::time::Duration, std::time::SystemTime,
}
#[cfg(feature = "chrono")]
impl_snapshot_observe! {
chrono::Days, chrono::FixedOffset, chrono::Month, chrono::Months, chrono::IsoWeek,
chrono::NaiveDate, chrono::NaiveDateTime, chrono::NaiveTime, chrono::NaiveWeek,
chrono::TimeDelta, chrono::Utc, chrono::Weekday, chrono::WeekdaySet,
}
#[cfg(feature = "uuid")]
impl_snapshot_observe! {
uuid::Uuid, uuid::NonNilUuid,
}
macro_rules! generic_impl_snapshot_observe {
($(impl $([$($gen:tt)*])? _ for $ty:ty);* $(;)?) => {
$(
impl<$($($gen)*)?> Snapshot for $ty {
type Snapshot = Self;
fn to_snapshot(&self) -> Self {
self.clone()
}
fn eq_snapshot(&self, snapshot: &Self) -> bool {
self == snapshot
}
}
impl<$($($gen)*)?> Observe for $ty {
type Observer<'ob, S, D>
= SnapshotObserver<'ob, S, D>
where
Self: 'ob,
D: Unsigned,
S: AsDerefMut<D, Target = Self> + ?Sized + 'ob;
type Spec = SnapshotSpec;
}
impl<$($($gen)*)?> RefObserve for $ty {
type Observer<'ob, S, D>
= SnapshotObserver<'ob, S, D>
where
Self: 'ob,
D: Unsigned,
S: AsDeref<D, Target = Self> + ?Sized + 'ob;
type Spec = SnapshotSpec;
}
)*
};
}
generic_impl_snapshot_observe! {
impl [T] _ for std::marker::PhantomData<T>;
}
#[cfg(feature = "chrono")]
generic_impl_snapshot_observe! {
impl [Tz: chrono::TimeZone] _ for chrono::DateTime<Tz>;
}