firewheel_core/diff/
notify.rs

1use crate::{
2    diff::{Diff, Patch, RealtimeClone},
3    event::ParamData,
4};
5use bevy_platform::sync::atomic::{AtomicU64, Ordering};
6
7// Increment an atomic counter.
8//
9// This is guaranteed to never return zero.
10#[inline(always)]
11fn increment_counter() -> u64 {
12    static NOTIFY_COUNTER: AtomicU64 = AtomicU64::new(1);
13
14    NOTIFY_COUNTER.fetch_add(1, Ordering::Relaxed)
15}
16
17/// A lightweight wrapper that guarantees an event
18/// will be generated every time the inner value is accessed mutably,
19/// even if the value doesn't change.
20///
21/// This is useful for types like a play head
22/// where periodically writing the same value
23/// carries useful information.
24///
25/// [`Notify`] implements [`core::ops::Deref`] and [`core::ops::DerefMut`]
26/// for the inner `T`.
27#[derive(Debug, Clone)]
28pub struct Notify<T> {
29    value: T,
30    counter: u64,
31}
32
33impl<T> Notify<T> {
34    /// Construct a new [`Notify`].
35    ///
36    /// If two instances of [`Notify`] are constructed separately,
37    /// a call to [`Diff::diff`] will produce an event, even if the
38    /// value is the same.
39    ///
40    /// ```
41    /// # use firewheel_core::diff::Notify;
42    /// // Diffing `a` and `b` will produce an event
43    /// let a = Notify::new(1);
44    /// let b = Notify::new(1);
45    ///
46    /// // whereas `b` and `c` will not.
47    /// let c = b.clone();
48    /// ```
49    pub fn new(value: T) -> Self {
50        Self {
51            value,
52            counter: increment_counter(),
53        }
54    }
55
56    /// Get this instance's unique ID.
57    ///
58    /// After each mutable dereference, this ID will be replaced
59    /// with a new, unique value. For all practical purposes,
60    /// the ID can be considered unique among all [`Notify`] instances.
61    ///
62    /// [`Notify`] IDs are guaranteed to never be 0, so it can be
63    /// used as a sentinel value.
64    #[inline(always)]
65    pub fn id(&self) -> u64 {
66        self.counter
67    }
68
69    /// Get mutable access to the inner value without updating the ID.
70    pub fn as_mut_unsync(&mut self) -> &mut T {
71        &mut self.value
72    }
73
74    /// Manually update the internal ID without modifying the internals.
75    pub fn notify(&mut self) {
76        self.counter = increment_counter();
77    }
78}
79
80impl<T> AsRef<T> for Notify<T> {
81    fn as_ref(&self) -> &T {
82        &self.value
83    }
84}
85
86impl<T> AsMut<T> for Notify<T> {
87    fn as_mut(&mut self) -> &mut T {
88        self.counter = increment_counter();
89
90        &mut self.value
91    }
92}
93
94impl<T: Default> Default for Notify<T> {
95    fn default() -> Self {
96        Self::new(T::default())
97    }
98}
99
100impl<T> core::ops::Deref for Notify<T> {
101    type Target = T;
102
103    fn deref(&self) -> &Self::Target {
104        &self.value
105    }
106}
107
108impl<T> core::ops::DerefMut for Notify<T> {
109    fn deref_mut(&mut self) -> &mut Self::Target {
110        self.counter = increment_counter();
111
112        &mut self.value
113    }
114}
115
116impl<T: Copy> Copy for Notify<T> {}
117
118impl<T: RealtimeClone + Send + Sync + 'static> Diff for Notify<T> {
119    fn diff<E: super::EventQueue>(
120        &self,
121        baseline: &Self,
122        path: super::PathBuilder,
123        event_queue: &mut E,
124    ) {
125        if self.counter != baseline.counter {
126            event_queue.push_param(ParamData::any(self.clone()), path);
127        }
128    }
129}
130
131impl<T: RealtimeClone + Send + Sync + 'static> Patch for Notify<T> {
132    type Patch = Self;
133
134    fn patch(data: &ParamData, _: &[u32]) -> Result<Self::Patch, super::PatchError> {
135        data.downcast_ref()
136            .ok_or(super::PatchError::InvalidData)
137            .cloned()
138    }
139
140    fn apply(&mut self, patch: Self::Patch) {
141        *self = patch;
142    }
143}
144
145impl<T: PartialEq> PartialEq for Notify<T> {
146    fn eq(&self, other: &Self) -> bool {
147        self.value == other.value && self.counter == other.counter
148    }
149}
150
151#[cfg(test)]
152mod test {
153    use crate::diff::PathBuilder;
154
155    use super::*;
156
157    #[test]
158    fn test_identical_write() {
159        #[cfg(not(feature = "std"))]
160        use bevy_platform::prelude::Vec;
161
162        let baseline = Notify::new(0.5f32);
163        let mut value = baseline;
164
165        let mut events = Vec::new();
166        value.diff(&baseline, PathBuilder::default(), &mut events);
167        assert_eq!(events.len(), 0);
168
169        *value = 0.5f32;
170
171        value.diff(&baseline, PathBuilder::default(), &mut events);
172        assert_eq!(events.len(), 1);
173    }
174}
175
176#[cfg(feature = "bevy_reflect")]
177mod reflect {
178    use super::Notify;
179
180    #[cfg(not(feature = "std"))]
181    use bevy_platform::prelude::{Box, ToString};
182
183    impl<T> bevy_reflect::GetTypeRegistration for Notify<T>
184    where
185        Notify<T>: ::core::any::Any + ::core::marker::Send + ::core::marker::Sync,
186        T: Clone
187            + Default
188            + bevy_reflect::FromReflect
189            + bevy_reflect::TypePath
190            + bevy_reflect::MaybeTyped
191            + bevy_reflect::__macro_exports::RegisterForReflection,
192    {
193        fn get_type_registration() -> bevy_reflect::TypeRegistration {
194            let mut registration = bevy_reflect::TypeRegistration::of::<Self>();
195            registration.insert:: <bevy_reflect::ReflectFromPtr>(bevy_reflect::FromType:: <Self> ::from_type());
196            registration.insert::<bevy_reflect::ReflectFromReflect>(
197                bevy_reflect::FromType::<Self>::from_type(),
198            );
199            registration
200                .insert::<bevy_reflect::prelude::ReflectDefault>(
201                    bevy_reflect::FromType::<Self>::from_type(),
202                );
203            registration
204        }
205
206        #[inline(never)]
207        fn register_type_dependencies(registry: &mut bevy_reflect::TypeRegistry) {
208            <T as bevy_reflect::__macro_exports::RegisterForReflection>::__register(registry);
209            <u64 as bevy_reflect::__macro_exports::RegisterForReflection>::__register(registry);
210        }
211    }
212
213    impl<T> bevy_reflect::Typed for Notify<T>
214    where
215        Notify<T>: ::core::any::Any + ::core::marker::Send + ::core::marker::Sync,
216        T: Clone
217            + bevy_reflect::FromReflect
218            + bevy_reflect::TypePath
219            + bevy_reflect::MaybeTyped
220            + bevy_reflect::__macro_exports::RegisterForReflection,
221    {
222        #[inline]
223        fn type_info() -> &'static bevy_reflect::TypeInfo {
224            static CELL: bevy_reflect::utility::GenericTypeInfoCell =
225                bevy_reflect::utility::GenericTypeInfoCell::new();
226            CELL.get_or_insert::<Self, _>(|| {
227                bevy_reflect::TypeInfo::Struct(
228                    bevy_reflect::StructInfo::new::<Self>(&[
229                        bevy_reflect::NamedField::new::<T>("value").with_custom_attributes(
230                            bevy_reflect::attributes::CustomAttributes::default(),
231                        ),
232                    ])
233                    .with_custom_attributes(bevy_reflect::attributes::CustomAttributes::default())
234                    .with_generics(bevy_reflect::Generics::from_iter([
235                        bevy_reflect::GenericInfo::Type(bevy_reflect::TypeParamInfo::new::<T>(
236                            // TODO: Use nicer path once bevy_reflect exposes it.
237                            bevy_reflect::__macro_exports::alloc_utils::Cow::Borrowed("T"),
238                        )),
239                    ])),
240                )
241            })
242        }
243    }
244
245    extern crate alloc;
246    impl<T> bevy_reflect::TypePath for Notify<T>
247    where
248        Notify<T>: ::core::any::Any + ::core::marker::Send + ::core::marker::Sync,
249        T: bevy_reflect::TypePath,
250    {
251        fn type_path() -> &'static str {
252            static CELL: bevy_reflect::utility::GenericTypePathCell =
253                bevy_reflect::utility::GenericTypePathCell::new();
254            CELL.get_or_insert::<Self, _>(|| {
255                ::core::ops::Add::<&str>::add(
256                    ::core::ops::Add::<&str>::add(
257                        ToString::to_string(::core::concat!(
258                            ::core::concat!(
259                                ::core::concat!(::core::module_path!(), "::"),
260                                "Notify"
261                            ),
262                            "<"
263                        )),
264                        <T as bevy_reflect::TypePath>::type_path(),
265                    ),
266                    ">",
267                )
268            })
269        }
270        fn short_type_path() -> &'static str {
271            static CELL: bevy_reflect::utility::GenericTypePathCell =
272                bevy_reflect::utility::GenericTypePathCell::new();
273            CELL.get_or_insert::<Self, _>(|| {
274                ::core::ops::Add::<&str>::add(
275                    ::core::ops::Add::<&str>::add(
276                        ToString::to_string(::core::concat!("Notify", "<")),
277                        <T as bevy_reflect::TypePath>::short_type_path(),
278                    ),
279                    ">",
280                )
281            })
282        }
283        fn type_ident() -> Option<&'static str> {
284            Some("Notify")
285        }
286        fn crate_name() -> Option<&'static str> {
287            Some(::core::module_path!().split(':').next().unwrap())
288        }
289        fn module_path() -> Option<&'static str> {
290            Some(::core::module_path!())
291        }
292    }
293
294    impl<T> bevy_reflect::Reflect for Notify<T>
295    where
296        Notify<T>: ::core::any::Any + ::core::marker::Send + ::core::marker::Sync,
297        T: Clone
298            + bevy_reflect::FromReflect
299            + bevy_reflect::TypePath
300            + bevy_reflect::MaybeTyped
301            + bevy_reflect::__macro_exports::RegisterForReflection,
302    {
303        #[inline]
304        fn into_any(self: Box<Self>) -> Box<dyn ::core::any::Any> {
305            self
306        }
307        #[inline]
308        fn as_any(&self) -> &dyn ::core::any::Any {
309            self
310        }
311        #[inline]
312        fn as_any_mut(&mut self) -> &mut dyn ::core::any::Any {
313            self
314        }
315        #[inline]
316        fn into_reflect(self: Box<Self>) -> Box<dyn bevy_reflect::Reflect> {
317            self
318        }
319        #[inline]
320        fn as_reflect(&self) -> &dyn bevy_reflect::Reflect {
321            self
322        }
323        #[inline]
324        fn as_reflect_mut(&mut self) -> &mut dyn bevy_reflect::Reflect {
325            self
326        }
327        #[inline]
328        fn set(
329            &mut self,
330            value: Box<dyn bevy_reflect::Reflect>,
331        ) -> Result<(), Box<dyn bevy_reflect::Reflect>> {
332            *self = <dyn bevy_reflect::Reflect>::take(value)?;
333            Ok(())
334        }
335    }
336
337    impl<T> bevy_reflect::Struct for Notify<T>
338    where
339        Notify<T>: ::core::any::Any + ::core::marker::Send + ::core::marker::Sync,
340        T: Clone
341            + bevy_reflect::FromReflect
342            + bevy_reflect::TypePath
343            + bevy_reflect::MaybeTyped
344            + bevy_reflect::__macro_exports::RegisterForReflection,
345    {
346        fn field(&self, name: &str) -> Option<&dyn bevy_reflect::PartialReflect> {
347            match name {
348                "value" => Some(&self.value),
349                _ => None,
350            }
351        }
352
353        fn field_mut(&mut self, name: &str) -> Option<&mut dyn bevy_reflect::PartialReflect> {
354            match name {
355                "value" => Some(self.as_mut()),
356                _ => None,
357            }
358        }
359
360        fn field_at(&self, index: usize) -> Option<&dyn bevy_reflect::PartialReflect> {
361            match index {
362                0usize => Some(&self.value),
363                _ => None,
364            }
365        }
366
367        fn field_at_mut(&mut self, index: usize) -> Option<&mut dyn bevy_reflect::PartialReflect> {
368            match index {
369                0usize => Some(self.as_mut()),
370                _ => None,
371            }
372        }
373
374        fn name_at(&self, index: usize) -> Option<&str> {
375            match index {
376                0usize => Some("value"),
377                _ => None,
378            }
379        }
380
381        fn field_len(&self) -> usize {
382            1usize
383        }
384
385        fn iter_fields<'a>(&'a self) -> bevy_reflect::FieldIter<'a> {
386            bevy_reflect::FieldIter::new(self)
387        }
388
389        fn to_dynamic_struct(&self) -> bevy_reflect::DynamicStruct {
390            let mut dynamic: bevy_reflect::DynamicStruct = Default::default();
391            dynamic.set_represented_type(bevy_reflect::PartialReflect::get_represented_type_info(
392                self,
393            ));
394            dynamic.insert_boxed(
395                "value",
396                bevy_reflect::PartialReflect::to_dynamic(&self.value),
397            );
398            dynamic
399        }
400    }
401
402    impl<T> bevy_reflect::PartialReflect for Notify<T>
403    where
404        Notify<T>: ::core::any::Any + ::core::marker::Send + ::core::marker::Sync,
405        T: Clone
406            + bevy_reflect::FromReflect
407            + bevy_reflect::TypePath
408            + bevy_reflect::MaybeTyped
409            + bevy_reflect::__macro_exports::RegisterForReflection,
410    {
411        #[inline]
412        fn get_represented_type_info(&self) -> Option<&'static bevy_reflect::TypeInfo> {
413            Some(<Self as bevy_reflect::Typed>::type_info())
414        }
415
416        #[inline]
417        fn try_apply(
418            &mut self,
419            value: &dyn bevy_reflect::PartialReflect,
420        ) -> Result<(), bevy_reflect::ApplyError> {
421            if let bevy_reflect::ReflectRef::Struct(struct_value) =
422                bevy_reflect::PartialReflect::reflect_ref(value)
423            {
424                for (i, value) in ::core::iter::Iterator::enumerate(
425                    bevy_reflect::Struct::iter_fields(struct_value),
426                ) {
427                    let name = bevy_reflect::Struct::name_at(struct_value, i).unwrap();
428                    if let Some(v) = bevy_reflect::Struct::field_mut(self, name) {
429                        bevy_reflect::PartialReflect::try_apply(v, value)?;
430                    }
431                }
432            } else {
433                return Result::Err(bevy_reflect::ApplyError::MismatchedKinds {
434                    from_kind: bevy_reflect::PartialReflect::reflect_kind(value),
435                    to_kind: bevy_reflect::ReflectKind::Struct,
436                });
437            }
438            Ok(())
439        }
440
441        #[inline]
442        fn reflect_kind(&self) -> bevy_reflect::ReflectKind {
443            bevy_reflect::ReflectKind::Struct
444        }
445
446        #[inline]
447        fn reflect_ref<'a>(&'a self) -> bevy_reflect::ReflectRef<'a> {
448            bevy_reflect::ReflectRef::Struct(self)
449        }
450
451        #[inline]
452        fn reflect_mut<'a>(&'a mut self) -> bevy_reflect::ReflectMut<'a> {
453            bevy_reflect::ReflectMut::Struct(self)
454        }
455
456        #[inline]
457        fn reflect_owned(self: Box<Self>) -> bevy_reflect::ReflectOwned {
458            bevy_reflect::ReflectOwned::Struct(self)
459        }
460
461        #[inline]
462        fn try_into_reflect(
463            self: Box<Self>,
464        ) -> Result<Box<dyn bevy_reflect::Reflect>, Box<dyn bevy_reflect::PartialReflect>> {
465            Ok(self)
466        }
467
468        #[inline]
469        fn try_as_reflect(&self) -> Option<&dyn bevy_reflect::Reflect> {
470            Some(self)
471        }
472
473        #[inline]
474        fn try_as_reflect_mut(&mut self) -> Option<&mut dyn bevy_reflect::Reflect> {
475            Some(self)
476        }
477
478        #[inline]
479        fn into_partial_reflect(self: Box<Self>) -> Box<dyn bevy_reflect::PartialReflect> {
480            self
481        }
482
483        #[inline]
484        fn as_partial_reflect(&self) -> &dyn bevy_reflect::PartialReflect {
485            self
486        }
487
488        #[inline]
489        fn as_partial_reflect_mut(&mut self) -> &mut dyn bevy_reflect::PartialReflect {
490            self
491        }
492
493        fn reflect_partial_eq(&self, value: &dyn bevy_reflect::PartialReflect) -> Option<bool> {
494            (bevy_reflect::struct_partial_eq)(self, value)
495        }
496
497        #[inline]
498        fn reflect_clone(
499            &self,
500        ) -> Result<Box<dyn bevy_reflect::Reflect>, bevy_reflect::ReflectCloneError> {
501            Ok(Box::new(Clone::clone(self)))
502        }
503    }
504
505    impl<T> bevy_reflect::FromReflect for Notify<T>
506    where
507        Notify<T>: ::core::any::Any + ::core::marker::Send + ::core::marker::Sync,
508        T: Default
509            + Clone
510            + bevy_reflect::FromReflect
511            + bevy_reflect::TypePath
512            + bevy_reflect::MaybeTyped
513            + bevy_reflect::__macro_exports::RegisterForReflection,
514    {
515        fn from_reflect(reflect: &dyn bevy_reflect::PartialReflect) -> Option<Self> {
516            if let bevy_reflect::ReflectRef::Struct(ref_struct) =
517                bevy_reflect::PartialReflect::reflect_ref(reflect)
518            {
519                let mut this = <Self as ::core::default::Default>::default();
520                if let Some(field) = (|| {
521                    <T as bevy_reflect::FromReflect>::from_reflect(bevy_reflect::Struct::field(
522                        ref_struct, "value",
523                    )?)
524                })() {
525                    this.value = field;
526                }
527                Some(this)
528            } else {
529                None
530            }
531        }
532    }
533}