1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
use std::backtrace;
use std::borrow::Cow;
/// A type raising a panic when dropped.
///
/// Call [`PanicOnDrop::defuse`] to prevent the panic.
#[derive(Debug)]
pub struct PanicOnDrop {
/// Name of the original resource that encloses this type.
resource_name: Cow<'static, str>,
/// Descriptive error, telling the user why the enclosing type should not have been dropped.
error_msg: Cow<'static, str>,
/// Descriptive message, telling the user what can, should or must be done to prevent the panic.
help_msg: Cow<'static, str>,
/// Internal flag to control the panic raise.
armed: bool,
}
impl PanicOnDrop {
/// Creates a new `PanicOnDrop` instance, raising a panic when dropped.
///
/// This panic can only be prevented when [`PanicOnDrop::defuse`] was called before this type is
/// dropped.
pub fn new(
resource_name: impl Into<Cow<'static, str>>,
error_msg: impl Into<Cow<'static, str>>,
help_msg: impl Into<Cow<'static, str>>,
) -> Self {
Self {
resource_name: resource_name.into(),
error_msg: error_msg.into(),
help_msg: help_msg.into(),
armed: true,
}
}
/// Calling this prevents the panic from being raised when this type is dropped.
pub fn defuse(&mut self) {
self.armed = false;
}
/// When armed, a panic is raised when dropped.
pub fn is_armed(&self) -> bool {
self.armed
}
}
impl Drop for PanicOnDrop {
fn drop(&mut self) {
if self.is_armed() {
let backtrace = backtrace::Backtrace::capture();
panic!(
"Resource '{}' should not have been dropped at this point!\n\nErr: {}\n\nHelp: {}\n\nBacktrace: {:#?}",
self.resource_name, self.error_msg, self.help_msg, backtrace,
);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use assertr::assert_that_type;
use assertr::prelude::*;
#[test]
fn needs_drop() {
assert_that_type::<PanicOnDrop>().needs_drop();
}
}