duat_core/data/
ro.rs

1use std::{
2    any::TypeId,
3    sync::{
4        Arc,
5        atomic::{AtomicUsize, Ordering},
6    },
7};
8
9use super::{Data, RwData, private::InnerData};
10// use parking_lot::{RwLock, RwLockReadGuard};
11use super::{RwLock, RwLockReadGuard};
12
13/// A read-only reference to information.
14///
15/// A data type that can read, but not write, the inner data. If you
16/// wish to get a read write struct, see [`RwData`]. This struct is
17/// usually what you will get as a readable handle from structs
18/// containing [`RwData`]. For example, when opening a new file, we
19/// use a hook, called the `constructor_hook`, to do certain actions
20/// to that file, like opening surrounding [`Widget`]s. This hook can
21/// provide an [`RoData`], which is then queryed by the
22/// widget for information about the file. This also prevents the
23/// modification of data by third parties which shouldn`t be able to
24/// do so.
25///
26/// Can only be created by cloning the [`Arc<RwLock<T>>`] from a
27/// [`RwData`], or through cloning.
28///
29/// [`Widget`]: crate::widgets::Widget
30/// [`RoData`]: RoData
31pub struct RoData<T>
32where
33    T: ?Sized + 'static,
34{
35    data: Arc<RwLock<T>>,
36    cur_state: Arc<AtomicUsize>,
37    read_state: AtomicUsize,
38    type_id: TypeId,
39}
40
41impl<T> RoData<T> {
42    /// Returns a new instance of a [`RoData`], assuming that it is
43    /// sized.
44    ///
45    /// This has to be sized because of some Rust limitations, as you
46    /// can`t really pass an unsized argument to a function (for now).
47    /// If you're looking to store unsized types (`dyn Trait`,
48    /// `\[Type\]`, etc) on a [`RwData`], see [`RwData::new_unsized`].
49    pub fn new(data: T) -> Self {
50        Self {
51            data: Arc::new(RwLock::new(data)),
52            cur_state: Arc::new(AtomicUsize::new(1)),
53            read_state: AtomicUsize::new(1),
54            type_id: TypeId::of::<T>(),
55        }
56    }
57}
58
59impl<T> RoData<T>
60where
61    T: ?Sized,
62{
63    /// Returns a new instance of [`RoData`], assuming that it is
64    /// unsized.
65    ///
66    /// This method is only required if you're dealing with types that
67    /// may not be [`Sized`] (`dyn Trait`, `[Type]`, etc). If the type
68    /// in question is sized, use [`RwData::new`] instead.
69    pub fn new_unsized<SizedT: 'static>(data: Arc<RwLock<T>>) -> Self {
70        // It's 1 here so that any `RoState`s created from this will have
71        // `has_changed()` return `true` at least once, by copying the
72        // second value - 1.
73        Self {
74            data,
75            cur_state: Arc::new(AtomicUsize::new(1)),
76            read_state: AtomicUsize::new(1),
77            type_id: TypeId::of::<SizedT>(),
78        }
79    }
80
81    /// Blocking reference to the information.
82    ///
83    /// Also makes it so that [`has_changed`] returns `false`.
84    ///
85    /// # Examples
86    ///
87    /// Since this is a blocking read, the thread will hault while the
88    /// data is being written to:
89    /// ```rust
90    /// # use std::{thread, time::{Duration, Instant}};
91    /// # use duat_core::data::{Data, RoData, RwData};
92    /// let read_write_data = RwData::new("☹️");
93    /// let read_only_data = RoData::from(&read_write_data);
94    /// let instant = Instant::now();
95    /// thread::scope(|scope| {
96    ///     scope.spawn(|| {
97    ///         let mut read_write = read_write_data.write();
98    ///         // Supposedly long computations.
99    ///         thread::sleep(Duration::from_millis(150));
100    ///         *read_write = "☺️";
101    ///     });
102    ///
103    ///     // Just making sure that the read happens slightly after the write.
104    ///     thread::sleep(Duration::from_millis(10));
105    ///
106    ///     let read_only = read_only_data.read();
107    ///     let time_elapsed = Instant::now().duration_since(instant);
108    ///     assert!(time_elapsed >= Duration::from_millis(100));
109    ///     assert!(*read_only == "☺️");
110    /// });
111    /// ```
112    /// Note that other reads will **NOT** block reading in this way,
113    /// only writes:
114    /// ```rust
115    /// # use std::{thread, time::{Duration, Instant} };
116    /// # use duat_core::data::{Data, RoData, RwData};
117    /// let read_write_data = RwData::new("☹️");
118    /// let read_only_data = RoData::from(&read_write_data);
119    /// let instant = Instant::now();
120    /// thread::scope(|scope| {
121    ///     scope.spawn(|| {
122    ///         let read_only = read_write_data.read();
123    ///         // The thread hangs, but reading is still possible.
124    ///         thread::sleep(Duration::from_millis(100));
125    ///     });
126    ///
127    ///     // Just making sure that this read happens slightly after the last one.
128    ///     thread::sleep(Duration::from_millis(1));
129    ///
130    ///     let read_only = read_only_data.read();
131    ///     let time_elapsed = Instant::now().duration_since(instant);
132    ///     assert!(time_elapsed < Duration::from_millis(100));
133    /// });
134    /// ```
135    /// [`has_changed`]: Self::has_changed
136    pub fn read(&self) -> RwLockReadGuard<'_, T> {
137        let cur_state = self.cur_state().load(Ordering::Acquire);
138        self.read_state().store(cur_state, Ordering::Release);
139        self.data()
140    }
141
142    /// Blocking inspection of the inner data.
143    ///
144    /// Also makes it so that [`has_changed`] returns `false`.
145    ///
146    /// # Examples
147    ///
148    /// This method is useful if you want to scope the reading, or
149    /// need to drop the reference quickly, so it can be written to.
150    ///
151    /// You can do this:
152    /// ```rust
153    /// # use duat_core::data::{Data, RoData, RwData};
154    /// # fn add_to_count(count: &mut usize) {}
155    /// # fn new_count() -> usize {
156    /// #     0
157    /// # }
158    /// let count = RwData::new(31);
159    /// let count_reader = RoData::from(&count);
160    ///
161    /// // The read write counterpart to `inspect`.
162    /// count.mutate(|count| {
163    ///     *count += 5;
164    ///     add_to_count(count)
165    /// });
166    ///
167    /// count_reader.inspect(|count| { /* reading operations */ });
168    ///
169    /// *count.write() = new_count();
170    /// ```
171    /// Instead of this:
172    /// ```rust
173    /// # use duat_core::data::{Data, RoData, RwData};
174    /// # fn add_to_count(count: &mut usize) {}
175    /// # fn new_count() -> usize {
176    /// #     0
177    /// # }
178    /// let count = RwData::new(31);
179    /// let count_reader = RoData::from(&count);
180    ///
181    /// // The read write counterpart to `inspect`.
182    /// let mut count_write = count.write();
183    /// *count_write += 5;
184    /// add_to_count(&mut *count_write);
185    /// drop(count_write);
186    ///
187    /// let count_read = count_reader.read();
188    /// // reading operations
189    /// drop(count_read);
190    ///
191    /// *count.write() = new_count();
192    /// ```
193    /// Or this:
194    /// ```rust
195    /// # use duat_core::data::{Data, RoData, RwData};
196    /// # fn add_to_count(count: &mut usize) {}
197    /// # fn new_count() -> usize {
198    /// #     0
199    /// # }
200    /// let count = RwData::new(31);
201    /// let count_reader = RoData::from(&count);
202    ///
203    /// // The read write counterpart to `inspect`.
204    /// {
205    ///     let mut count = count.write();
206    ///     *count += 5;
207    ///     add_to_count(&mut count)
208    /// }
209    ///
210    /// {
211    ///     let count = count.read();
212    ///     // reading operations
213    /// }
214    ///
215    /// *count.write() = new_count();
216    /// ```
217    /// [`has_changed`]: Self::has_changed
218    pub fn inspect<U>(&self, f: impl FnOnce(&T) -> U) -> U {
219        let cur_state = self.cur_state().load(Ordering::Acquire);
220        self.read_state().store(cur_state, Ordering::Release);
221        f(&self.data())
222    }
223
224    /// Non blocking reference to the information.
225    ///
226    /// If successful, also makes it so that [`has_changed`] returns
227    /// `false`.
228    ///
229    /// # Examples
230    ///
231    /// Unlike [`read`], can fail to return a reference to the
232    /// underlying data:
233    /// ```rust
234    /// # use duat_core::data::{Data, RwData};
235    /// let new_data = RwData::new("hello 👋");
236    ///
237    /// let mut blocking_write = new_data.write();
238    /// *blocking_write = "bye 👋";
239    ///
240    /// let try_read = new_data.try_read();
241    /// assert!(matches!(try_read, None));
242    /// ```
243    ///
244    /// [`has_changed`]: Self::has_changed
245    /// [`read`]: Self::read
246    pub fn try_read(&self) -> Option<RwLockReadGuard<'_, T>> {
247        self.try_data().inspect(|_| {
248            let cur_state = self.cur_state().load(Ordering::Acquire);
249            self.read_state().store(cur_state, Ordering::Release);
250        })
251    }
252
253    /// Non blocking inspection of the inner data.
254    ///
255    /// If successful, also makes it so that [`has_changed`] returns
256    /// `false`.
257    ///
258    /// # Examples
259    ///
260    /// Unlike [`inspect`], can fail to return a reference to the
261    /// underlying data:
262    /// ```rust
263    /// # use duat_core::data::{Data, RwData};
264    /// let new_data = RwData::new("hello 👋");
265    ///
266    /// let try_inspect = new_data.mutate(|blocking_mutate| {
267    ///     *blocking_mutate = "bye 👋";
268    ///
269    ///     new_data.try_inspect(|try_inspect| *try_inspect == "bye 👋")
270    /// });
271    ///
272    /// assert!(matches!(try_inspect, None));
273    /// ```
274    ///
275    /// [`has_changed`]: Self::has_changed
276    /// [`inspect`]: Self::inspect
277    pub fn try_inspect<U>(&self, f: impl FnOnce(&T) -> U) -> Option<U> {
278        self.try_data().map(|data| {
279            let cur_state = self.cur_state().load(Ordering::Acquire);
280            self.read_state().store(cur_state, Ordering::Release);
281            f(&data)
282        })
283    }
284
285    /// Whether or not it has changed since it was last read.
286    ///
287    /// A "change" is defined as any time the methods [`write`],
288    /// [`mutate`], [`try_write`], or [`try_mutate`], are called on an
289    /// [`RwData`]. Once `has_changed` is called, the data will be
290    /// considered unchanged since the last `has_changed` call, for
291    /// that specific instance of a [`Data`].
292    ///
293    /// When first creating a [`Data`] type, `has_changed`
294    /// will return `false`;
295    ///
296    /// # Examples
297    /// ```rust
298    /// use duat_core::data::{Data, RoData, RwData};
299    /// let new_data = RwData::new("Initial text");
300    /// assert!(!new_data.has_changed());
301    ///
302    /// let first_reader = RoData::from(&new_data);
303    ///
304    /// *new_data.write() = "Final text";
305    ///
306    /// let second_reader = RoData::from(&new_data);
307    ///
308    /// assert!(first_reader.has_changed());
309    /// assert!(!second_reader.has_changed());
310    /// ```
311    ///
312    /// [`write`]: RwData::write
313    /// [`mutate`]: RwData::mutate
314    /// [`try_write`]: RwData::try_write
315    /// [`try_mutate`]: RwData::try_mutate
316    pub fn has_changed(&self) -> bool {
317        let cur_state = self.cur_state().load(Ordering::Relaxed);
318        let read_state = self.read_state().swap(cur_state, Ordering::Relaxed);
319        cur_state > read_state
320    }
321
322    /// Returns `true` if both [`Data<T>`]s point to the same
323    /// data.
324    ///
325    /// # Examples
326    /// ```rust
327    /// # use duat_core::data::{Data, RwData};
328    /// let data_1 = RwData::new(false);
329    /// let data_1_clone = data_1.clone();
330    ///
331    /// let data_2 = RwData::new(true);
332    ///
333    /// assert!(data_1.ptr_eq(&data_1_clone));
334    /// assert!(!data_1.ptr_eq(&data_2));
335    /// ```
336    pub fn ptr_eq<U>(&self, other: &impl Data<U>) -> bool
337    where
338        U: ?Sized,
339    {
340        Arc::as_ptr(self.cur_state()) as usize == Arc::as_ptr(other.cur_state()) as usize
341    }
342
343    /// Blocking inspection of the inner data.
344    ///
345    /// # Examples
346    ///
347    /// You may want this method if you're storing a list of
348    /// [`RwData`]s, and want to know, at runtime, what type
349    /// each element is:
350    ///
351    /// ```rust
352    /// # use std::{any::Any, fmt::Display, sync::Arc};
353    /// # use duat_core::data::{RwData, RwLock};
354    /// let list: [RwData<dyn Display>; 3] = [
355    ///     RwData::new_unsized::<String>(Arc::new(RwLock::new(String::from(
356    ///         "I can show you the world",
357    ///     )))),
358    ///     RwData::new_unsized::<String>(Arc::new(RwLock::new(String::from(
359    ///         "Shining, shimmering, splendid",
360    ///     )))),
361    ///     RwData::new_unsized::<char>(Arc::new(RwLock::new('🧞'))),
362    /// ];
363    ///
364    /// assert!(matches!(
365    ///     list[2].inspect_as::<char, bool>(|char| char.is_ascii()),
366    ///     Some(false)
367    /// ));
368    /// assert!(matches!(
369    ///     list[1].inspect_as::<char, bool>(|char| char.is_ascii()),
370    ///     None
371    /// ));
372    /// ```
373    ///
374    /// [`RwData`]: RwData
375    pub fn inspect_as<U: 'static, R>(&self, f: impl FnOnce(&U) -> R) -> Option<R> {
376        self.data_is::<U>().then(|| {
377            let ptr = Arc::as_ptr(&self.data);
378            let cast = unsafe { ptr.cast::<RwLock<U>>().as_ref().unwrap() };
379
380            self.read_state
381                .store(self.cur_state.load(Ordering::Acquire), Ordering::Release);
382
383            f(&cast.read())
384        })
385    }
386
387    pub fn data_is<U>(&self) -> bool
388    where
389        U: 'static,
390    {
391        self.type_id == TypeId::of::<U>()
392    }
393
394    /// Tries to downcast to a concrete type.
395    pub fn try_downcast<U>(&self) -> Option<RoData<U>>
396    where
397        U: 'static,
398    {
399        if self.data_is::<U>() {
400            let Self { data, cur_state, read_state, .. } = self.clone();
401            let raw_data_pointer = Arc::into_raw(data);
402            let data = unsafe { Arc::from_raw(raw_data_pointer.cast::<RwLock<U>>()) };
403            Some(RoData {
404                data,
405                cur_state,
406                read_state,
407                type_id: TypeId::of::<U>(),
408            })
409        } else {
410            None
411        }
412    }
413}
414
415impl<T> Default for RoData<T>
416where
417    T: Default,
418{
419    fn default() -> Self {
420        Self {
421            data: Arc::new(RwLock::new(T::default())),
422            cur_state: Arc::new(AtomicUsize::new(0)),
423            read_state: AtomicUsize::new(0),
424            type_id: TypeId::of::<T>(),
425        }
426    }
427}
428
429impl<T> std::fmt::Debug for RoData<T>
430where
431    T: ?Sized + std::fmt::Debug,
432{
433    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
434        std::fmt::Debug::fmt(&*self.data.read(), f)
435    }
436}
437
438impl<T> From<&RwData<T>> for RoData<T>
439where
440    T: ?Sized,
441{
442    fn from(value: &RwData<T>) -> Self {
443        RoData {
444            data: value.data.clone(),
445            cur_state: value.cur_state.clone(),
446            read_state: AtomicUsize::new(value.cur_state.load(Ordering::Relaxed)),
447            type_id: value.type_id,
448        }
449    }
450}
451
452impl<T> InnerData<T> for RoData<T>
453where
454    T: ?Sized,
455{
456    fn data(&self) -> RwLockReadGuard<'_, T> {
457        self.data.read()
458    }
459
460    fn try_data(&self) -> Option<RwLockReadGuard<'_, T>> {
461        self.data.try_read()
462    }
463
464    fn cur_state(&self) -> &Arc<AtomicUsize> {
465        &self.cur_state
466    }
467
468    fn read_state(&self) -> &AtomicUsize {
469        &self.read_state
470    }
471}
472
473// NOTE: Each `RoState` of a given state will have its own internal
474// update counter.
475impl<T> Clone for RoData<T>
476where
477    T: ?Sized,
478{
479    fn clone(&self) -> Self {
480        RoData {
481            data: self.data.clone(),
482            cur_state: self.cur_state.clone(),
483            read_state: AtomicUsize::new(self.cur_state.load(Ordering::Relaxed)),
484            type_id: self.type_id,
485        }
486    }
487}
488
489impl<T: ?Sized> Data<T> for RoData<T> {
490    fn to_ro(&self) -> RoData<T> {
491        self.clone()
492    }
493
494    fn has_changed(&self) -> bool {
495        self.has_changed()
496    }
497}