1#![no_std]
11#![warn(missing_docs)]
12#![allow(clippy::style)]
13
14#[cfg(feature = "alloc")]
15extern crate alloc;
16#[cfg(feature = "std")]
17extern crate std;
18
19use core::fmt;
20use core::panic::{Location, PanicInfo};
21
22pub trait Message: fmt::Display + fmt::Debug {}
24
25impl<T: fmt::Display + fmt::Debug> Message for T {}
26
27#[track_caller]
28#[inline(always)]
29pub fn panic_details<'a>(payload: &'a impl PanicInfoExt<'a>) -> PanicDetails<'a, impl Message + 'a> {
31 payload.panic_details()
32}
33
34#[inline(always)]
35pub fn panic_message<'a>(payload: &'a impl PanicInfoExt<'a>) -> impl Message + 'a {
37 payload.panic_message()
38}
39
40#[inline(always)]
41pub fn downcast_payload<'a>(payload: &'a (dyn core::any::Any + Send + 'static)) -> &'a dyn Message {
45 const DEFAULT_MESSAGE: &'static str = "panic occurred";
46 match payload.downcast_ref::<&'static str>() {
47 Some(message) => message,
48 #[cfg(feature = "alloc")]
49 None => match payload.downcast_ref::<alloc::string::String>() {
50 Some(message) => message,
51 None => &DEFAULT_MESSAGE,
52 },
53 #[cfg(not(feature = "alloc"))]
54 None => &DEFAULT_MESSAGE,
55 }
56}
57
58#[derive(Clone, Copy, Debug)]
59pub struct PanicDetails<'a, M: 'a> {
61 pub location: &'a Location<'a>,
68 pub message: M,
71}
72
73impl<M: Message> core::error::Error for PanicDetails<'_, M> {
74}
75
76impl<M: Message> fmt::Display for PanicDetails<'_, M> {
77 #[inline(always)]
78 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
79 fmt::Display::fmt(&self.location, fmt)?;
80 fmt.write_str(": ")?;
81 fmt::Display::fmt(&self.message, fmt)
82 }
83}
84
85pub trait PanicInfoExt<'a> {
87 fn panic_message(&'a self) -> impl Message + 'a;
89
90 #[track_caller]
91 #[inline(always)]
92 fn panic_details(&'a self) -> PanicDetails<'a, impl Message + 'a> {
97 PanicDetails {
98 location: Location::caller(),
99 message: self.panic_message(),
100 }
101 }
102}
103
104impl<'a> PanicInfoExt<'a> for PanicInfo<'a> {
105 #[inline(always)]
106 fn panic_message(&'a self) -> impl Message + 'a {
107 self.message()
108 }
109
110 #[track_caller]
111 #[inline(always)]
112 fn panic_details(&'a self) -> PanicDetails<'a, impl Message + 'a> {
113 let location = match self.location() {
114 Some(location) => location,
115 None => Location::caller(),
116 };
117 PanicDetails {
118 location,
119 message: self.panic_message()
120 }
121 }
122}
123
124impl<'a> PanicInfoExt<'a> for &'a (dyn core::any::Any + Send + 'static) {
125 #[inline(always)]
126 fn panic_message(&'a self) -> impl Message + 'a {
127 downcast_payload(*self)
128 }
129}
130
131#[cfg(feature = "alloc")]
132impl<'a> PanicInfoExt<'a> for alloc::boxed::Box<dyn core::any::Any + Send + 'static> {
133 #[inline(always)]
134 fn panic_message(&'a self) -> impl Message + 'a {
135 downcast_payload(self)
136 }
137}
138
139#[cfg(feature = "std")]
140impl<'a> PanicInfoExt<'a> for std::panic::PanicHookInfo<'a> {
141 #[inline(always)]
142 fn panic_message(&'a self) -> impl Message + 'a {
143 downcast_payload(self.payload())
144 }
145
146 #[track_caller]
147 #[inline(always)]
148 fn panic_details(&'a self) -> PanicDetails<'a, impl Message + 'a> {
149 let location = match self.location() {
150 Some(location) => location,
151 None => Location::caller(),
152 };
153 PanicDetails {
154 location,
155 message: self.panic_message()
156 }
157 }
158}