use std::fmt::Debug;
use std::marker::PhantomData;
use std::ops::{Deref, DerefMut};
use serde::Serialize;
use crate::Mutations;
use crate::general::Snapshot;
use crate::helper::macros::{spec_impl_observe, spec_impl_ref_observe};
use crate::helper::{AsDeref, AsDerefMut, ObserverState, Pointer, QuasiObserver, Succ, Unsigned, Zero};
use crate::observe::{Observer, RefObserver, SerializeObserver};
struct OptionObserverState<O> {
initial: bool,
mutated: bool,
inner: Option<O>,
}
impl<O> ObserverState for OptionObserverState<O>
where
O: QuasiObserver<Head: Sized>,
{
type Target = Option<O::Head>;
fn invalidate(this: &mut Self, _value: &Self::Target) {
this.mutated = true;
this.inner = None;
}
}
pub struct OptionObserver<O, S: ?Sized, D = Zero> {
ptr: Pointer<S>,
state: OptionObserverState<O>,
phantom: PhantomData<D>,
}
impl<O, S: ?Sized, D> Deref for OptionObserver<O, S, D> {
type Target = Pointer<S>;
fn deref(&self) -> &Self::Target {
&self.ptr
}
}
impl<O, S: ?Sized, D> DerefMut for OptionObserver<O, S, D> {
fn deref_mut(&mut self) -> &mut Self::Target {
std::ptr::from_mut(self).expose_provenance();
Pointer::invalidate(&mut self.ptr);
&mut self.ptr
}
}
impl<O, S: ?Sized, D> QuasiObserver for OptionObserver<O, S, D>
where
O: QuasiObserver<InnerDepth = Zero, Head: Sized>,
D: Unsigned,
S: AsDeref<D, Target = Option<O::Head>>,
{
type Head = S;
type OuterDepth = Succ<Zero>;
type InnerDepth = D;
fn invalidate(this: &mut Self) {
ObserverState::invalidate(&mut this.state, (*this.ptr).as_deref());
}
}
impl<O, S: ?Sized, D> Observer for OptionObserver<O, S, D>
where
D: Unsigned,
S: AsDerefMut<D, Target = Option<O::Head>>,
O: Observer<InnerDepth = Zero>,
O::Head: Sized,
{
fn observe(head: &mut Self::Head) -> Self {
let this = Self {
state: OptionObserverState {
initial: head.as_deref_mut().is_some(),
mutated: false,
inner: head.as_deref_mut().as_mut().map(O::observe),
},
ptr: Pointer::new(head),
phantom: PhantomData,
};
Pointer::register_state::<_, D>(&this.ptr, &this.state);
this
}
unsafe fn relocate(this: &mut Self, head: &mut Self::Head) {
if let (Some(inner), Some(value)) = (&mut this.state.inner, head.as_deref_mut().as_mut()) {
unsafe { O::relocate(inner, value) }
}
Pointer::set(this, head);
}
}
impl<O, S: ?Sized, D> RefObserver for OptionObserver<O, S, D>
where
D: Unsigned,
S: AsDeref<D, Target = Option<O::Head>>,
O: RefObserver<InnerDepth = Zero>,
O::Head: Sized,
{
fn observe(head: &Self::Head) -> Self {
let this = Self {
ptr: Pointer::new(head),
state: OptionObserverState {
initial: head.as_deref().is_some(),
mutated: false,
inner: head.as_deref().as_ref().map(O::observe),
},
phantom: PhantomData,
};
Pointer::register_state::<_, D>(&this.ptr, &this.state);
this
}
unsafe fn relocate(this: &mut Self, head: &Self::Head) {
Pointer::set(this, head);
if let (Some(inner), Some(value)) = (&mut this.state.inner, head.as_deref().as_ref()) {
unsafe { O::relocate(inner, value) }
}
}
}
impl<O, S: ?Sized, D> SerializeObserver for OptionObserver<O, S, D>
where
D: Unsigned,
S: AsDeref<D, Target = Option<O::Head>>,
O: SerializeObserver<InnerDepth = Zero>,
O::Head: Serialize + Sized + 'static,
{
unsafe fn flush(this: &mut Self) -> Mutations {
let option = (*this.ptr).as_deref();
let initial = std::mem::replace(&mut this.state.initial, option.is_some());
let mutated = std::mem::take(&mut this.state.mutated);
if !mutated && initial {
return unsafe { O::flush(this.state.inner.as_mut().unwrap()) };
}
this.state.inner = None;
if initial || option.is_some() {
Mutations::replace(option)
} else {
Mutations::new()
}
}
}
impl<O, S: ?Sized, D> OptionObserver<O, S, D>
where
D: Unsigned,
S: AsDerefMut<D, Target = Option<O::Head>>,
O: Observer<InnerDepth = Zero>,
O::Head: Sized,
{
pub fn as_mut(&mut self) -> Option<&mut O> {
let value = (*self.ptr).as_deref_mut().as_mut()?;
let inner = match &mut self.state.inner {
Some(inner) => inner,
slot @ None => slot.insert(O::observe(value)),
};
unsafe { O::relocate(inner, value) }
Some(inner)
}
pub fn insert(&mut self, value: O::Head) -> &mut O {
*self.tracked_mut() = Some(value);
self.as_mut().unwrap()
}
pub fn get_or_insert(&mut self, value: O::Head) -> &mut O {
self.get_or_insert_with(|| value)
}
pub fn get_or_insert_default(&mut self) -> &mut O
where
O::Head: Default,
{
self.get_or_insert_with(Default::default)
}
pub fn get_or_insert_with<F>(&mut self, f: F) -> &mut O
where
F: FnOnce() -> O::Head,
{
if (*self).untracked_ref().is_none() {
*self.tracked_mut() = Some(f());
}
self.as_mut().unwrap()
}
}
impl<O, S: ?Sized, D> Debug for OptionObserver<O, S, D>
where
O: QuasiObserver<InnerDepth = Zero, Head: Sized + Debug>,
D: Unsigned,
S: AsDeref<D, Target = Option<O::Head>>,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("OptionObserver").field(&self.untracked_ref()).finish()
}
}
impl<O, S: ?Sized, D, U> PartialEq<Option<U>> for OptionObserver<O, S, D>
where
O: QuasiObserver<InnerDepth = Zero, Head: Sized>,
D: Unsigned,
S: AsDeref<D, Target = Option<O::Head>>,
Option<O::Head>: PartialEq<Option<U>>,
{
fn eq(&self, other: &Option<U>) -> bool {
self.untracked_ref().eq(other)
}
}
impl<O1, O2, S1: ?Sized, S2: ?Sized, D1, D2> PartialEq<OptionObserver<O2, S2, D2>> for OptionObserver<O1, S1, D1>
where
O1: QuasiObserver<InnerDepth = Zero, Head: Sized>,
O2: QuasiObserver<InnerDepth = Zero, Head: Sized>,
D1: Unsigned,
D2: Unsigned,
S1: AsDeref<D1, Target = Option<O1::Head>>,
S2: AsDeref<D2, Target = Option<O2::Head>>,
Option<O1::Head>: PartialEq<Option<O2::Head>>,
{
fn eq(&self, other: &OptionObserver<O2, S2, D2>) -> bool {
self.untracked_ref().eq(other.untracked_ref())
}
}
impl<O, S: ?Sized, D> Eq for OptionObserver<O, S, D>
where
O: QuasiObserver<InnerDepth = Zero, Head: Sized + Eq>,
D: Unsigned,
S: AsDeref<D, Target = Option<O::Head>>,
{
}
impl<O, S: ?Sized, D, U> PartialOrd<Option<U>> for OptionObserver<O, S, D>
where
O: QuasiObserver<InnerDepth = Zero, Head: Sized>,
D: Unsigned,
S: AsDeref<D, Target = Option<O::Head>>,
Option<O::Head>: PartialOrd<Option<U>>,
{
fn partial_cmp(&self, other: &Option<U>) -> Option<std::cmp::Ordering> {
self.untracked_ref().partial_cmp(other)
}
}
impl<O1, O2, S1: ?Sized, S2: ?Sized, D1, D2> PartialOrd<OptionObserver<O2, S2, D2>> for OptionObserver<O1, S1, D1>
where
O1: QuasiObserver<InnerDepth = Zero, Head: Sized>,
O2: QuasiObserver<InnerDepth = Zero, Head: Sized>,
D1: Unsigned,
D2: Unsigned,
S1: AsDeref<D1, Target = Option<O1::Head>>,
S2: AsDeref<D2, Target = Option<O2::Head>>,
Option<O1::Head>: PartialOrd<Option<O2::Head>>,
{
fn partial_cmp(&self, other: &OptionObserver<O2, S2, D2>) -> Option<std::cmp::Ordering> {
self.untracked_ref().partial_cmp(other.untracked_ref())
}
}
impl<O, S: ?Sized, D> Ord for OptionObserver<O, S, D>
where
O: QuasiObserver<InnerDepth = Zero, Head: Sized + Ord>,
D: Unsigned,
S: AsDeref<D, Target = Option<O::Head>>,
{
fn cmp(&self, other: &OptionObserver<O, S, D>) -> std::cmp::Ordering {
self.untracked_ref().cmp(other.untracked_ref())
}
}
spec_impl_observe!(OptionObserveImpl, Option<Self>, Option<T>, OptionObserver);
spec_impl_ref_observe!(OptionRefObserveImpl, Option<Self>, Option<T>, OptionObserver);
impl<T: Snapshot> Snapshot for Option<T> {
type Snapshot = Option<T::Snapshot>;
fn to_snapshot(&self) -> Self::Snapshot {
self.as_ref().map(|v| v.to_snapshot())
}
fn eq_snapshot(&self, snapshot: &Self::Snapshot) -> bool {
match (self, snapshot) {
(Some(v), Some(snapshot)) => v.eq_snapshot(snapshot),
(None, None) => true,
_ => false,
}
}
}
#[cfg(test)]
mod tests {
use morphix_test_utils::*;
use serde_json::json;
use super::*;
use crate::adapter::Json;
use crate::general::GeneralObserver;
use crate::observe::{ObserveExt, SerializeObserverExt};
#[test]
fn no_change_returns_none() {
let mut opt: Option<i32> = None;
let mut ob = opt.__observe();
let Json(mutation) = ob.flush().unwrap();
assert_eq!(mutation, None);
let mut opt: Option<i32> = Some(1);
let mut ob = opt.__observe();
let Json(mutation) = ob.flush().unwrap();
assert_eq!(mutation, None);
}
#[test]
fn deref_triggers_replace() {
let mut opt: Option<i32> = Some(42);
let mut ob = opt.__observe();
**ob = None;
let Json(mutation) = ob.flush().unwrap();
assert_eq!(mutation, Some(replace!(_, json!(null))));
let mut opt: Option<i32> = None;
let mut ob = opt.__observe();
**ob = Some(42);
let Json(mutation) = ob.flush().unwrap();
assert_eq!(mutation, Some(replace!(_, json!(42))));
let mut opt: Option<i32> = None;
let mut ob = opt.__observe();
**ob = Some(42);
**ob = None;
let Json(mutation) = ob.flush().unwrap();
assert_eq!(mutation, None);
let mut opt: Option<&str> = Some("42");
let mut ob = opt.__observe();
**ob = None;
**ob = Some("42");
let Json(mutation) = ob.flush().unwrap();
assert_eq!(mutation, Some(replace!(_, json!("42"))));
}
#[test]
fn insert_returns_observer() {
let mut opt: Option<String> = None;
let mut ob = opt.__observe();
let s = ob.insert(String::from("99"));
assert_eq!(format!("{s:?}"), r#"StringObserver("99")"#);
*s += "9";
let Json(mutation) = ob.flush().unwrap();
assert_eq!(mutation, Some(replace!(_, json!("999"))));
}
#[test]
fn as_mut_tracks_inner() {
let mut opt = Some(String::from("foo"));
let mut ob = opt.__observe();
*ob.as_mut().unwrap() += "bar";
let Json(mutation) = ob.flush().unwrap();
assert_eq!(mutation, Some(append!(_, json!("bar"))));
}
#[test]
fn get_or_insert() {
let mut opt: Option<i32> = None;
let mut ob = opt.__observe();
*ob.get_or_insert(5) = 6;
let Json(mutation) = ob.flush().unwrap();
assert_eq!(mutation, Some(replace!(_, json!(6))));
let mut opt: Option<i32> = None;
let mut ob = opt.__observe();
*ob.get_or_insert_default() = 77;
let Json(mutation) = ob.flush().unwrap();
assert_eq!(mutation, Some(replace!(_, json!(77))));
let mut opt: Option<i32> = None;
let mut ob = opt.__observe();
*ob.get_or_insert_with(|| 88) = 99;
let Json(mutation) = ob.flush().unwrap();
assert_eq!(mutation, Some(replace!(_, json!(99))));
}
#[test]
fn specialization() {
let mut opt: Option<i32> = Some(0i32);
let ob: GeneralObserver<_, _, _> = opt.__observe();
assert_eq!(format!("{ob:?}"), r#"SnapshotObserver(Some(0))"#);
let mut opt: Option<&str> = Some("");
let ob: OptionObserver<_, _, _> = opt.__observe();
assert_eq!(format!("{ob:?}"), r#"OptionObserver(Some(""))"#);
}
#[test]
fn ref_specialization() {
let mut opt = &Some(0i32);
let ob = opt.__observe();
assert_eq!(format!("{ob:?}"), r#"DerefObserver(SnapshotObserver(Some(0)))"#);
let mut opt = &Some("");
let ob = opt.__observe();
assert_eq!(format!("{ob:?}"), r#"DerefObserver(OptionObserver(Some("")))"#);
}
#[test]
fn relocate() {
let mut vec = vec![None::<i32>];
let mut ob = vec.__observe();
**ob[0] = Some(1);
ob.reserve(10); assert_eq!(**ob[0], Some(1));
let Json(mutation) = ob.flush().unwrap();
assert_eq!(mutation, Some(replace!(_, json!([1]))));
}
}