output_tracker/non_threadsafe/
mod.rs

1//! Non-threadsafe variant of [`OutputTracker`] and [`OutputSubject`].
2//!
3//! For an example on how to use it see the crate level documentation.
4
5use crate::inner_subject::{BasicSubject, CelledSubject};
6use crate::inner_tracker::{BasicTracker, CelledTracker};
7use crate::non_threadsafe::Error::{BorrowMutTrackerFailed, BorrowTrackerFailed};
8use crate::tracker_handle::TrackerHandle;
9use std::cell::{BorrowError, BorrowMutError, Ref, RefCell, RefMut};
10use std::rc::Rc;
11
12/// Error type for the non-threadsafe [`OutputTracker`] and [`OutputSubject`].
13#[derive(thiserror::Error, Debug)]
14pub enum Error {
15    /// Failed to obtain an immutable borrow of the tracker.
16    #[error("failed to obtain an immutable borrow of the tracker, reason: {0}")]
17    BorrowTrackerFailed(BorrowError),
18    /// Failed to obtain a mutable borrow of the tracker.
19    #[error("failed to obtain a mutable borrow of the tracker, reason: {0}")]
20    BorrowMutTrackerFailed(BorrowMutError),
21    /// Failed to obtain an immutable borrow of the subject.
22    #[error("failed to obtain an immutable borrow of the subject, reason: {0}")]
23    BorrowSubjectFailed(BorrowError),
24    /// Failed to obtain a mutable borrow of the subject.
25    #[error("failed to obtain a mutable borrow of the subject, reason: {0}")]
26    BorrowMutSubjectFailed(BorrowMutError),
27}
28
29/// Collects state data or action data of any kind.
30///
31/// This is the non-threadsafe variant.
32///
33/// The tracked data can be read any time and as often as needed by calling the
34/// [`output()`][OutputTracker::output]. Each time the output is read, all data
35/// collected so far are returned. To track only new data emitted after the last
36/// read of the output, the [`clear()`][OutputTracker::clear] function should be
37/// called.
38///
39/// The tracker can be deactivated by calling the [`stop()`][OutputTracker::stop]
40/// function to stop it from collecting data. Once stopped the tracker can not
41/// be activated again.
42#[derive(Debug)]
43pub struct OutputTracker<M> {
44    handle: TrackerHandle,
45    inner: NonThreadsafeTracker<M>,
46    subject: NonThreadsafeSubject<M>,
47}
48
49impl<M> OutputTracker<M> {
50    const fn new(
51        handle: TrackerHandle,
52        inner: NonThreadsafeTracker<M>,
53        subject: NonThreadsafeSubject<M>,
54    ) -> Self {
55        Self {
56            handle,
57            inner,
58            subject,
59        }
60    }
61
62    /// Stops this tracker.
63    ///
64    /// After stopping a tracker it no longer tracks emitted data. Once a
65    /// tracker is stopped it can not be activated again.
66    pub fn stop(&self) -> Result<(), Error> {
67        self.subject.remove_tracker(self.handle)
68    }
69
70    /// Clears the data this tracker has been collected so far.
71    ///
72    /// After clearing a tracker it still tracks any data which is emitted after
73    /// this clear function has been called.
74    pub fn clear(&self) -> Result<(), Error> {
75        self.inner.clear()
76    }
77
78    /// Returns the data collected by this tracker so far.
79    ///
80    /// Each time this function is called it returns all data collected since
81    /// the tracker has been created or since the last call to of the
82    /// [`clear()`][OutputTracker::clear] function. To track only data that are
83    /// emitted after the last time the output was read, the
84    /// [`clear()`][OutputTracker::clear] should be called after the output has
85    /// been read.
86    pub fn output(&self) -> Result<Vec<M>, Error>
87    where
88        M: Clone,
89    {
90        self.inner.output()
91    }
92}
93
94/// Holds created [`OutputTracker`]s and emits data to all known trackers.
95///
96/// This is the non-threadsafe variant.
97///
98/// New [`OutputTracker`]s can be created by calling the
99/// [`create_tracker()`][OutputSubject::create_tracker] function.
100///
101/// The [`emit(data)`][OutputSubject::emit] function emits data to all trackers,
102/// that have been created for this subject and are not stopped yet.
103#[derive(Default, Debug, Clone)]
104pub struct OutputSubject<M> {
105    inner: NonThreadsafeSubject<M>,
106}
107
108impl<M> OutputSubject<M> {
109    /// Constructs a new [`OutputSubject`].
110    ///
111    /// A new subject does nothing unless one or more trackers have been
112    /// created.
113    #[must_use]
114    pub fn new() -> Self {
115        Self {
116            inner: NonThreadsafeSubject::new(),
117        }
118    }
119}
120
121impl<M> OutputSubject<M>
122where
123    M: Clone,
124{
125    /// Creates a new [`OutputTracker`] and registers it to be ready to track
126    /// emitted data.
127    pub fn create_tracker(&self) -> Result<OutputTracker<M>, Error> {
128        let new_tracker = NonThreadsafeTracker::new();
129        let handle = self.inner.add_tracker(new_tracker.clone())?;
130        Ok(OutputTracker::new(handle, new_tracker, self.inner.clone()))
131    }
132
133    /// Emits given data to all active [`OutputTracker`]s.
134    ///
135    /// Stopped [`OutputTracker`]s do not receive any emitted data.
136    pub fn emit(&self, data: M) -> Result<(), Error> {
137        self.inner.emit(data)
138    }
139}
140
141#[derive(Default, Debug, Clone)]
142struct NonThreadsafeSubject<M> {
143    cell: Rc<RefCell<BasicSubject<M, NonThreadsafeTracker<M>>>>,
144}
145
146impl<M> NonThreadsafeSubject<M> {
147    fn new() -> Self {
148        Self {
149            cell: Rc::new(RefCell::new(BasicSubject::new())),
150        }
151    }
152}
153
154impl<M> CelledSubject<M, NonThreadsafeTracker<M>> for NonThreadsafeSubject<M> {
155    type Inner<'a>
156        = Ref<'a, BasicSubject<M, NonThreadsafeTracker<M>>>
157    where
158        M: 'a;
159    type InnerMut<'a>
160        = RefMut<'a, BasicSubject<M, NonThreadsafeTracker<M>>>
161    where
162        M: 'a;
163    type Error = Error;
164
165    fn subject(&self) -> Result<Self::Inner<'_>, Error> {
166        self.cell.try_borrow().map_err(Error::BorrowSubjectFailed)
167    }
168
169    fn subject_mut(&self) -> Result<Self::InnerMut<'_>, Error> {
170        self.cell
171            .try_borrow_mut()
172            .map_err(Error::BorrowMutSubjectFailed)
173    }
174}
175
176#[derive(Debug, Clone)]
177struct NonThreadsafeTracker<M> {
178    cell: Rc<RefCell<BasicTracker<M>>>,
179}
180
181impl<M> CelledTracker<M> for NonThreadsafeTracker<M> {
182    type Inner<'a>
183        = Ref<'a, BasicTracker<M>>
184    where
185        Self: 'a;
186    type InnerMut<'a>
187        = RefMut<'a, BasicTracker<M>>
188    where
189        Self: 'a;
190    type Error = Error;
191
192    fn new() -> Self {
193        Self {
194            cell: Rc::new(RefCell::new(BasicTracker::new())),
195        }
196    }
197
198    fn tracker(&self) -> Result<Self::Inner<'_>, Self::Error> {
199        self.cell.try_borrow().map_err(BorrowTrackerFailed)
200    }
201
202    fn tracker_mut(&self) -> Result<Self::InnerMut<'_>, Self::Error> {
203        self.cell.try_borrow_mut().map_err(BorrowMutTrackerFailed)
204    }
205}
206
207#[cfg(test)]
208mod tests;