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}