fyrox_resource/
event.rs

1// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in all
11// copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19// SOFTWARE.
20
21//! Resource event handling.
22
23use crate::core::{
24    parking_lot::Mutex,
25    pool::{Handle, Pool},
26};
27use crate::UntypedResource;
28use std::{
29    path::PathBuf,
30    sync::{mpsc::Sender, Arc},
31};
32
33/// A resource event.
34#[derive(Clone)]
35pub enum ResourceEvent {
36    /// Occurs when a resource was fully loaded without any errors.
37    Loaded(UntypedResource),
38
39    /// Occurs when a resource was already fully loaded, but was reloaded by an explicit request.
40    Reloaded(UntypedResource),
41
42    /// Occurs when a resource was just added to a resource container.
43    Added(UntypedResource),
44
45    /// Occurs when a resource was removed from a resource container.
46    Removed(PathBuf),
47}
48
49/// Type alias for event sender.
50pub type ResourceEventSender = Sender<ResourceEvent>;
51
52/// Event broadcaster is responsible for delivering resource events to "subscribers".
53#[derive(Clone)]
54pub struct ResourceEventBroadcaster {
55    container: Arc<Mutex<Pool<ResourceEventSender>>>,
56}
57
58impl Default for ResourceEventBroadcaster {
59    fn default() -> Self {
60        Self::new()
61    }
62}
63
64impl ResourceEventBroadcaster {
65    /// Creates new empty event broadcaster.
66    pub fn new() -> Self {
67        Self {
68            container: Arc::new(Default::default()),
69        }
70    }
71
72    /// Adds an event sender to the broadcaster and returns its handle.
73    pub fn add(&self, sender: ResourceEventSender) -> Handle<ResourceEventSender> {
74        self.container.lock().spawn(sender)
75    }
76
77    /// Removes an event sender by its handle.
78    pub fn remove(&self, handle: Handle<ResourceEventSender>) -> ResourceEventSender {
79        self.container.lock().free(handle)
80    }
81
82    /// Sends an event to all "subscribers" in the broadcaster.
83    pub fn broadcast(&self, event: ResourceEvent) {
84        let container = self.container.lock();
85        for sender in container.iter() {
86            let _ = sender.send(event.clone());
87        }
88    }
89
90    /// Sends a [`ResourceEvent::Loaded`] event to all "subscribers" in the broadcaster.
91    pub fn broadcast_loaded(&self, resource: UntypedResource) {
92        self.broadcast(ResourceEvent::Loaded(resource))
93    }
94
95    /// Sends either a [`ResourceEvent::Loaded`] event or a [`ResourceEvent::Reloaded`] to all
96    /// "subscribers" in the broadcaster depending on the `reload` parameter.
97    pub fn broadcast_loaded_or_reloaded(&self, resource: UntypedResource, reload: bool) {
98        self.broadcast(if reload {
99            ResourceEvent::Reloaded(resource)
100        } else {
101            ResourceEvent::Loaded(resource)
102        })
103    }
104}
105
106#[cfg(test)]
107mod test {
108    use std::sync::mpsc::channel;
109
110    use super::*;
111
112    #[test]
113    fn resource_event_broadcaster_add_and_remove() {
114        let broadcaster = ResourceEventBroadcaster::new();
115        let (sender, receiver) = channel();
116
117        let h = broadcaster.add(sender);
118        assert!(h.is_some());
119        assert_eq!(h.index(), 0);
120        assert_eq!(h.generation(), 1);
121
122        broadcaster.broadcast(ResourceEvent::Added(UntypedResource::default()));
123        assert!(matches!(
124            receiver.recv(),
125            Ok(ResourceEvent::Added(UntypedResource(_)))
126        ));
127
128        broadcaster.remove(h);
129        broadcaster.broadcast(ResourceEvent::Added(UntypedResource::default()));
130        assert!(receiver.recv().is_err());
131    }
132
133    #[test]
134    fn resource_event_broadcaster_broadcast_loaded() {
135        let broadcaster = ResourceEventBroadcaster::default();
136        let (sender, receiver) = channel();
137        broadcaster.add(sender);
138
139        broadcaster.broadcast_loaded(UntypedResource::default());
140        assert!(matches!(
141            receiver.recv(),
142            Ok(ResourceEvent::Loaded(UntypedResource(_)))
143        ));
144    }
145
146    #[test]
147    fn resource_event_broadcaster_broadcast_loaded_or_reloaded() {
148        let broadcaster = ResourceEventBroadcaster::default();
149        let (sender, receiver) = channel();
150        broadcaster.add(sender);
151
152        broadcaster.broadcast_loaded_or_reloaded(UntypedResource::default(), false);
153        assert!(matches!(
154            receiver.recv(),
155            Ok(ResourceEvent::Loaded(UntypedResource(_)))
156        ));
157
158        broadcaster.broadcast_loaded_or_reloaded(UntypedResource::default(), true);
159        assert!(matches!(
160            receiver.recv(),
161            Ok(ResourceEvent::Reloaded(UntypedResource(_)))
162        ));
163    }
164
165    #[test]
166    fn resource_event_broadcaster_clone() {
167        let broadcaster = ResourceEventBroadcaster::new();
168        let (sender, receiver) = channel();
169
170        broadcaster.add(sender);
171        let broadcaster2 = broadcaster.clone();
172
173        broadcaster2.broadcast(ResourceEvent::Added(UntypedResource::default()));
174        assert!(matches!(
175            receiver.recv(),
176            Ok(ResourceEvent::Added(UntypedResource(_)))
177        ));
178    }
179}