morphix/observe/
shallow.rs

1use std::marker::PhantomData;
2use std::ops::{Deref, DerefMut, Index, IndexMut};
3
4use serde::Serialize;
5
6use crate::{Adapter, Mutation, MutationKind, Observe, Observer};
7
8/// A generic observer that only tracks complete replacements.
9///
10/// `ShallowObserver` provides a basic observer implementation that treats any mutation through
11/// `DerefMut` as a complete replacement of the value. It does not track internal mutations, making
12/// it suitable for:
13///
14/// 1. Primitive types (numbers, booleans, etc.) that cannot be partially modified
15/// 2. Types where internal mutation tracking is not needed
16/// 3. External types that do not implement `Observe`
17///
18/// ## Examples
19///
20/// Built-in implementation for primitive types:
21///
22/// ```
23/// use morphix::{Observe, Observer, JsonAdapter};
24///
25/// let mut value = 42i32;
26/// let mut observer = value.observe();  // ShallowObserver<i32>
27/// *observer = 43;  // Recorded as a complete replacement
28/// ```
29///
30/// Explicit usage via `#[observe(shallow)]` attribute:
31///
32/// ```
33/// use morphix::Observe;
34/// use serde::Serialize;
35///
36/// // External type that doesn't implement Observe
37/// #[derive(Serialize)]
38/// struct External;
39///
40/// #[derive(Serialize, Observe)]
41/// struct MyStruct {
42///     #[observe(shallow)]
43///     external: External,  // use ShallowObserver<External>
44///     normal: String,      // use StringObserver
45/// }
46/// ```
47///
48/// ## Type Parameters
49///
50/// - `'i` - lifetime of the observed value
51/// - `T` - type being observed
52pub struct ShallowObserver<'i, T> {
53    ptr: *mut T,
54    replaced: bool,
55    phantom: PhantomData<&'i mut T>,
56}
57
58impl<'i, T> Observer<'i, T> for ShallowObserver<'i, T> {
59    #[inline]
60    fn observe(value: &'i mut T) -> Self {
61        ShallowObserver::new(value)
62    }
63
64    fn collect<A: Adapter>(this: Self) -> Result<Option<Mutation<A>>, A::Error>
65    where
66        T: Serialize,
67    {
68        Ok(if this.replaced {
69            Some(Mutation {
70                path_rev: vec![],
71                operation: MutationKind::Replace(A::serialize_value(&*this)?),
72            })
73        } else {
74            None
75        })
76    }
77}
78
79impl<'i, T> ShallowObserver<'i, T> {
80    /// Creates a new shallow observer for the given value.
81    ///
82    /// ## Arguments
83    ///
84    /// - `value` - value to observe
85    pub fn new(value: &'i mut T) -> Self {
86        Self {
87            ptr: value as *mut T,
88            replaced: false,
89            phantom: PhantomData,
90        }
91    }
92}
93
94macro_rules! impl_observe {
95    ($($ty:ty $(=> $target:ty)?),* $(,)?) => {
96        $(
97            impl Observe for $ty {
98                type Observer<'i> = ShallowObserver<'i, $ty>
99                where
100                    Self: 'i;
101            }
102        )*
103    };
104}
105
106impl_observe! {
107    usize, u8, u16, u32, u64, u128, isize, i8, i16, i32, i64, i128, f32, f64, bool,
108}
109
110impl<'i, T> Deref for ShallowObserver<'i, T> {
111    type Target = T;
112    fn deref(&self) -> &Self::Target {
113        unsafe { &*self.ptr }
114    }
115}
116
117impl<'i, T> DerefMut for ShallowObserver<'i, T> {
118    fn deref_mut(&mut self) -> &mut Self::Target {
119        self.replaced = true;
120        unsafe { &mut *self.ptr }
121    }
122}
123
124impl<'i, T: Index<U>, U> Index<U> for ShallowObserver<'i, T> {
125    type Output = T::Output;
126    fn index(&self, index: U) -> &Self::Output {
127        (**self).index(index)
128    }
129}
130
131impl<'i, T: IndexMut<U>, U> IndexMut<U> for ShallowObserver<'i, T> {
132    fn index_mut(&mut self, index: U) -> &mut Self::Output {
133        (**self).index_mut(index)
134    }
135}
136
137impl<'i, T: PartialEq<U>, U: ?Sized> PartialEq<U> for ShallowObserver<'i, T> {
138    fn eq(&self, other: &U) -> bool {
139        (**self).eq(other)
140    }
141}
142
143impl<'i, T: PartialOrd<U>, U: ?Sized> PartialOrd<U> for ShallowObserver<'i, T> {
144    fn partial_cmp(&self, other: &U) -> Option<std::cmp::Ordering> {
145        (**self).partial_cmp(other)
146    }
147}
148
149macro_rules! impl_assign_ops {
150    ($($trait:ident => $method:ident),* $(,)?) => {
151        $(
152            impl<'i, T: ::std::ops::$trait<U>, U> ::std::ops::$trait<U> for ShallowObserver<'i, T> {
153                fn $method(&mut self, rhs: U) {
154                    (**self).$method(rhs);
155                }
156            }
157        )*
158    };
159}
160
161impl_assign_ops! {
162    AddAssign => add_assign,
163    SubAssign => sub_assign,
164    MulAssign => mul_assign,
165    DivAssign => div_assign,
166    RemAssign => rem_assign,
167    BitAndAssign => bitand_assign,
168    BitOrAssign => bitor_assign,
169    BitXorAssign => bitxor_assign,
170    ShlAssign => shl_assign,
171    ShrAssign => shr_assign,
172}
173
174macro_rules! impl_ops_copy {
175    ($($trait:ident => $method:ident),* $(,)?) => {
176        $(
177            impl<'i, T: Copy + ::std::ops::$trait<U>, U> ::std::ops::$trait<U> for ShallowObserver<'i, T> {
178                type Output = T::Output;
179                fn $method(self, rhs: U) -> Self::Output {
180                    (*self).$method(rhs)
181                }
182            }
183        )*
184    };
185}
186
187impl_ops_copy! {
188    Add => add,
189    Sub => sub,
190    Mul => mul,
191    Div => div,
192    Rem => rem,
193    BitAnd => bitand,
194    BitOr => bitor,
195    BitXor => bitxor,
196    Shl => shl,
197    Shr => shr,
198}