lazy_panic/
formatter.rs

1//! Formatter module for Panic related messages
2
3#[cfg(feature = "backtrace-on")]
4extern crate backtrace;
5
6use std::panic;
7use std::io;
8
9///Describes how to write panic's message prefix.
10///
11///Generally should be simple prefix that will go as `{Prefix}{PanicInfo}...`
12pub trait Prefix {
13    fn write_in<W: io::Write>(writer: &mut W) -> io::Result<()>;
14}
15
16///Describes how to write `PanicInfo`
17pub trait PanicInfo {
18    fn write_in<W: io::Write>(writer: &mut W, info: &panic::PanicInfo) -> io::Result<()>;
19}
20
21///Describes how to write panic's message suffix.
22///
23///Generally should be simple suffix that will go as `...{PanicInfo}{Suffix}`
24pub trait Suffix {
25    fn write_in<W: io::Write>(writer: &mut W) -> io::Result<()>;
26}
27
28///Describes how to write panic's backtrace
29pub trait Backtrace {
30    fn write_in<W: io::Write>(writer: &mut W) -> io::Result<()>;
31}
32
33///Noop configuration.
34///
35///Literally does nothing, use it when you want to omit the part
36///
37///As [PanicFormat](trait.PanicFormat.html) it writes nothing
38pub 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
68///Simple configuration that should be generic.
69///
70///For prefix it is constant string `Panic: `
71///
72///For `PanicInfo` it writes `{file}:{line} - {payload}`
73///
74///For suffix it is `\n`
75///
76///For backtrace it is noop
77///
78///As [PanicFormat](trait.PanicFormat.html) all above together.
79pub 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
113///Panic formatter
114///
115///Default print method writes each component in following order:
116///1. Backtrace
117///2. Prefix
118///3. `PanicInfo`
119///5. Suffix
120pub 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
168///Provides simple output with backtrace
169///
170///Note that if `backtrace-on` is disabled
171///then `Backtrace` is noop
172///
173///Note: Backtrace output is trimmed to only user's most recent call
174///So actual call stack may be longer
175pub 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        //First 3 frames are from backtrace.
190        //In middle 3 are from lazy_panic
191        //Last 2 are from Rust runtime
192        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        //By default backtrace includes last function call
197        //which means the above new()
198        //But we should really trim it down to user panic
199
200        //Code is based on backtrace source
201        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
245///Treats panic as just error
246///
247///Only panic's payload gets printed to stderr
248pub 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}