stdext/
macros.rs

1//! Various helper macros.
2
3/// `compile_warning` macro is a brother of [`std::compile_error`],
4/// which emits a compile-time warning with a provided message.
5///
6/// This implemented through an existing `dead_code` warning, thus the
7/// output for the following example:
8///
9/// ```rust
10/// # use stdext::compile_warning;
11/// compile_warning!("Sample user-defined warning!");
12/// ```
13///
14/// may look as follows:
15///
16/// ```text
17/// warning: constant item is never used: `WARNING`
18///   --> src/lib.rs:7:9
19///   |
20/// 7 |         const WARNING: &str = $expr;
21///   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
22/// ...
23/// 11 | compile_warning!("Sample user-defined warning!");
24///    | ------------------------------------------------- in this macro invocation
25/// ```
26///
27/// Once [`proc_macro_diagnostics`] feature is stabilized, this macro will be replaced
28/// with a proper proc-macro-based implementation.
29///
30/// This macro is intended to be used in the development process, as an alternative to the
31/// [`unimplemented`] macro which doesn't cause code to panic.
32///
33/// [`std::compile_error`]: https://doc.rust-lang.org/std/macro.compile_error.html
34/// [`proc_macro_diagnostics`]: https://github.com/rust-lang/rust/issues/54140
35/// [`unimplemented`]: https://doc.rust-lang.org/std/macro.unimplemented.html
36#[macro_export]
37macro_rules! compile_warning {
38    ($expr:expr) => {
39        #[warn(dead_code)]
40        const WARNING: &str = $expr;
41    };
42}
43
44/// This macro returns the name of the enclosing function.
45/// As the internal implementation is based on the [`std::any::type_name`], this macro derives
46/// all the limitations of this function.
47///
48/// ## Examples
49///
50/// ```rust
51/// mod bar {
52///     pub fn sample_function() {
53///         use stdext::function_name;
54///         assert!(function_name!().ends_with("bar::sample_function"));
55///     }
56/// }
57///
58/// bar::sample_function();
59/// ```
60///
61/// [`std::any::type_name`]: https://doc.rust-lang.org/std/any/fn.type_name.html
62#[macro_export]
63macro_rules! function_name {
64    () => {{
65        // Okay, this is ugly, I get it. However, this is the best we can get on a stable rust.
66        fn f() {}
67        fn type_name_of<T>(_: T) -> &'static str {
68            std::any::type_name::<T>()
69        }
70        let name = type_name_of(f);
71        // `3` is the length of the `::f`.
72        &name[..name.len() - 3]
73    }};
74}
75
76/// This macro returns the name of the enclosing function, line number and also filename.
77/// As the internal implementation is based on the [`std::any::type_name`], this macro derives
78/// all the limitations of this function.
79///
80/// ## Examples
81///
82/// ```rust
83/// mod bar {
84///     pub fn sample_function() {
85///         use stdext::debug_name;
86///         assert!(debug_name!().starts_with("src/macros.rs:8"));
87///         assert!(debug_name!().ends_with("bar::sample_function"));
88///     }
89/// }
90///
91/// bar::sample_function();
92/// ```
93///
94/// [`std::any::type_name`]: https://doc.rust-lang.org/std/any/fn.type_name.html
95#[macro_export]
96macro_rules! debug_name {
97    () => {{
98        fn f() {}
99        fn type_name_of<T>(_: T) -> &'static str {
100            std::any::type_name::<T>()
101        }
102        let name = type_name_of(f);
103        // `3` is the length of the `::f`.
104        let trimmed_name = &name[..name.len() - 3];
105        let file = file!();
106        let line = line!();
107        format!("{file}:{line} at {trimmed_name}")
108    }};
109}
110
111/// Attempts to get variant from the enum variable.
112///
113/// ## Examples
114///
115/// ```rust
116/// # use stdext::try_match;
117///
118/// #[derive(Debug, PartialEq)]
119/// enum Foo {
120///     Left(u16),
121///     Right(&'static str),
122/// }
123///
124/// assert_eq!(try_match!(Foo::Left(18), Foo::Left), Ok(18));
125/// assert_eq!(
126///     try_match!(Foo::Right("nope"), Foo::Left),
127///     Err(Foo::Right("nope"))
128/// );
129/// ```
130#[macro_export]
131macro_rules! try_match {
132    ($var:expr, $variant:path) => {
133        if let $variant(x) = $var {
134            Ok(x)
135        } else {
136            Err($var)
137        }
138    };
139}
140
141/// Similar to [`try_match`] but additionally unwraps the result.
142///
143/// ## Panics
144///
145/// Panics if expression didn't match the provided path.
146///
147/// ## Examples
148///
149/// ```rust
150/// # use stdext::unwrap_match;
151///
152/// #[derive(Debug, PartialEq)]
153/// enum Foo {
154///     Left(u16),
155///     Right(&'static str),
156/// }
157///
158/// assert_eq!(unwrap_match!(Foo::Left(18), Foo::Left), 18);
159/// ```
160///
161/// The following example will panic:
162///
163/// ```should_panic
164/// # use stdext::unwrap_match;
165/// # #[derive(Debug, PartialEq)]
166/// # enum Foo {
167/// #     Left(u16),
168/// #     Right(&'static str),
169/// # }
170/// assert_eq!(unwrap_match!(Foo::Right("nope"), Foo::Left), 18);
171/// ```
172#[macro_export]
173macro_rules! unwrap_match {
174    ($var:expr, $variant:path) => {
175        $crate::try_match!($var, $variant).unwrap()
176    };
177}
178
179/// Checks whether supplied [`Result`] variable is `Ok`
180/// and if so, returns it.
181///
182/// If variant is an `Err`, macro evaluates to the contents of the `Err`
183/// variant.
184///
185/// This macro supports two forms:
186/// - `return_ok!(Ok(42));` - will return `Ok(42)`.
187/// - `return_ok!(inner Ok(42));` - will return just `42`.
188///
189/// ## Examples
190///
191/// ```rust
192/// # use stdext::return_ok;
193///
194/// fn choose_one(left: Result<u8, ()>, right: Result<u8, ()>) -> Result<u8, ()> {
195///     return_ok!(left);
196///     return_ok!(right);
197///     Err(())
198/// }
199///
200/// fn choose_one_inner(left: Result<u8, ()>, right: Result<u8, ()>) -> u8 {
201///     return_ok!(inner left);
202///     return_ok!(inner right);
203///     panic!("Both variables are bad")
204/// }
205///
206/// assert_eq!(choose_one(Err(()), Ok(10)), Ok(10));
207/// assert_eq!(choose_one_inner(Ok(1), Err(())), 1);
208/// ```
209#[macro_export]
210macro_rules! return_ok {
211    ($var:expr) => {
212        match $var {
213            Ok(val) => return Ok(val),
214            Err(err) => err,
215        }
216    };
217    (inner $var:expr) => {
218        match $var {
219            Ok(val) => return val,
220            Err(err) => err,
221        }
222    };
223}
224
225/// Checks whether supplied [`Option`] variable is `Some`
226/// and if so, returns it.
227///
228/// If variant is an `None`, nothing happens.
229///
230/// This macro supports two forms:
231/// - `return_some!(Some(42));` - will return `Some(42)`.
232/// - `return_some!(inner Some(42));` - will return just `42`.
233///
234/// ## Examples
235///
236/// ```rust
237/// # use stdext::return_some;
238///
239/// fn choose_one(left: Option<u8>, right: Option<u8>) -> Option<u8> {
240///     return_some!(left);
241///     return_some!(right);
242///     None
243/// }
244///
245/// fn choose_one_inner(left: Option<u8>, right: Option<u8>) -> u8 {
246///     return_some!(inner left);
247///     return_some!(inner right);
248///     panic!("Both variables are bad")
249/// }
250///
251/// assert_eq!(choose_one(None, Some(10)), Some(10));
252/// assert_eq!(choose_one_inner(Some(1), None), 1);
253/// ```
254#[macro_export]
255macro_rules! return_some {
256    ($var:expr) => {
257        match $var {
258            Some(val) => return Some(val),
259            None => {}
260        }
261    };
262    (inner $var:expr) => {
263        match $var {
264            Some(val) => return val,
265            None => {}
266        }
267    };
268}