1use std::borrow::Cow;
45
46#[derive(Debug)]
47#[must_use]
48pub struct DropBomb(RealBomb);
49
50impl DropBomb {
51 pub fn new(msg: impl Into<Cow<'static, str>>) -> DropBomb {
52 DropBomb(RealBomb::new(msg.into()))
53 }
54 pub fn defuse(&mut self) {
55 self.set_defused(true)
56 }
57 pub fn set_defused(&mut self, defused: bool) {
58 self.0.set_defused(defused)
59 }
60 pub fn is_defused(&self) -> bool {
61 self.0.is_defused()
62 }
63}
64
65#[derive(Debug)]
66#[must_use]
67pub struct DebugDropBomb(DebugBomb);
68
69impl DebugDropBomb {
70 pub fn new(msg: impl Into<Cow<'static, str>>) -> DebugDropBomb {
71 DebugDropBomb(DebugBomb::new(msg.into()))
72 }
73 pub fn defuse(&mut self) {
74 self.set_defused(true)
75 }
76 pub fn set_defused(&mut self, defused: bool) {
77 self.0.set_defused(defused)
78 }
79 pub fn is_defused(&self) -> bool {
80 self.0.is_defused()
81 }
82}
83
84#[cfg(debug_assertions)]
85type DebugBomb = RealBomb;
86#[cfg(not(debug_assertions))]
87type DebugBomb = FakeBomb;
88
89#[derive(Debug)]
90struct RealBomb {
91 msg: Cow<'static, str>,
92 defused: bool,
93}
94
95impl RealBomb {
96 fn new(msg: Cow<'static, str>) -> RealBomb {
97 RealBomb {
98 msg: msg.into(),
99 defused: false,
100 }
101 }
102 fn set_defused(&mut self, defused: bool) {
103 self.defused = defused
104 }
105 fn is_defused(&self) -> bool {
106 self.defused
107 }
108}
109
110impl Drop for RealBomb {
111 fn drop(&mut self) {
112 if !self.defused && !::std::thread::panicking() {
113 panic!("{}", self.msg)
114 }
115 }
116}
117
118#[derive(Debug)]
119#[cfg(not(debug_assertions))]
120struct FakeBomb {}
121
122#[cfg(not(debug_assertions))]
123impl FakeBomb {
124 fn new(_msg: Cow<'static, str>) -> FakeBomb {
125 FakeBomb {}
126 }
127 fn set_defused(&mut self, _defused: bool) {}
128 fn is_defused(&self) -> bool {
129 true
130 }
131}
132
133#[cfg(not(debug_assertions))]
134impl Drop for FakeBomb {
135 fn drop(&mut self) {}
136}
137
138#[cfg(test)]
139mod tests {
140 use super::*;
141
142 #[test]
143 #[should_panic(expected = "Kaboom")]
144 fn armed_bomb_bombs() {
145 let _b = DropBomb::new("Kaboom");
146 }
147
148 #[test]
149 fn defused_bomb_is_safe() {
150 let mut b = DropBomb::new("Kaboom");
151 assert!(!b.is_defused());
152 b.defuse();
153 assert!(b.is_defused());
154 }
155
156 #[test]
157 #[should_panic(expected = r#"printf("sucks to be you"); exit(666);"#)]
158 fn no_double_panics() {
159 let _b = DropBomb::new("Kaboom");
160 panic!(r#"printf("sucks to be you"); exit(666);"#)
161 }
162
163 #[test]
164 #[should_panic(expected = "Kaboom")]
165 #[cfg(debug_assertions)]
166 fn debug_bomb_bombs_if_debug() {
167 let _b = DebugDropBomb::new("Kaboom");
168 }
169
170 #[test]
171 #[cfg(not(debug_assertions))]
172 fn debug_bomb_bombs_if_debug() {
173 let _b = DebugDropBomb::new("Kaboom");
174 }
175
176 #[test]
177 fn defused_bomb_is_safe_if_debug() {
178 let mut b = DebugDropBomb::new("Kaboom");
179 #[cfg(debug_assertions)]
180 assert!(!b.is_defused());
181 #[cfg(not(debug_assertions))]
182 assert!(b.is_defused());
183 b.defuse();
184 assert!(b.is_defused());
185 }
186
187 #[test]
188 #[should_panic(expected = r#"printf("sucks to be you"); exit(666);"#)]
189 fn no_double_panics_if_debug() {
190 let _b = DebugDropBomb::new("Kaboom");
191 panic!(r#"printf("sucks to be you"); exit(666);"#)
192 }
193
194 #[test]
195 #[cfg(not(debug_assertions))]
196 fn debug_bomb_is_zst() {
197 assert_eq!(::std::mem::size_of::<DebugDropBomb>(), 0);
198 }
199
200 #[test]
201 fn check_traits() {
202 fn assert_traits<T: ::std::fmt::Debug + Send + Sync>() {}
203 assert_traits::<DropBomb>();
204 assert_traits::<DebugDropBomb>();
205 }
206}