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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
//! Resource event handling.

use crate::core::{
    parking_lot::Mutex,
    pool::{Handle, Pool},
};
use crate::UntypedResource;
use std::{
    path::PathBuf,
    sync::{mpsc::Sender, Arc},
};

/// A resource event.
#[derive(Clone)]
pub enum ResourceEvent {
    /// Occurs when a resource was fully loaded without any errors.
    Loaded(UntypedResource),

    /// Occurs when a resource was already fully loaded, but was reloaded by an explicit request.
    Reloaded(UntypedResource),

    /// Occurs when a resource was just added to a resource container.
    Added(UntypedResource),

    /// Occurs when a resource was removed from a resource container.
    Removed(PathBuf),
}

/// Type alias for event sender.
pub type ResourceEventSender = Sender<ResourceEvent>;

/// Event broadcaster is responsible for delivering resource events to "subscribers".
#[derive(Clone)]
pub struct ResourceEventBroadcaster {
    container: Arc<Mutex<Pool<ResourceEventSender>>>,
}

impl Default for ResourceEventBroadcaster {
    fn default() -> Self {
        Self::new()
    }
}

impl ResourceEventBroadcaster {
    /// Creates new empty event broadcaster.
    pub fn new() -> Self {
        Self {
            container: Arc::new(Default::default()),
        }
    }

    /// Adds an event sender to the broadcaster and returns its handle.
    pub fn add(&self, sender: ResourceEventSender) -> Handle<ResourceEventSender> {
        self.container.lock().spawn(sender)
    }

    /// Removes an event sender by its handle.
    pub fn remove(&self, handle: Handle<ResourceEventSender>) -> ResourceEventSender {
        self.container.lock().free(handle)
    }

    /// Sends an event to all "subscribers" in the broadcaster.
    pub fn broadcast(&self, event: ResourceEvent) {
        let container = self.container.lock();
        for sender in container.iter() {
            let _ = sender.send(event.clone());
        }
    }

    /// Sends a [`ResourceEvent::Loaded`] event to all "subscribers" in the broadcaster.
    pub fn broadcast_loaded(&self, resource: UntypedResource) {
        self.broadcast(ResourceEvent::Loaded(resource))
    }

    /// Sends either a [`ResourceEvent::Loaded`] event or a [`ResourceEvent::Reloaded`] to all
    /// "subscribers" in the broadcaster depending on the `reload` parameter.
    pub fn broadcast_loaded_or_reloaded(&self, resource: UntypedResource, reload: bool) {
        self.broadcast(if reload {
            ResourceEvent::Reloaded(resource)
        } else {
            ResourceEvent::Loaded(resource)
        })
    }
}

#[cfg(test)]
mod test {
    use std::sync::mpsc::channel;

    use super::*;

    #[test]
    fn resource_event_broadcaster_add_and_remove() {
        let broadcaster = ResourceEventBroadcaster::new();
        let (sender, receiver) = channel();

        let h = broadcaster.add(sender);
        assert!(h.is_some());
        assert_eq!(h.index(), 0);
        assert_eq!(h.generation(), 1);

        broadcaster.broadcast(ResourceEvent::Added(UntypedResource::default()));
        assert!(matches!(
            receiver.recv(),
            Ok(ResourceEvent::Added(UntypedResource(_)))
        ));

        broadcaster.remove(h);
        broadcaster.broadcast(ResourceEvent::Added(UntypedResource::default()));
        assert!(receiver.recv().is_err());
    }

    #[test]
    fn resource_event_broadcaster_broadcast_loaded() {
        let broadcaster = ResourceEventBroadcaster::default();
        let (sender, receiver) = channel();
        broadcaster.add(sender);

        broadcaster.broadcast_loaded(UntypedResource::default());
        assert!(matches!(
            receiver.recv(),
            Ok(ResourceEvent::Loaded(UntypedResource(_)))
        ));
    }

    #[test]
    fn resource_event_broadcaster_broadcast_loaded_or_reloaded() {
        let broadcaster = ResourceEventBroadcaster::default();
        let (sender, receiver) = channel();
        broadcaster.add(sender);

        broadcaster.broadcast_loaded_or_reloaded(UntypedResource::default(), false);
        assert!(matches!(
            receiver.recv(),
            Ok(ResourceEvent::Loaded(UntypedResource(_)))
        ));

        broadcaster.broadcast_loaded_or_reloaded(UntypedResource::default(), true);
        assert!(matches!(
            receiver.recv(),
            Ok(ResourceEvent::Reloaded(UntypedResource(_)))
        ));
    }

    #[test]
    fn resource_event_broadcaster_clone() {
        let broadcaster = ResourceEventBroadcaster::new();
        let (sender, receiver) = channel();

        broadcaster.add(sender);
        let broadcaster2 = broadcaster.clone();

        broadcaster2.broadcast(ResourceEvent::Added(UntypedResource::default()));
        assert!(matches!(
            receiver.recv(),
            Ok(ResourceEvent::Added(UntypedResource(_)))
        ));
    }
}