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}