postfix_macros/lib.rs
1/*!
2Postfix macros on stable Rust, today.
3
4```
5# use postfix_macros::{postfix_macros, unwrap_or};
6# #[derive(Debug, Clone, Copy)] enum Custom { Enum(()), EnumOther}
7# let val = [((),Custom::EnumOther,)];
8postfix_macros! {
9 "hello world".println!();
10 42.assert_ne!(4 + 2);
11
12 val.iter()
13 .map(|v| v.1)
14 .find(|z| z.matches!(Custom::Enum(_) | Custom::EnumOther))
15 .dbg!()
16 .unwrap_or!{ return };
17}
18```
19
20The crate provides the [`postfix_macros!`] macro,
21as well as some helpful macros for use in a postfix context,
22for your greatest convenience.
23
24| Rust construct | postfix replacement macro |
25| - | - |
26| `unwrap_or`, `unwrap_or_else` | [`unwrap_or!`] |
27| **`if let`** with else clause | [`match_or!`] |
28| **`match`** with default case | [`match_or!`] |
29| **`if`** `<bool>`, `bool::then` | [`then!`] |
30| **`else`** | [`then_else!`] |
31*/
32#![forbid(unsafe_code)]
33
34/**
35Proc macro to parse code containing postfix macros,
36to rewrite it to use traditional macro invocations.
37
38The main macro of this crate.
39
40The macro scans for `expr.macro_invoc!(params)` patterns
41and changes them to `macro_invoc!(expr, params)` patterns.
42
43If no parameters are passed to the postfix macro,
44then no trailing `,` is emitted.
45*/
46pub use postfix_macros_impl::postfix_macros;
47
48/**
49Either unwraps the content passed to the macro,
50or executes the passed code block.
51
52The macro is very similar to functions like
53[`Option::unwrap_or`](std::option::Option::unwrap_or),
54in that it tries to attain the content contained inside,
55and if that's not possible, evaluates to the alternative
56provided by the user.
57
58Unlike the function though, the body of the macro is lazily
59evaluated, so only if there is actually the need to return
60the alternative, similar to the `unwrap_or_else` function.
61
62A code block is way more powerful as `unwrap_or_else`,
63though, as it allows issuing commands like `continue`,
64`return`, or `break` in the body that can bring control
65flow outside of the body.
66
67As such, the `unwrap_or` macro combines the benefits of
68both the `unwrap_or` and `unwrap_or_else` functions.
69
70The macro requires the presence of two functions on the
71underlying type: `map` and `unwrap_or`. Maybe in the future
72when the `Try` trait is stable, it will be used instead.
73
74If you want to do more powerful matching, you can
75use the [`match_or!`] macro instead.
76
77# Examples
78
79```
80# use postfix_macros::{postfix_macros, unwrap_or};
81# postfix_macros! {
82let v = Err(());
83let mut w = 0;
84for i in 0..3 {
85 w += i;
86 v.unwrap_or!{ continue };
87 break
88}
89assert_eq!(w, 3);
90# }
91```
92*/
93#[macro_export]
94macro_rules! unwrap_or {
95 ($v:expr, $($w:tt)*) => {
96 if let Some(inner) = $v.map(|v| Some(v)).unwrap_or(None) {
97 inner
98 } else {
99 $($w)*
100 }
101 };
102}
103
104/**
105**`match`** macro with a default case shorthand
106
107Meant to be used in a postfix context, as
108the postfix analog of **`match`** and **`if let`**
109Rust constructs.
110
111```
112# use postfix_macros::{postfix_macros, match_or};
113# postfix_macros! {
114#[derive(Copy, Clone)]
115enum Foo {
116 Bar(u8),
117 Baz,
118}
119let v = Foo::Bar(42);
120let mut w = 0;
121for i in 0..3 {
122 w += i;
123 v.match_or!{ Foo::Bar(x) => x; break };
124}
125assert_eq!(w, 3);
126# }
127```
128*/
129#[macro_export]
130macro_rules! match_or {
131 ($v:expr, $($pat:pat => $e:expr)+ ; $($else:tt)*) => {
132 match $v {
133 $($pat => $e)*,
134 _ => {
135 $($else)*
136 },
137 }
138 };
139}
140
141
142/**
143Executes the body if the argument is `true`
144
145Meant to be used in a postfix context, as
146the postfix analog of **`if`**.
147
148With the `bool::then` function, there is a
149[currently unstable](https://github.com/rust-lang/rust/issues/64260)
150equivalent in the standard library, but
151the macro doesn't put the body into a
152closure, and is thus more powerful.
153
154Evaluates the first argument as a boolean,
155and if it's `true`, executes the body.
156
157```
158# use postfix_macros::{postfix_macros, then};
159# postfix_macros! {
160let mut w = 0;
161for i in 1..10 {
162 w += i;
163 (w % i == 0).then!{ w += i * i };
164}
165assert_eq!(w, 75);
166# }
167```
168*/
169#[macro_export]
170macro_rules! then {
171 ($v:expr, $($body:tt)*) => {
172 if $v {
173 $($body)*
174 }
175 };
176}
177
178/**
179**`else`** clauses for the [`then!`] macro
180
181Meant to be used in a postfix context.
182The [`then!`] macro would serve as
183the postfix analog of **`if`**, while
184this macro would be the postfix analog
185of **`else`**.
186
187```
188# use postfix_macros::{postfix_macros, then, then_else};
189# postfix_macros! {
190let mut w = 0;
191for i in 1..10 {
192 w += i;
193 (w % i == 0)
194 .then!{ w += i * i }
195 .then_else!{ w += 1 };
196}
197assert_eq!(w, 181);
198# }
199```
200*/
201#[macro_export]
202macro_rules! then_else {
203 ($v:tt, $($body:tt)*) => {
204 $v {
205 $($body)*
206 }
207 };
208}