limnus_message/lib.rs
1/*
2 * Copyright (c) Peter Bjorklund. All rights reserved. https://github.com/swamp/limnus
3 * Licensed under the MIT License. See LICENSE in the project root for license information.
4 */
5pub mod prelude;
6
7use std::{
8 any::{Any, TypeId},
9 collections::HashMap,
10 fmt::Debug,
11 marker::PhantomData,
12 slice::Iter,
13};
14
15use limnus_macros::Resource;
16use limnus_resource::Resource;
17use tracing::trace;
18
19/// Trait representing a message in the system.
20///
21/// Messages must be `'static` to allow for type-erased storage and must implement `Copy`,
22/// `Debug`, `Send`, and `Sync` to ensure they can be safely shared across threads and easily debugged.
23pub trait Message: 'static + Debug + Send + Sync {}
24
25/// Unique identifier for a specific message type.
26///
27/// This is primarily used for debugging purposes.
28#[derive(Debug)]
29pub struct MessageId<M: Message> {
30 /// Internal value representing the message ID.
31 ///
32 value: u16,
33
34 /// Phantom data to associate the ID with its message type.
35 _phantom: PhantomData<M>,
36}
37
38impl<M: Message> Copy for MessageId<M> {}
39
40impl<M: Message> Clone for MessageId<M> {
41 fn clone(&self) -> Self {
42 *self
43 }
44}
45
46impl<M: Message> MessageId<M> {
47 /// Creates a new `MessageId` with the given value.
48 #[must_use]
49 pub const fn new(value: u16) -> Self {
50 Self {
51 value,
52 _phantom: PhantomData,
53 }
54 }
55
56 /// Retrieves the underlying value of the `MessageId`.
57 #[must_use]
58 pub const fn value(&self) -> u16 {
59 self.value
60 }
61}
62
63/// Stores information about a message, including its ID and the message itself.
64///
65/// This struct is used internally to manage messages across different frames.
66#[derive(Debug)]
67struct MessageInfo<M: Message> {
68 /// Unique identifier for the message.
69 #[allow(unused)]
70 message_id: MessageId<M>,
71
72 /// The actual message data.
73 message: M,
74}
75
76/// Container for managing messages of a specific type.
77///
78/// This struct maintains separate lists for messages from the current and previous frames.
79#[derive(Default, Resource, Debug)]
80pub struct Messages<M: Message> {
81 /// Messages sent in the previous frame.
82 previous_frame_messages: Vec<MessageInfo<M>>,
83
84 /// Messages sent in the current frame.
85 current_messages: Vec<MessageInfo<M>>,
86}
87
88impl<M: Message> Messages<M> {
89 /// Creates a new `Messages` container.
90 #[must_use]
91 pub const fn new() -> Self {
92 Self {
93 previous_frame_messages: Vec::new(),
94 current_messages: Vec::new(),
95 }
96 }
97
98 /// Sends a new message, assigning it a unique `MessageId`.
99 ///
100 /// The message is added to the `current_messages` list.
101 ///
102 /// # Arguments
103 ///
104 /// * `message` - The message to be sent.
105 ///
106 /// # Returns
107 ///
108 /// A `MessageId` uniquely identifying the sent message.
109 pub fn send(&mut self, message: M) -> MessageId<M> {
110 let message_id = MessageId::new(self.current_messages.len() as u16);
111
112 trace!("Sending message: {:?}", message);
113
114 let message_info = MessageInfo {
115 message_id,
116 message,
117 };
118
119 self.current_messages.push(message_info);
120
121 message_id
122 }
123
124 /// Swaps the current and previous frame message lists.
125 ///
126 /// This should be called at the start of each new frame (update) to transition messages appropriately.
127 pub fn swap(&mut self) {
128 self.previous_frame_messages.clear();
129
130 std::mem::swap(
131 &mut self.previous_frame_messages,
132 &mut self.current_messages,
133 );
134
135 self.current_messages.clear();
136 }
137
138 /// Returns an iterator over the current frame's messages.
139 #[must_use]
140 pub fn iter_current(&self) -> MessagesIterator<'_, M> {
141 MessagesIterator {
142 iter: self.current_messages.iter(),
143 }
144 }
145
146 /// Returns an iterator over the previous frame's messages.
147 #[must_use]
148 pub fn iter_previous(&self) -> MessagesIterator<'_, M> {
149 MessagesIterator {
150 iter: self.previous_frame_messages.iter(),
151 }
152 }
153
154 /// Returns the number of messages in the current frame.
155 #[must_use]
156 pub const fn len_current(&self) -> usize {
157 self.current_messages.len()
158 }
159
160 /// Returns the number of messages in the previous frame.
161 #[must_use]
162 pub const fn len_previous(&self) -> usize {
163 self.previous_frame_messages.len()
164 }
165
166 /// Checks if there are no messages in the current frame.
167 #[must_use]
168 pub const fn is_empty_current(&self) -> bool {
169 self.current_messages.is_empty()
170 }
171
172 /// Checks if there are no messages in the previous frame.
173 #[must_use]
174 pub const fn is_empty_previous(&self) -> bool {
175 self.previous_frame_messages.is_empty()
176 }
177}
178
179/// Iterator over messages of a specific type.
180///
181/// This iterator yields references to messages, allowing for non-consuming traversal.
182pub struct MessagesIterator<'a, M: Message> {
183 iter: Iter<'a, MessageInfo<M>>,
184}
185
186impl<'a, M: Message> Iterator for MessagesIterator<'a, M> {
187 type Item = &'a M;
188
189 fn next(&mut self) -> Option<Self::Item> {
190 self.iter.next().map(|message_info| &message_info.message)
191 }
192}
193
194impl<M: Message> DoubleEndedIterator for MessagesIterator<'_, M> {
195 fn next_back(&mut self) -> Option<Self::Item> {
196 self.iter
197 .next_back()
198 .map(|message_info| &message_info.message)
199 }
200}
201
202/// Trait for type-erased message containers.
203///
204/// This allows for storing heterogeneous `Messages<M>` containers within a single collection.
205pub trait MessageContainer: Any + Send + Sync {
206 /// Swaps the current and previous frame message lists.
207 fn swap(&mut self);
208
209 /// Provides a reference to the container as `Any` for downcasting.
210 fn as_any(&self) -> &dyn Any;
211
212 /// Provides a mutable reference to the container as `Any` for downcasting.
213 fn as_any_mut(&mut self) -> &mut dyn Any;
214}
215
216impl<M: Message> MessageContainer for Messages<M> {
217 fn swap(&mut self) {
218 self.swap();
219 }
220
221 fn as_any(&self) -> &dyn Any {
222 self
223 }
224
225 fn as_any_mut(&mut self) -> &mut dyn Any {
226 self
227 }
228}
229
230/// Storage for all message types.
231///
232/// This struct maintains a registry mapping each message type to its corresponding `Messages<M>` container.
233#[derive(Default)]
234pub struct MessageStorage {
235 registry: HashMap<TypeId, Box<dyn MessageContainer>>,
236}
237
238impl Debug for MessageStorage {
239 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
240 write!(f, "MessageStorage")
241 }
242}
243
244impl MessageStorage {
245 /// Creates a new, empty `MessageStorage`.
246 #[must_use]
247 pub fn new() -> Self {
248 Self {
249 registry: HashMap::new(),
250 }
251 }
252
253 /// Registers a new message type within the storage.
254 ///
255 /// # Type Parameters
256 ///
257 /// * `M` - The message type to register.
258 ///
259 /// # Example
260 ///
261 /// ```rust
262 /// use limnus_message::prelude::*;
263 ///
264 /// #[derive(Message, Debug, Copy, Clone)]
265 /// struct MyMessage;
266 ///
267 /// let mut storage = MessageStorage::new();
268 ///
269 /// storage.register_message_type::<MyMessage>();
270 /// ```
271 pub fn register_message_type<M: Message>(&mut self) {
272 let type_id = TypeId::of::<M>();
273 self.registry.insert(
274 type_id,
275 Box::new(Messages::<M>::new()) as Box<dyn MessageContainer>,
276 );
277 }
278
279 /// Retrieves a mutable reference to the `Messages<M>` container for the specified message type.
280 ///
281 /// Returns `None` if the message type has not been registered.
282 ///
283 /// # Type Parameters
284 ///
285 /// * `M` - The message type to retrieve.
286 ///
287 /// # Example
288 ///
289 /// ```rust
290 /// use limnus_message::prelude::*;
291 ///
292 /// #[derive(Message, Debug, Copy, Clone)]
293 /// struct MyMessage;
294 ///
295 /// let mut storage = MessageStorage::new();
296 ///
297 /// if let Some(messages) = storage.get_mut::<MyMessage>() {
298 /// // Use `messages` here
299 /// }
300 /// ```
301 #[must_use]
302 pub fn get_mut<M: Message>(&mut self) -> Option<&mut Messages<M>> {
303 self.registry
304 .get_mut(&TypeId::of::<M>())
305 .and_then(|boxed| boxed.as_any_mut().downcast_mut::<Messages<M>>())
306 }
307
308 /// Retrieves an immutable reference to the `Messages<M>` container for the specified message type.
309 ///
310 /// Returns `None` if the message type has not been registered.
311 ///
312 /// # Type Parameters
313 ///
314 /// * `M` - The message type to retrieve.
315 ///
316 /// # Example
317 ///
318 /// ```rust
319 /// use limnus_message::prelude::*;
320 ///
321 /// #[derive(Message, Debug, Copy, Clone)]
322 /// struct MyMessage;
323 ///
324 /// let mut storage = MessageStorage::new();
325 ///
326 /// if let Some(messages) = storage.get::<MyMessage>() {
327 /// // Use `messages` here
328 /// }
329 /// ```
330 #[must_use]
331 pub fn get<M: Message>(&self) -> Option<&Messages<M>> {
332 self.registry
333 .get(&TypeId::of::<M>())
334 .and_then(|boxed| boxed.as_any().downcast_ref::<Messages<M>>())
335 }
336
337 /// Swaps the current and previous frame message lists for all registered message types.
338 ///
339 /// This should be called at the start of each new frame to transition messages appropriately.
340 ///
341 /// **Note:** The order in which message queues are swapped is not deterministic due to the nature of `HashMap`.
342 /// This is generally acceptable but should be considered if order matters.
343 pub fn swap_all(&mut self) {
344 for messages_any in &mut self.registry.values_mut() {
345 messages_any.swap();
346 }
347 }
348
349 /// Sends a message of a specific type.
350 ///
351 /// This method abstracts over the message type, automatically handling registration if necessary.
352 ///
353 /// # Type Parameters
354 ///
355 /// * `M` - The message type to send.
356 ///
357 /// # Arguments
358 ///
359 /// * `message` - The message to be sent.
360 ///
361 /// # Returns
362 ///
363 /// A `MessageId` uniquely identifying the sent message.
364 ///
365 /// # Example
366 ///
367 /// ```rust
368 /// use limnus_message::prelude::*;
369 ///
370 /// #[derive(Message, Debug, Copy, Clone)]
371 /// struct MyMessage;
372 ///
373 /// let mut storage = MessageStorage::new();
374 ///
375 /// let msg_id = storage.send(MyMessage { /* fields */ });
376 /// ```
377 #[allow(clippy::missing_panics_doc)]
378 pub fn send<M: Message>(&mut self, message: M) -> MessageId<M> {
379 // Ensure the message type is registered.
380 if !self.registry.contains_key(&TypeId::of::<M>()) {
381 self.register_message_type::<M>();
382 }
383
384 // It's safe to unwrap here because we just registered the type if it wasn't present.
385 self.get_mut::<M>()
386 .expect("Message type should be registered")
387 .send(message)
388 }
389
390 /// Iterates over all messages of a specific type in the current frame.
391 ///
392 /// # Type Parameters
393 ///
394 /// * `M` - The message type to iterate over.
395 ///
396 /// # Returns
397 ///
398 /// An iterator over references to messages of type `M` in the current frame.
399 ///
400 /// # Example
401 ///
402 /// ```rust
403 /// use limnus_message::prelude::*;
404 ///
405 /// #[derive(Message, Debug, Copy, Clone)]
406 /// struct MyMessage;
407 ///
408 /// let mut storage = MessageStorage::new();
409 ///
410 /// for message in storage.iter_current::<MyMessage>() {
411 /// // Process `message`
412 /// }
413 /// ```
414 #[must_use]
415 pub fn iter_current<M: Message>(&self) -> Option<MessagesIterator<'_, M>> {
416 self.get::<M>().map(|messages| messages.iter_current())
417 }
418
419 /// Iterates over all messages of a specific type in the previous frame.
420 ///
421 /// # Type Parameters
422 ///
423 /// * `M` - The message type to iterate over.
424 ///
425 /// # Returns
426 ///
427 /// An iterator over references to messages of type `M` in the previous frame.
428 ///
429 /// # Example
430 ///
431 /// ```rust
432 /// use limnus_message::prelude::*;
433 ///
434 /// #[derive(Message, Debug, Copy, Clone)]
435 /// struct MyMessage;
436 ///
437 /// let mut storage = MessageStorage::new();
438 ///
439 /// for message in storage.iter_previous::<MyMessage>() {
440 /// // Process `message`
441 /// }
442 /// ```
443 #[must_use]
444 pub fn iter_previous<M: Message>(&self) -> Option<MessagesIterator<'_, M>> {
445 self.get::<M>().map(|messages| messages.iter_previous())
446 }
447}