1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
use core::hint;
use core::mem;
use core::ops::Deref;

use alloc::sync::Arc;
use alloc::sync::Weak;
use parking_lot::Mutex;
use parking_lot::RwLock;
use parking_lot::RwLockReadGuard;

/// `Listener` implements the emitter side of the [Output Tracking pattern].
///
/// [Output Tracking]: https://www.jamesshore.com/v2/projects/nullables/testing-without-mocks#output-tracking
pub struct Listener<T> {
    trackers: Mutex<Vec<Weak<RwLock<Vec<T>>>>>,
}

impl<T> Listener<T> {
    /// Emit a new event to attached listeners.
    pub fn emit(&self, event: &T)
    where
        T: Clone,
    {
        let should_prune = self
            .trackers
            .lock()
            .iter()
            .fold(false, |should_prune, tracker| {
                let Some(tracker) = tracker.upgrade() else {
                    return true;
                };

                tracker.write().push(event.clone());
                should_prune
            });

        if should_prune {
            self.prune();
        }
    }

    /// Create a new [`Tracker`], connected to this listener.
    pub fn track(&self) -> Tracker<T> {
        let events = Arc::default();
        self.trackers.lock().push(Arc::downgrade(&events));
        Tracker { events }
    }
}

impl<T> Listener<T> {
    fn prune(&self) {
        self.trackers.lock().retain(|arc| 0 < arc.strong_count());
    }
}

impl<T> Default for Listener<T> {
    fn default() -> Self {
        let trackers = Mutex::default();
        Self { trackers }
    }
}

/// `Tracker` implements the receiver side of the [Output Tracking pattern].
///
/// [Output Tracking]: https://www.jamesshore.com/v2/projects/nullables/testing-without-mocks#output-tracking
#[derive(Clone, Debug)]
pub struct Tracker<T> {
    events: Arc<RwLock<Vec<T>>>,
}

impl<T> Tracker<T> {
    /// Consume all current events.
    ///
    /// The consumed events will not be returned by future invocations of `data` or `consume`.
    #[must_use]
    pub fn consume(&self) -> Vec<T> {
        let mut events = self.events.write();
        mem::take(&mut events)
    }

    /// Get a read-only view of tracked data so far.
    ///
    /// <div class="warning">
    ///
    /// This will block the sender side while the returned handle exists, so it should not be held for a long time.
    /// Alternatively, you can copy out of the returned view or use [`Tracker::consume`] if you do not need to retain the
    /// data.
    ///
    /// </div>
    #[must_use]
    pub fn data(&self) -> TrackerData<T> {
        TrackerData(self.events.read())
    }

    /// Stop listening on this `Tracker`.
    ///
    /// This returns any remaining events in the tracker.
    #[must_use]
    pub fn stop(mut self) -> Vec<T> {
        loop {
            match Arc::try_unwrap(self.events) {
                Ok(events) => return events.into_inner(),
                Err(ev) => {
                    self.events = ev;
                    hint::spin_loop();
                }
            };
        }
    }
}

/// `TrackerData` is a read-only view of data tracked in an [`OutputTracker`] so far.
///
/// <div class="warning">
///
/// This will block the sender side while the handle exists, so it should not be held for a long time.
/// Alternatively, you can copy out of the returned view or use [`Tracker::consume`] if you do not need to retain the
/// data.
///
/// </div>
#[derive(Debug)]
pub struct TrackerData<'l, T>(RwLockReadGuard<'l, Vec<T>>);

impl<T> Deref for TrackerData<'_, T> {
    type Target = [T];

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}