1use std::any::Any;
7use std::fmt;
8
9pub struct Message(Box<dyn Any + Send>);
27
28impl Message {
29 pub fn new<M: Any + Send + 'static>(msg: M) -> Self {
31 Self(Box::new(msg))
32 }
33
34 pub fn downcast<M: Any + Send + 'static>(self) -> Option<M> {
38 self.0.downcast::<M>().ok().map(|b| *b)
39 }
40
41 pub fn downcast_ref<M: Any + Send + 'static>(&self) -> Option<&M> {
43 self.0.downcast_ref::<M>()
44 }
45
46 pub fn is<M: Any + Send + 'static>(&self) -> bool {
48 self.0.is::<M>()
49 }
50}
51
52impl fmt::Debug for Message {
53 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
54 f.debug_struct("Message").finish_non_exhaustive()
55 }
56}
57
58#[derive(Debug, Clone, Copy, PartialEq, Eq)]
62pub struct QuitMsg;
63
64#[derive(Debug, Clone, Copy, PartialEq, Eq)]
66pub struct InterruptMsg;
67
68#[derive(Debug, Clone, Copy, PartialEq, Eq)]
70pub struct SuspendMsg;
71
72#[derive(Debug, Clone, Copy, PartialEq, Eq)]
74pub struct ResumeMsg;
75
76#[derive(Debug, Clone, Copy, PartialEq, Eq)]
78pub struct WindowSizeMsg {
79 pub width: u16,
81 pub height: u16,
83}
84
85#[derive(Debug, Clone, Copy, PartialEq, Eq)]
87pub struct FocusMsg;
88
89#[derive(Debug, Clone, Copy, PartialEq, Eq)]
91pub struct BlurMsg;
92
93#[derive(Debug, Clone, PartialEq, Eq)]
95pub(crate) struct SetWindowTitleMsg(pub String);
96
97#[derive(Debug, Clone, Copy, PartialEq, Eq)]
99pub(crate) struct RequestWindowSizeMsg;
100
101pub struct BatchMsg(pub Vec<super::Cmd>);
105
106pub struct SequenceMsg(pub Vec<super::Cmd>);
110
111#[derive(Debug, Clone, PartialEq, Eq)]
117pub(crate) struct PrintLineMsg(pub String);
118
119#[cfg(test)]
120mod tests {
121 use super::*;
122
123 #[test]
124 fn test_message_downcast() {
125 struct TestMsg(i32);
126
127 let msg = Message::new(TestMsg(42));
128 assert!(msg.is::<TestMsg>());
129 let inner = msg.downcast::<TestMsg>().unwrap();
130 assert_eq!(inner.0, 42);
131 }
132
133 #[test]
134 fn test_message_downcast_wrong_type() {
135 struct TestMsg1;
136 struct TestMsg2;
137
138 let msg = Message::new(TestMsg1);
139 assert!(!msg.is::<TestMsg2>());
140 assert!(msg.downcast::<TestMsg2>().is_none());
141 }
142
143 #[test]
144 fn test_quit_msg() {
145 let msg = Message::new(QuitMsg);
146 assert!(msg.is::<QuitMsg>());
147 }
148
149 #[test]
150 fn test_window_size_msg() {
151 let msg = WindowSizeMsg {
152 width: 80,
153 height: 24,
154 };
155 assert_eq!(msg.width, 80);
156 assert_eq!(msg.height, 24);
157 }
158
159 #[test]
164 fn test_message_downcast_ref_success() {
165 struct TestMsg(i32);
166
167 let msg = Message::new(TestMsg(42));
168 let inner_ref = msg.downcast_ref::<TestMsg>().unwrap();
170 assert_eq!(inner_ref.0, 42);
171
172 let inner_ref2 = msg.downcast_ref::<TestMsg>().unwrap();
174 assert_eq!(inner_ref2.0, 42);
175 }
176
177 #[test]
178 fn test_message_downcast_ref_wrong_type() {
179 struct TestMsg1(#[expect(dead_code)] i32);
180 struct TestMsg2;
181
182 let msg = Message::new(TestMsg1(42));
183 assert!(msg.downcast_ref::<TestMsg2>().is_none());
185 }
186
187 #[test]
188 fn test_message_is_without_consuming() {
189 struct TestMsg(i32);
190
191 let msg = Message::new(TestMsg(42));
192 assert!(msg.is::<TestMsg>());
194 assert!(msg.is::<TestMsg>());
196 assert_eq!(msg.downcast::<TestMsg>().unwrap().0, 42);
198 }
199
200 #[test]
201 fn test_message_debug_format() {
202 struct TestMsg;
203
204 let msg = Message::new(TestMsg);
205 let debug_str = format!("{:?}", msg);
206 assert!(debug_str.contains("Message"));
208 }
209
210 #[test]
211 fn test_interrupt_msg() {
212 let msg = Message::new(InterruptMsg);
213 assert!(msg.is::<InterruptMsg>());
214 assert!(msg.downcast::<InterruptMsg>().is_some());
216 }
217
218 #[test]
219 fn test_suspend_msg() {
220 let msg = Message::new(SuspendMsg);
221 assert!(msg.is::<SuspendMsg>());
222 }
223
224 #[test]
225 fn test_resume_msg() {
226 let msg = Message::new(ResumeMsg);
227 assert!(msg.is::<ResumeMsg>());
228 }
229
230 #[test]
231 fn test_focus_msg() {
232 let msg = Message::new(FocusMsg);
233 assert!(msg.is::<FocusMsg>());
234 }
235
236 #[test]
237 fn test_blur_msg() {
238 let msg = Message::new(BlurMsg);
239 assert!(msg.is::<BlurMsg>());
240 }
241
242 #[test]
243 fn test_window_size_msg_in_message() {
244 let size = WindowSizeMsg {
245 width: 120,
246 height: 40,
247 };
248 let msg = Message::new(size);
249
250 assert!(msg.is::<WindowSizeMsg>());
251
252 let size_ref = msg.downcast_ref::<WindowSizeMsg>().unwrap();
253 assert_eq!(size_ref.width, 120);
254 assert_eq!(size_ref.height, 40);
255 }
256
257 #[test]
258 fn test_message_with_string() {
259 let msg = Message::new(String::from("hello"));
260 assert!(msg.is::<String>());
261 assert_eq!(msg.downcast::<String>().unwrap(), "hello");
262 }
263
264 #[test]
265 fn test_message_with_vec() {
266 let msg = Message::new(vec![1, 2, 3]);
267 assert!(msg.is::<Vec<i32>>());
268 assert_eq!(msg.downcast::<Vec<i32>>().unwrap(), vec![1, 2, 3]);
269 }
270
271 #[test]
272 fn test_message_with_tuple() {
273 let msg = Message::new((1i32, "hello", 2.71f64));
274 assert!(msg.is::<(i32, &str, f64)>());
275
276 let (a, b, c) = msg.downcast::<(i32, &str, f64)>().unwrap();
277 assert_eq!(a, 1);
278 assert_eq!(b, "hello");
279 assert!((c - 2.71).abs() < f64::EPSILON);
280 }
281
282 #[test]
283 fn test_message_with_unit() {
284 let msg = Message::new(());
285 assert!(msg.is::<()>());
286 assert!(msg.downcast::<()>().is_some());
287 }
288
289 #[test]
290 fn test_builtin_msg_equality() {
291 assert_eq!(QuitMsg, QuitMsg);
293 assert_eq!(InterruptMsg, InterruptMsg);
294 assert_eq!(SuspendMsg, SuspendMsg);
295 assert_eq!(ResumeMsg, ResumeMsg);
296 assert_eq!(FocusMsg, FocusMsg);
297 assert_eq!(BlurMsg, BlurMsg);
298
299 let size1 = WindowSizeMsg {
300 width: 80,
301 height: 24,
302 };
303 let size2 = WindowSizeMsg {
304 width: 80,
305 height: 24,
306 };
307 let size3 = WindowSizeMsg {
308 width: 120,
309 height: 40,
310 };
311 assert_eq!(size1, size2);
312 assert_ne!(size1, size3);
313 }
314
315 #[test]
316 fn test_builtin_msg_clone() {
317 let quit = QuitMsg;
319 let quit_copy = quit;
320 assert_eq!(quit, quit_copy);
321
322 let size = WindowSizeMsg {
323 width: 80,
324 height: 24,
325 };
326 let size_copy = size;
327 assert_eq!(size, size_copy);
328 }
329
330 #[test]
331 fn test_builtin_msg_copy() {
332 let quit = QuitMsg;
334 let quit_copy = quit; assert_eq!(quit, quit_copy);
336
337 let size = WindowSizeMsg {
338 width: 80,
339 height: 24,
340 };
341 let size_copy = size; assert_eq!(size, size_copy);
343 }
344}