1#[cfg(feature = "backtrace-on")]
4extern crate backtrace;
5
6use std::panic;
7use std::io;
8
9pub trait Prefix {
13 fn write_in<W: io::Write>(writer: &mut W) -> io::Result<()>;
14}
15
16pub trait PanicInfo {
18 fn write_in<W: io::Write>(writer: &mut W, info: &panic::PanicInfo) -> io::Result<()>;
19}
20
21pub trait Suffix {
25 fn write_in<W: io::Write>(writer: &mut W) -> io::Result<()>;
26}
27
28pub trait Backtrace {
30 fn write_in<W: io::Write>(writer: &mut W) -> io::Result<()>;
31}
32
33pub struct Empty;
39
40impl Prefix for Empty {
41 #[inline]
42 fn write_in<W: io::Write>(_: &mut W) -> io::Result<()> {
43 Ok(())
44 }
45}
46
47impl PanicInfo for Empty {
48 #[inline]
49 fn write_in<W: io::Write>(_: &mut W, _: &panic::PanicInfo) -> io::Result<()> {
50 Ok(())
51 }
52}
53
54impl Suffix for Empty {
55 #[inline]
56 fn write_in<W: io::Write>(_: &mut W) -> io::Result<()> {
57 Ok(())
58 }
59}
60
61impl Backtrace for Empty {
62 #[inline]
63 fn write_in<W: io::Write>(_: &mut W) -> io::Result<()> {
64 Ok(())
65 }
66}
67
68pub struct Simple;
80
81impl Prefix for Simple {
82 #[inline]
83 fn write_in<W: io::Write>(writer: &mut W) -> io::Result<()> {
84 writer.write_all("Panic: ".as_bytes())
85 }
86}
87
88impl PanicInfo for Simple {
89 #[inline]
90 fn write_in<W: io::Write>(writer: &mut W, info: &panic::PanicInfo) -> io::Result<()> {
91 match info.location() {
92 Some(location) => write!(writer, "{}:{} - ", location.file(), location.line()),
93 None => write!(writer, "unknown:0 - ")
94 }?;
95 write_payload!(writer, info.payload(), types: [&str, String])
96 }
97}
98
99impl Suffix for Simple {
100 #[inline]
101 fn write_in<W: io::Write>(writer: &mut W) -> io::Result<()> {
102 write!(writer, "\n")
103 }
104}
105
106impl Backtrace for Simple {
107 #[inline]
108 fn write_in<W: io::Write>(_: &mut W) -> io::Result<()> {
109 Ok(())
110 }
111}
112
113pub trait PanicFormat {
121 type Writer: io::Write;
122 type Backtrace: Backtrace;
123 type Prefix: Prefix;
124 type PanicInfo: PanicInfo;
125 type Suffix: Suffix;
126
127 fn writer() -> Self::Writer;
128
129 fn print(info: &panic::PanicInfo) {
130 let mut writer = Self::writer();
131
132 let _ = Self::Backtrace::write_in(&mut writer);
133 let _ = Self::Prefix::write_in(&mut writer);
134 let _ = Self::PanicInfo::write_in(&mut writer, info);
135 let _ = Self::Suffix::write_in(&mut writer);
136 }
137}
138
139impl PanicFormat for Simple {
140 type Writer = io::BufWriter<io::Stderr>;
141 type Backtrace = Self;
142 type Prefix = Self;
143 type PanicInfo = Self;
144 type Suffix = Self;
145
146 fn writer() -> Self::Writer {
147 let stderr = io::stderr();
148 io::BufWriter::new(stderr)
149 }
150}
151
152impl PanicFormat for Empty {
153 type Writer = io::Stderr;
154 type Backtrace = Self;
155 type Prefix = Self;
156 type PanicInfo = Self;
157 type Suffix = Self;
158
159 fn writer() -> Self::Writer {
160 io::stderr()
161 }
162
163 fn print(_: &panic::PanicInfo) {
164 }
165}
166
167
168pub struct Debug;
176
177impl Backtrace for Debug {
178 #[cfg(not(feature = "backtrace-on"))]
179 #[inline]
180 fn write_in<W: io::Write>(_: &mut W) -> io::Result<()> {
181 Ok(())
182 }
183
184 #[cfg(feature = "backtrace-on")]
185 #[inline]
186 fn write_in<W: io::Write>(writer: &mut W) -> io::Result<()> {
187 use std::mem;
188
189 const TRASH_FRAMES_NUM: usize = 8;
193 const HEX_WIDTH: usize = mem::size_of::<usize>() + 2;
194
195 let backtrace = self::backtrace::Backtrace::new();
196 write!(writer, "Stack backtrace:")?;
202 for (idx, frame) in backtrace.frames().iter().skip(TRASH_FRAMES_NUM).enumerate() {
203 let ip = frame.ip();
204 write!(writer, "\n{:4}: {:2$?}", idx, ip, HEX_WIDTH)?;
205
206 let symbols = frame.symbols();
207 if symbols.len() == 0 {
208 write!(writer, " - <unresolved>")?;
209 }
210
211 for (idx, symbol) in symbols.iter().enumerate() {
212 if idx != 0 {
213 write!(writer, "\n {:1$}", "", HEX_WIDTH)?;
214 }
215
216 if let Some(name) = symbol.name() {
217 write!(writer, " - {}", name)?;
218 } else {
219 write!(writer, " - <unknown>")?;
220 }
221
222 if let (Some(file), Some(line)) = (symbol.filename(), symbol.lineno()) {
223 write!(writer, "\n {:3$}at {}:{}", "", file.display(), line, HEX_WIDTH)?;
224 }
225 }
226 }
227
228 write!(writer, "\n")
229 }
230}
231
232impl PanicFormat for Debug {
233 type Writer = io::BufWriter<io::Stderr>;
234 type Prefix = Simple;
235 type PanicInfo = Simple;
236 type Suffix = Simple;
237 type Backtrace = Self;
238
239 fn writer() -> Self::Writer {
240 let stderr = io::stderr();
241 io::BufWriter::new(stderr)
242 }
243}
244
245pub struct JustError;
249
250impl PanicInfo for JustError {
251 #[inline]
252 fn write_in<W: io::Write>(writer: &mut W, info: &panic::PanicInfo) -> io::Result<()> {
253 write_payload!(writer, info.payload(), types: [&str, String])
254 }
255}
256
257impl PanicFormat for JustError {
258 type Writer = io::BufWriter<io::Stderr>;
259 type Prefix = Empty;
260 type PanicInfo = Self;
261 type Suffix = Simple;
262 type Backtrace = Empty;
263
264 fn writer() -> Self::Writer {
265 let stderr = io::stderr();
266 io::BufWriter::new(stderr)
267 }
268}
269
270#[cfg(test)]
271mod tests {
272 use super::{Simple, Empty, Debug, JustError};
273
274 #[test]
275 #[should_panic]
276 fn should_simple_panic() {
277 set_panic_message!(Simple);
278 panic!("lolka");
279 }
280
281 #[test]
282 #[should_panic]
283 fn should_empty_panic() {
284 set_panic_message!(Empty);
285 panic!("lolka");
286 }
287
288 #[test]
289 #[should_panic]
290 fn should_debug_panic() {
291 set_panic_message!(Debug);
292 panic!("lolka");
293 }
294
295 #[test]
296 #[should_panic]
297 fn should_just_error_panic() {
298 set_panic_message!(JustError);
299 panic!("lolka");
300 }
301
302}