unwind_context/context.rs
1/// Creates [`UnwindContextWithIo`] with a default writer, panic detector, color
2/// scheme , and given function or scope context.
3///
4/// It uses [`std::io::stderr`] writer, [`StdPanicDetector`] panic detector, and
5/// a color scheme determined by the [`get_default_color_scheme_if_enabled`]
6/// function. If you want to customize a writer, a panic detector, or a color
7/// scheme, use [`unwind_context_with_io`] or [`unwind_context_with_fmt`].
8///
9/// The returned unwind context scope guard value should be kept alive as long
10/// as unwind context is needed. If unused, the [`UnwindContextWithIo`] will
11/// immediately drop. Note that the colorization is disabled by default and can
12/// be enabled
13#[cfg_attr(
14 feature = "detect-color-support",
15 doc = "
16 either by the [`set_colors_enabled`] or [`enable_colors_if_supported`]
17 functions.
18 "
19)]
20#[cfg_attr(
21 not(feature = "detect-color-support"),
22 doc = "by the [`set_colors_enabled`] function."
23)]
24#[doc = ""]
25/// Passed arguments are lazily formatted. The created wrapper takes ownership
26/// of the given arguments, so it may be necessary to use value references,
27/// clones, or pass the pre-prepared string representation. It also supports the
28/// `...` placeholder to show that some values have been omitted.
29///
30/// There are three forms of this macro:
31/// - Create [`UnwindContextFunc`] with an automatically determined function
32/// name and the given attributes as function attributes. The arguments do not
33/// have to be the real function arguments.
34///
35/// ```rust
36/// use unwind_context::unwind_context;
37///
38/// fn func(a: u32, b: String, c: bool) {
39/// let _ctx = unwind_context!(fn());
40/// let _ctx = unwind_context!(fn(a, &b, c));
41/// let _ctx = unwind_context!(fn(a, b.clone(), c));
42/// let _ctx = unwind_context!(fn(..., c));
43/// let _ctx = unwind_context!(fn(a, ...));
44/// let _ctx = unwind_context!(fn(a, ..., c));
45/// let _ctx = unwind_context!(fn(a, &b, c, "step 1"));
46/// }
47/// ```
48///
49/// - Create [`UnwindContextFunc`] with a specific function names and the given
50/// attributes as function attributes. Note that only ident-like function
51/// names are supported is unquoted. Path names should be enclosed in quotes.
52/// The arguments do not have to be the real function arguments.
53///
54/// ```rust
55/// use unwind_context::unwind_context;
56///
57/// fn func(a: u32, b: String, c: bool) {
58/// let _ctx = unwind_context!(fn func());
59/// let _ctx = unwind_context!(fn func(a, &b, c));
60/// let _ctx = unwind_context!(fn func(a, b.clone(), c));
61/// let _ctx = unwind_context!(fn func(..., c));
62/// let _ctx = unwind_context!(fn func(a, ...));
63/// let _ctx = unwind_context!(fn func(a, ..., c));
64/// let _ctx = unwind_context!(fn func(a, &b, c, "step 1"));
65/// let _ctx = unwind_context!(fn "func"());
66/// let _ctx = unwind_context!(fn "mod1::mod2::func"());
67/// let _ctx = unwind_context!(fn "mod1::mod2::func"(a, &b, c));
68/// }
69/// ```
70///
71/// - Create [`UnwindContextArgs`] with the given scope attributes.
72///
73/// ```rust
74/// use unwind_context::unwind_context;
75///
76/// fn func(a: u32) {
77/// let b = a.to_string();
78/// let c = a > 100;
79/// let _ctx = unwind_context!(a, &b, c);
80/// let _ctx = unwind_context!(a, b.clone(), c);
81/// let _ctx = unwind_context!(..., c);
82/// let _ctx = unwind_context!(a, ...);
83/// let _ctx = unwind_context!(a, ..., c);
84/// let _ctx = unwind_context!(a, &b, c, "step 1");
85/// }
86/// ```
87///
88/// [`unwind_context_with_io`]: crate::unwind_context_with_io
89/// [`unwind_context_with_fmt`]: crate::unwind_context_with_fmt
90/// [`UnwindContextWithIo`]: crate::UnwindContextWithIo
91/// [`StdPanicDetector`]: crate::StdPanicDetector
92/// [`get_default_color_scheme_if_enabled`]: crate::get_default_color_scheme_if_enabled
93/// [`set_colors_enabled`]: crate::set_colors_enabled
94#[cfg_attr(
95 feature = "detect-color-support",
96 doc = "[`enable_colors_if_supported`]: crate::enable_colors_if_supported"
97)]
98/// [`UnwindContextFunc`]: crate::UnwindContextFunc
99/// [`UnwindContextArgs`]: crate::UnwindContextArgs
100#[macro_export]
101macro_rules! unwind_context {
102 ( $( $context:tt )* ) => {
103 $crate::unwind_context_with_io!(
104 ( $($context)* ),
105 writer = ::std::io::stderr(),
106 panic_detector = $crate::StdPanicDetector,
107 color_scheme = $crate::get_default_color_scheme_if_enabled(),
108 )
109 };
110}
111
112/// Creates [`UnwindContextWithIo`] with a default writer, panic detector, color
113/// scheme , and given function or scope context in debug builds only.
114///
115/// It uses [`std::io::stderr`] writer, [`StdPanicDetector`] panic detector, and
116/// a color scheme determined by the [`get_default_color_scheme_if_enabled`]
117/// function. If you want to customize a writer, a panic detector, or a color
118/// scheme, use [`unwind_context_with_io`] or [`unwind_context_with_fmt`].
119///
120/// The returned unwind context scope guard value should be kept alive as long
121/// as unwind context is needed. If unused, the [`UnwindContextWithIo`] will
122/// immediately drop. Note that the colorization is disabled by default and can
123/// be enabled
124#[cfg_attr(
125 feature = "detect-color-support",
126 doc = "
127 `either by the [`set_colors_enabled`] or [`enable_colors_if_supported`]
128 functions.
129 "
130)]
131#[cfg_attr(
132 not(feature = "detect-color-support"),
133 doc = "by the [`set_colors_enabled`] function."
134)]
135#[doc = ""]
136/// Passed arguments are lazily formatted. The created wrapper takes ownership
137/// of the given arguments, so it may be necessary to use value references,
138/// clones, or pass the pre-prepared string representation. It also supports the
139/// `...` placeholder to show that some values have been omitted.
140///
141/// An optimized build will generate `()` unless `-C debug-assertions` is passed
142/// to the compiler. This makes this macro no-op with the default release
143/// profile.
144///
145/// There are three forms of this macro:
146/// - Create [`UnwindContextFunc`] with an automatically determined function
147/// name and the given attributes as function attributes. The arguments do not
148/// have to be the real function arguments.
149///
150/// ```rust
151/// use unwind_context::debug_unwind_context;
152///
153/// fn func(a: u32, b: String, c: bool) {
154/// let _ctx = debug_unwind_context!(fn());
155/// let _ctx = debug_unwind_context!(fn(a, &b, c));
156/// let _ctx = debug_unwind_context!(fn(a, b.clone(), c));
157/// let _ctx = debug_unwind_context!(fn(..., c));
158/// let _ctx = debug_unwind_context!(fn(a, ...));
159/// let _ctx = debug_unwind_context!(fn(a, ..., c));
160/// let _ctx = debug_unwind_context!(fn(a, &b, c, "step 1"));
161/// // ...
162/// }
163/// ```
164///
165/// - Create [`UnwindContextFunc`] with a specific function names and the given
166/// attributes as function attributes. Note that only ident-like function
167/// names are supported is unquoted. Path names should be enclosed in quotes.
168/// The arguments do not have to be the real function arguments.
169///
170/// ```rust
171/// use unwind_context::debug_unwind_context;
172///
173/// fn func(a: u32, b: String, c: bool) {
174/// let _ctx = debug_unwind_context!(fn func());
175/// let _ctx = debug_unwind_context!(fn func(a, &b, c));
176/// let _ctx = debug_unwind_context!(fn func(a, b.clone(), c));
177/// let _ctx = debug_unwind_context!(fn func(..., c));
178/// let _ctx = debug_unwind_context!(fn func(a, ...));
179/// let _ctx = debug_unwind_context!(fn func(a, ..., c));
180/// let _ctx = debug_unwind_context!(fn func(a, &b, c, "step 1"));
181/// let _ctx = debug_unwind_context!(fn "func"());
182/// let _ctx = debug_unwind_context!(fn "mod1::mod2::func"());
183/// let _ctx = debug_unwind_context!(fn "mod1::mod2::func"(a, &b, c));
184/// // ...
185/// }
186/// ```
187///
188/// - Create [`UnwindContextArgs`] with the given scope attributes.
189///
190/// ```rust
191/// use unwind_context::debug_unwind_context;
192///
193/// fn func(a: u32) {
194/// let b = a.to_string();
195/// let c = a > 100;
196/// let _ctx = debug_unwind_context!(a, &b, c);
197/// let _ctx = debug_unwind_context!(a, b.clone(), c);
198/// let _ctx = debug_unwind_context!(..., c);
199/// let _ctx = debug_unwind_context!(a, ...);
200/// let _ctx = debug_unwind_context!(a, ..., c);
201/// let _ctx = debug_unwind_context!(a, &b, c, "step 1");
202/// // ...
203/// }
204/// ```
205///
206/// [`unwind_context_with_io`]: crate::unwind_context_with_io
207/// [`unwind_context_with_fmt`]: crate::unwind_context_with_fmt
208/// [`UnwindContextWithIo`]: crate::UnwindContextWithIo
209/// [`StdPanicDetector`]: crate::StdPanicDetector
210/// [`get_default_color_scheme_if_enabled`]: crate::get_default_color_scheme_if_enabled
211/// [`set_colors_enabled`]: crate::set_colors_enabled
212#[cfg_attr(
213 feature = "detect-color-support",
214 doc = "[`enable_colors_if_supported`]: crate::enable_colors_if_supported"
215)]
216/// [`UnwindContextFunc`]: crate::UnwindContextFunc
217/// [`UnwindContextArgs`]: crate::UnwindContextArgs
218#[macro_export]
219macro_rules! debug_unwind_context {
220 ( $( $context:tt )* ) => { $crate::debug_unwind_context_impl!( $($context)* ) };
221}
222
223#[doc(hidden)]
224#[cfg(debug_assertions)]
225#[macro_export]
226macro_rules! debug_unwind_context_impl {
227 ( $( $context:tt )* ) => { $crate::unwind_context!( $($context)* ) };
228}
229
230#[doc(hidden)]
231#[cfg(not(debug_assertions))]
232#[macro_export]
233macro_rules! debug_unwind_context_impl {
234 ($($context:tt)*) => {
235 ()
236 };
237}
238
239#[cfg(test)]
240mod tests {
241 #[allow(clippy::unwrap_used)]
242 #[test]
243 fn test_unwind_context_without_unwind() {
244 fn func1(foo: usize, bar: &str) -> usize {
245 let _ctx = unwind_context!(fn(foo, bar));
246 func2(foo.checked_mul(2).unwrap(), &bar[1..])
247 }
248
249 fn func2(foo: usize, bar: &str) -> usize {
250 let _ctx = unwind_context!(fn(foo, bar));
251 func3(foo.checked_mul(3).unwrap(), &bar[1..])
252 }
253
254 fn func3(foo: usize, bar: &str) -> usize {
255 let _ctx = unwind_context!(fn(foo, bar));
256 foo.checked_sub(bar.len()).unwrap()
257 }
258
259 let result = func1(1000, "abcdef");
260 assert_eq!(result, 5996);
261
262 let result = func1(1000, "ab");
263 assert_eq!(result, 6000);
264
265 // Only positive cases checked to avoid capturing `stderr`.
266 // Negative cases checked separately with `unwind_context_with_io`.
267 }
268}