arcdps_imgui/drag_drop.rs
1//! Structs to create a Drag and Drop sequence. Almost all structs are re-exported
2//! and can be accessed from the crate root; some additional utilities can be found in here.
3//!
4//! A DragDrop is a UI mechanism where users can appear to "drag"
5//! some data from one [source](DragDropSource) to one [target](DragDropTarget).
6//! A source and a target must both have some `name` identifier, which is declared when they
7//! are created. If these names are equal, then a `payload` of some kind
8//! will be given to the target caller whne the user releases their mouse button over
9//! the target (additionally, the UI will reflect that the payload *can* be deposited
10//! in the target).
11//!
12//! The complexity of this implementation is primarily in managing this payload. Users
13//! can provide three different kinds of payloads:
14//!
15//! 1. Users can give an [empty payload](DragDropPayloadEmpty) with [begin](DragDropSource::begin).
16//! This payload type is essentially just a notification system, but using some shared state,
17//! this can be reasonably powerful, and is the safest way to transfer non-Copy data offered
18//! right now.
19//! 2. Users can give a [simple Copy payload](DragDropPayloadPod) with [begin](DragDropSource::begin_payload).
20//! This allows users to copy data to Dear ImGui, which will take ownership over it, and then be given
21//! it back to the Target. Please note: users are of course free to not drop any drag (cancel a drag),
22//! so this data could easily be lost forever. Our `'static + Copy` bound is intended to keep users
23//! to simplistic types.
24//! 3. An unsafe implementation is provided which allows for any data to be unsafely copied. Note that once
25//! you use this method, the safe implementations in #1 and #2 can create memory unsafety problems; notably,
26//! they both assume that a payload has certain header information within it.
27//!
28//! For examples of each payload type, see [DragDropSource].
29use std::{any, ffi, marker::PhantomData};
30
31use crate::{sys, Condition, Ui};
32use bitflags::bitflags;
33
34bitflags!(
35 /// Flags for igBeginDragDropSource(), igAcceptDragDropPayload()
36 #[repr(transparent)]
37 pub struct DragDropFlags: u32 {
38 /// By default, a successful call to igBeginDragDropSource opens a tooltip so you can
39 /// display a preview or description of the source contents. This flag disable this
40 /// behavior.
41 const SOURCE_NO_PREVIEW_TOOLTIP = sys::ImGuiDragDropFlags_SourceNoPreviewTooltip;
42 /// By default, when dragging we clear data so that igIsItemHovered() will return false, to
43 /// avoid subsequent user code submitting tooltips. This flag disable this behavior so you
44 /// can still call igIsItemHovered() on the source item.
45 const SOURCE_NO_DISABLE_HOVER = sys::ImGuiDragDropFlags_SourceNoDisableHover;
46 /// Disable the behavior that allows to open tree nodes and collapsing header by holding
47 /// over them while dragging a source item.
48 const SOURCE_NO_HOLD_TO_OPEN_OTHERS = sys::ImGuiDragDropFlags_SourceNoHoldToOpenOthers;
49 /// Allow items such as igText(), igImage() that have no unique identifier to be used as
50 /// drag source, by manufacturing a temporary identifier based on their window-relative
51 /// position. This is extremely unusual within the dear imgui ecosystem and so we made it
52 /// explicit.
53 const SOURCE_ALLOW_NULL_ID = sys::ImGuiDragDropFlags_SourceAllowNullID;
54 /// External source (from outside of imgui), won't attempt to read current item/window
55 /// info. Will always return true. Only one Extern source can be active simultaneously.
56 const SOURCE_EXTERN = sys::ImGuiDragDropFlags_SourceExtern;
57 /// Automatically expire the payload if the source ceases to be submitted (otherwise
58 /// payloads are persisting while being dragged)
59 const SOURCE_AUTO_EXPIRE_PAYLOAD = sys::ImGuiDragDropFlags_SourceAutoExpirePayload;
60 /// igAcceptDragDropPayload() will returns true even before the mouse button is released.
61 /// You can then call igIsDelivery() to test if the payload needs to be delivered.
62 const ACCEPT_BEFORE_DELIVERY = sys::ImGuiDragDropFlags_AcceptBeforeDelivery;
63 /// Do not draw the default highlight rectangle when hovering over target.
64 const ACCEPT_NO_DRAW_DEFAULT_RECT = sys::ImGuiDragDropFlags_AcceptNoDrawDefaultRect;
65 /// Request hiding the igBeginDragDropSource tooltip from the igBeginDragDropTarget site.
66 const ACCEPT_NO_PREVIEW_TOOLTIP = sys::ImGuiDragDropFlags_AcceptNoPreviewTooltip;
67 /// For peeking ahead and inspecting the payload before delivery. This is just a convenience
68 /// flag for the intersection of `ACCEPT_BEFORE_DELIVERY` and `ACCEPT_NO_DRAW_DEFAULT_RECT`
69 const ACCEPT_PEEK_ONLY = sys::ImGuiDragDropFlags_AcceptPeekOnly;
70 }
71);
72
73/// Creates a source for drag drop data out of the last ID created.
74///
75/// ```no_run
76/// # use arcdps_imgui::*;
77/// fn show_ui(ui: &Ui<'_>) {
78/// ui.button("Hello, I am a drag source!");
79///
80/// // Creates an empty DragSource with no tooltip
81/// DragDropSource::new("BUTTON_DRAG").begin(ui);
82/// }
83/// ```
84///
85/// Notice especially the `"BUTTON_DRAG"` name -- this is the identifier of this
86/// DragDropSource; [DragDropTarget]'s will specify an identifier to *receive*, and these
87/// names must match up. A single item should only have one [DragDropSource], though
88/// a target may have multiple different targets.
89///
90/// DropDropSources don't do anything until you use one of the three `begin_` methods
91/// on this struct. Each of these methods describes how you handle the Payload which ImGui
92/// will manage, and then give to a [DragDropTarget], which will received the payload. The
93/// simplest and safest Payload is the empty payload, created with [begin](Self::begin).
94#[derive(Debug)]
95pub struct DragDropSource<T> {
96 name: T,
97 flags: DragDropFlags,
98 cond: Condition,
99}
100
101impl<T: AsRef<str>> DragDropSource<T> {
102 /// Creates a new [DragDropSource] with no flags and the `Condition::Always` with the given name.
103 /// ImGui refers to this `name` field as a `type`, but really it's just an identifier to match up
104 /// Source/Target for DragDrop.
105 pub fn new(name: T) -> Self {
106 Self {
107 name,
108 flags: DragDropFlags::empty(),
109 cond: Condition::Always,
110 }
111 }
112
113 /// Sets the flags on the [DragDropSource]. Only the flags `SOURCE_NO_PREVIEW_TOOLTIP`,
114 /// `SOURCE_NO_DISABLE_HOVER`, `SOURCE_NO_HOLD_TO_OPEN_OTHERS`, `SOURCE_ALLOW_NULL_ID`,
115 /// `SOURCE_EXTERN`, `SOURCE_AUTO_EXPIRE_PAYLOAD` make semantic sense, but any other flags will
116 /// be accepted without panic.
117 #[inline]
118 pub fn flags(mut self, flags: DragDropFlags) -> Self {
119 self.flags = flags;
120 self
121 }
122
123 /// Sets the condition on the [DragDropSource]. Defaults to [Always](Condition::Always).
124 #[inline]
125 pub fn condition(mut self, cond: Condition) -> Self {
126 self.cond = cond;
127 self
128 }
129
130 /// Creates the source of a drag and returns a handle on the tooltip.
131 /// This handle can be immediately dropped without binding it, in which case a default empty
132 /// circle will be used for the "blank" tooltip as this item is being dragged around.
133 ///
134 /// Otherwise, use this tooltip to add data which will display as this item is dragged.
135 /// If `SOURCE_NO_PREVIEW_TOOLTIP` is enabled, however, no preview will be displayed
136 /// and this returned token does nothing. Additionally, a given target may use the flag
137 /// `ACCEPT_NO_PREVIEW_TOOLTIP`, which will also prevent this tooltip from being shown.
138 ///
139 /// This drag has no payload, but is still probably the most useful way in imgui-rs to handle payloads.
140 /// Using `once_cell` or some shared data, this pattern can be very powerful:
141 ///
142 /// ```no_run
143 /// # use arcdps_imgui::*;
144 /// fn show_ui(ui: &Ui<'_>, drop_message: &mut Option<String>) {
145 /// ui.button("Drag me!");
146 ///
147 /// let drag_drop_name = "Test Drag";
148 ///
149 /// // drag drop SOURCE
150 /// if DragDropSource::new(drag_drop_name).begin(ui).is_some() {
151 /// // warning -- this would allocate every frame if `DragDropSource` has
152 /// // condition `Always`, which it does by default. We're okay with that for
153 /// // this example, but real code probably wouldn't want to allocate so much.
154 /// *drop_message = Some("Test Payload".to_string());
155 /// }
156 ///
157 /// ui.button("Target me!");
158 ///
159 /// // drag drop TARGET
160 /// if let Some(target) = arcdps_imgui::DragDropTarget::new(ui) {
161 /// if target
162 /// .accept_payload_empty(drag_drop_name, DragDropFlags::empty())
163 /// .is_some()
164 /// {
165 /// let msg = drop_message.take().unwrap();
166 /// assert_eq!(msg, "Test Payload");
167 /// }
168 ///
169 /// target.pop();
170 /// }
171 /// }
172 /// ```
173 ///
174 /// In the above, you'll see how the payload is really just a message passing service.
175 /// If you want to pass a simple integer or other "plain old data", take a look at
176 /// [begin_payload](Self::begin_payload).
177 #[inline]
178 pub fn begin<'ui>(self, ui: &Ui<'ui>) -> Option<DragDropSourceToolTip<'ui>> {
179 self.begin_payload(ui, ())
180 }
181
182 /// Creates the source of a drag and returns a handle on the tooltip.
183 /// This handle can be immediately dropped without binding it, in which case a default empty
184 /// circle will be used for the "blank" tooltip as this item is being dragged around.
185 ///
186 /// Otherwise, use this tooltip to add data which will display as this item is dragged.
187 /// If `SOURCE_NO_PREVIEW_TOOLTIP` is enabled, however, no preview will be displayed
188 /// and this returned token does nothing. Additionally, a given target may use the flag
189 /// `ACCEPT_NO_PREVIEW_TOOLTIP`, which will also prevent this tooltip from being shown.
190 ///
191 /// This function also takes a payload in the form of `T: Copy + 'static`. ImGui will
192 /// memcpy this data immediately to an internally held buffer, and will return the data
193 /// to [DragDropTarget].
194 ///
195 /// ```no_run
196 /// # use arcdps_imgui::*;
197 /// fn show_ui(ui: &Ui<'_>) {
198 /// ui.button("Drag me!");
199 ///
200 /// let drag_drop_name = "Test Drag";
201 /// let msg_to_send = "hello there sailor";
202 ///
203 /// // drag drop SOURCE
204 /// if let Some(tooltip) = DragDropSource::new(drag_drop_name).begin_payload(ui, msg_to_send) {
205 /// ui.text("Sending message!");
206 /// tooltip.end();
207 /// }
208 ///
209 /// ui.button("Target me!");
210 ///
211 /// // drag drop TARGET
212 /// if let Some(target) = arcdps_imgui::DragDropTarget::new(ui) {
213 /// if let Some(Ok(payload_data)) = target
214 /// .accept_payload::<&'static str, _>(drag_drop_name, DragDropFlags::empty())
215 /// {
216 /// let msg = payload_data.data;
217 /// assert_eq!(msg, msg_to_send);
218 /// }
219 ///
220 /// target.pop();
221 /// }
222 /// }
223 /// ```
224 #[inline]
225 pub fn begin_payload<'ui, P: Copy + 'static>(
226 self,
227 ui: &Ui<'ui>,
228 payload: P,
229 ) -> Option<DragDropSourceToolTip<'ui>> {
230 unsafe {
231 let payload = TypedPayload::new(payload);
232 self.begin_payload_unchecked(
233 ui,
234 &payload as *const _ as *const ffi::c_void,
235 std::mem::size_of::<TypedPayload<P>>(),
236 )
237 }
238 }
239
240 /// Creates the source of a drag and returns a handle on the tooltip.
241 /// This handle can be immediately dropped without binding it, in which case a default empty
242 /// circle will be used for the "blank" tooltip as this item is being dragged around.
243 ///
244 /// Otherwise, use this tooltip to add data which will display as this item is dragged.
245 /// If `SOURCE_NO_PREVIEW_TOOLTIP` is enabled, however, no preview will be displayed
246 /// and this returned token does nothing. Additionally, a given target may use the flag
247 /// `ACCEPT_NO_PREVIEW_TOOLTIP`, which will also prevent this tooltip from being shown.
248 ///
249 /// This function also takes a payload of any `*const T`. ImGui will
250 /// memcpy this data immediately to an internally held buffer, and will return the data
251 /// to [DragDropTarget].
252 ///
253 /// ## Safety
254 /// This function itself will not cause a panic, but using it directly opts you into
255 /// managing the lifetime of the pointer provided yourself. Dear ImGui will execute a memcpy on
256 /// the data passed in with the size (in bytes) given, but this is, of course, just a copy,
257 /// so if you pass in an `&String`, for example, the underlying String data will not be cloned,
258 /// and could easily dangle if the `String` is dropped.
259 ///
260 /// Moreover, if `Condition::Always` is set (as it is by default), you will be copying in your data
261 /// every time this function is ran in your update loop, which if it involves an allocating and then
262 /// handing the allocation to ImGui, would result in a significant amount of data created.
263 ///
264 /// Overall, users should be very sure that this function is needed before they reach for it, and instead
265 /// should consider either [begin](Self::begin) or [begin_payload](Self::begin_payload).
266 #[inline]
267 pub unsafe fn begin_payload_unchecked<'ui>(
268 &self,
269 ui: &Ui<'ui>,
270 ptr: *const ffi::c_void,
271 size: usize,
272 ) -> Option<DragDropSourceToolTip<'ui>> {
273 let should_begin = sys::igBeginDragDropSource(self.flags.bits() as i32);
274
275 if should_begin {
276 sys::igSetDragDropPayload(ui.scratch_txt(&self.name), ptr, size, self.cond as i32);
277
278 Some(DragDropSourceToolTip::push())
279 } else {
280 None
281 }
282 }
283}
284
285/// A helper struct for RAII drap-drop support.
286pub struct DragDropSourceToolTip<'ui>(PhantomData<Ui<'ui>>);
287
288impl DragDropSourceToolTip<'_> {
289 /// Creates a new tooltip internally.
290 #[inline]
291 fn push() -> Self {
292 Self(PhantomData)
293 }
294
295 /// Ends the tooltip directly. You could choose to simply allow this to drop
296 /// by not calling this, which will also be fine.
297 #[inline]
298 pub fn end(self) {
299 // left empty to invoke drop...
300 }
301}
302
303impl Drop for DragDropSourceToolTip<'_> {
304 fn drop(&mut self) {
305 unsafe { sys::igEndDragDropSource() }
306 }
307}
308
309/// Creates a target for drag drop data out of the last ID created.
310///
311/// ```no_run
312/// # use arcdps_imgui::*;
313/// fn show_ui(ui: &Ui<'_>) {
314/// // Drop something on this button please!
315/// ui.button("Hello, I am a drag Target!");
316///
317/// if let Some(target) = DragDropTarget::new(ui) {
318/// // accepting an empty payload (which is really just raising an event)
319/// if let Some(_payload_data) = target.accept_payload_empty("BUTTON_DRAG", DragDropFlags::empty()) {
320/// println!("Nice job getting on the payload!");
321/// }
322///
323/// // and we can accept multiple, different types of payloads with one drop target.
324/// // this is a good pattern for handling different kinds of drag/drop situations with
325/// // different kinds of data in them.
326/// if let Some(Ok(payload_data)) = target.accept_payload::<usize, _>("BUTTON_ID", DragDropFlags::empty()) {
327/// println!("Our payload's data was {}", payload_data.data);
328/// }
329/// }
330/// }
331/// ```
332///
333/// Notice especially the `"BUTTON_DRAG"` and `"BUTTON_ID"` name -- this is the identifier of this
334/// DragDropTarget; [DragDropSource]s will specify an identifier when they send a payload, and these
335/// names must match up. Notice how a target can have multiple acceptances on them -- this is a good
336/// pattern to handle multiple kinds of data which could be passed around.
337///
338/// DropDropTargets don't do anything until you use one of the three `accept_` methods
339/// on this struct. Each of these methods will spit out a _Payload struct with an increasing
340/// amount of information on the Payload. The absolute safest solution is [accept_payload_empty](Self::accept_payload_empty).
341#[derive(Debug)]
342pub struct DragDropTarget<'ui>(&'ui Ui<'ui>);
343
344impl<'ui> DragDropTarget<'ui> {
345 /// Creates a new DragDropTarget, holding the [Ui]'s lifetime for the duration
346 /// of its existence. This is required since this struct runs some code on its Drop
347 /// to end the DragDropTarget code.
348 #[doc(alias = "BeginDragDropTarget")]
349 pub fn new(ui: &'ui Ui<'ui>) -> Option<Self> {
350 let should_begin = unsafe { sys::igBeginDragDropTarget() };
351 if should_begin {
352 Some(Self(ui))
353 } else {
354 None
355 }
356 }
357
358 /// Accepts an empty payload. This is the safest option for raising named events
359 /// in the DragDrop API. See [DragDropSource::begin] for more information on how you
360 /// might use this pattern.
361 ///
362 /// Note: If you began this operation with `begin_payload_unchecked` it always incorrect
363 /// to use this function. Use `accept_payload_unchecked` instead
364 pub fn accept_payload_empty(
365 &self,
366 name: impl AsRef<str>,
367 flags: DragDropFlags,
368 ) -> Option<DragDropPayloadEmpty> {
369 self.accept_payload(name, flags)?
370 .ok()
371 .map(|payload_pod: DragDropPayloadPod<()>| DragDropPayloadEmpty {
372 preview: payload_pod.preview,
373 delivery: payload_pod.delivery,
374 })
375 }
376
377 /// Accepts a payload with plain old data in it. This returns a Result, since you can specify any
378 /// type. The sent type must match the return type (via TypeId) to receive an `Ok`.
379 ///
380 /// Note: If you began this operation with `begin_payload_unchecked` it always incorrect
381 /// to use this function. Use `accept_payload_unchecked` instead
382 pub fn accept_payload<T: 'static + Copy, Name: AsRef<str>>(
383 &self,
384 name: Name,
385 flags: DragDropFlags,
386 ) -> Option<Result<DragDropPayloadPod<T>, PayloadIsWrongType>> {
387 let output = unsafe { self.accept_payload_unchecked(name, flags) };
388
389 // convert the unsafe payload to our Result
390 output.map(|unsafe_payload| {
391 // sheering off the typeid...
392 let received =
393 unsafe { (unsafe_payload.data as *const TypedPayloadHeader).read_unaligned() };
394 let expected = any::TypeId::of::<T>();
395
396 if received.type_id == expected {
397 let data =
398 unsafe { (unsafe_payload.data as *const TypedPayload<T>).read_unaligned() }
399 .data;
400 Ok(DragDropPayloadPod {
401 data,
402 preview: unsafe_payload.preview,
403 delivery: unsafe_payload.delivery,
404 })
405 } else {
406 Err(PayloadIsWrongType {
407 received,
408 expected: TypedPayloadHeader::new::<T>(),
409 })
410 }
411 })
412 }
413
414 /// Accepts a drag and drop payload which contains a raw pointer to [c_void](std::ffi::c_void)
415 /// and a size in bytes. Users should generally avoid using this function
416 /// if one of the safer variants is acceptable.
417 ///
418 /// ## Safety
419 ///
420 /// Because this pointer comes from ImGui, absolutely no promises can be made on its
421 /// contents, alignment, or lifetime. Interacting with it is therefore extremely unsafe.
422 /// **Important:** a special note needs to be made to the [ACCEPT_BEFORE_DELIVERY](DragDropFlags::ACCEPT_BEFORE_DELIVERY) flag --
423 /// passing this flag will make this function return `Some(DragDropPayload)` **even before
424 /// the user has actually "dropped" the payload by release their mouse button.**
425 ///
426 /// In safe functions, this works just fine, since the data can be freely copied
427 /// (or doesn't exist at all!). However, if you are working with your own data, you must
428 /// be extremely careful with this data, as you may, effectively, only have immutable access to it.
429 ///
430 /// Moreover, if the `DragDropSource` has also used `Condition::Once` or similar when they sent the data,
431 /// ImGui will assume its data is still valid even after your preview, so corrupting that data could
432 /// lead to all sorts of unsafe behvaior on ImGui's side. In summary, using this function for any data
433 /// which isn't truly `Copy` or "plain old data" is difficult, and requires substantial knowledge
434 /// of the various edge cases.
435 pub unsafe fn accept_payload_unchecked(
436 &self,
437 name: impl AsRef<str>,
438 flags: DragDropFlags,
439 ) -> Option<DragDropPayload> {
440 let inner = sys::igAcceptDragDropPayload(self.0.scratch_txt(name), flags.bits() as i32);
441 if inner.is_null() {
442 None
443 } else {
444 let inner = *inner;
445
446 // @fixme: there are actually other fields on `inner` which I have shorn -- they're
447 // considered internal to imgui (such as id of who sent this), so i've left it for
448 // now this way.
449 Some(DragDropPayload {
450 data: inner.Data,
451 size: inner.DataSize as usize,
452 preview: inner.Preview,
453 delivery: inner.Delivery,
454 })
455 }
456 }
457
458 /// Ends the current target. Ironically, this doesn't really do anything in ImGui
459 /// or in imgui-rs, but it might in the future.
460 pub fn pop(self) {
461 // omitted...exists just to run Drop.
462 }
463}
464
465impl Drop for DragDropTarget<'_> {
466 fn drop(&mut self) {
467 unsafe { sys::igEndDragDropTarget() }
468 }
469}
470
471/// An empty DragDropPayload. It has no data in it, and just includes
472/// two bools with status information.
473#[derive(Debug, Clone, Copy)]
474#[non_exhaustive]
475pub struct DragDropPayloadEmpty {
476 /// Set when [`accept_payload_empty`](DragDropTarget::accept_payload_empty) was called
477 /// and mouse has been hovering the target item.
478 pub preview: bool,
479
480 /// Set when [`accept_payload_empty`](DragDropTarget::accept_payload_empty) was
481 /// called and mouse button is released over the target item.
482 pub delivery: bool,
483}
484
485/// A DragDropPayload with status information and some POD, or plain old data,
486/// in it.
487#[derive(Debug, Copy, Clone)]
488#[non_exhaustive]
489pub struct DragDropPayloadPod<T: 'static + Copy> {
490 /// The kind data which was requested.
491 pub data: T,
492
493 /// Set when [`accept_payload`](DragDropTarget::accept_payload) was called
494 /// and mouse has been hovering the target item.
495 pub preview: bool,
496
497 /// Set when [`accept_payload`](DragDropTarget::accept_payload) was
498 /// called and mouse button is released over the target item.
499 pub delivery: bool,
500}
501
502#[derive(Debug)]
503#[non_exhaustive]
504pub struct DragDropPayload {
505 /// Data which is copied and owned by ImGui. If you have accepted the payload, you can
506 /// take ownership of the data; otherwise, view it immutably. Interacting with `data` is
507 /// very unsafe.
508 pub data: *const ffi::c_void,
509
510 /// The size of the data in bytes.
511 pub size: usize,
512
513 /// Set when [`accept_payload_unchecked`](DragDropTarget::accept_payload_unchecked) was called
514 /// and mouse has been hovering the target item.
515 pub preview: bool,
516
517 /// Set when [`accept_payload_unchecked`](DragDropTarget::accept_payload_unchecked) was
518 /// called and mouse button is released over the target item. If this is set to false, then you
519 /// set DragDropFlags::ACCEPT_BEFORE_DELIVERY and shouldn't mutate `data`.
520 pub delivery: bool,
521}
522
523/// A typed payload.
524#[derive(Debug, Clone, Copy)]
525#[repr(C)]
526struct TypedPayload<T> {
527 header: TypedPayloadHeader,
528 data: T,
529}
530
531impl<T: Copy + 'static> TypedPayload<T> {
532 /// Creates a new typed payload which contains this data.
533 fn new(data: T) -> Self {
534 Self {
535 header: TypedPayloadHeader::new::<T>(),
536 data,
537 }
538 }
539}
540
541/// A header for a typed payload.
542#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd)]
543#[repr(C)]
544struct TypedPayloadHeader {
545 type_id: any::TypeId,
546 #[cfg(debug_assertions)]
547 type_name: &'static str,
548}
549
550impl TypedPayloadHeader {
551 #[cfg(debug_assertions)]
552 fn new<T: 'static>() -> Self {
553 Self {
554 type_id: any::TypeId::of::<T>(),
555 type_name: any::type_name::<T>(),
556 }
557 }
558
559 #[cfg(not(debug_assertions))]
560 fn new<T: 'static>() -> Self {
561 Self {
562 type_id: any::TypeId::of::<T>(),
563 }
564 }
565}
566
567/// Indicates that an incorrect payload type was received. It is opaque,
568/// but you can view useful information with Debug formatting when
569/// `debug_assertions` are enabled.
570#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd)]
571pub struct PayloadIsWrongType {
572 expected: TypedPayloadHeader,
573 received: TypedPayloadHeader,
574}
575
576#[cfg(debug_assertions)]
577impl std::fmt::Display for PayloadIsWrongType {
578 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
579 write!(
580 f,
581 "Payload is {} -- expected {}",
582 self.received.type_name, self.expected.type_name
583 )
584 }
585}
586
587#[cfg(not(debug_assertions))]
588impl std::fmt::Display for PayloadIsWrongType {
589 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
590 f.pad("Payload is wrong type")
591 }
592}
593
594impl std::error::Error for PayloadIsWrongType {}