fluid_macro/lib.rs
1//! Contains [`fluid`], a macro that allows you to write long method call chains as a series
2//! of steps instead.
3//!
4//! # Basic Usage
5//!
6//! ```rust
7//! # #[macro_use] extern crate fluid_macro;
8//! # use fluid_macro::fluid;
9//! #
10//! # fn main() {
11//! #
12//! let x = fluid!("123", {
13//! parse::<i32>();
14//! unwrap_or_default();
15//! clamp(5, 100);
16//! to_string();
17//! });
18//!
19//! assert_eq!(x, "100");
20//! #
21//! # }
22//! ```
23//!
24//! This is equivalent to writing:
25//!
26//! ```rust
27//! let x = Some(123).unwrap_or_default().clamp(5, 100).to_string();
28//!
29//! assert_eq!(x, "100");
30//! ```
31//!
32//! # Nested blocks
33//!
34//! Methods that have a final argument that is a one-argument closure support an alternate
35//! block syntax:
36//!
37//! ```rust
38//! # #[macro_use] extern crate fluid_macro;
39//! # use fluid_macro::fluid;
40//! #
41//! struct Example(i32);
42//!
43//! impl Example {
44//! fn add(mut self, arg: i32) -> Self {
45//! self.0 += arg;
46//! self
47//! }
48//!
49//! fn modify(mut self, f: impl FnOnce(i32) -> i32) -> Self {
50//! self.0 = f(self.0);
51//! self
52//! }
53//! }
54//!
55//! # fn main() {
56//! let x = fluid!(Example(0), {
57//! add(15);
58//! modify() {
59//! clamp(20, 50);
60//! }
61//! add(15);
62//! });
63//!
64//! assert_eq!(x.0, 35);
65//! # }
66//! ```
67//!
68//! The above is equivalent to writing:
69//!
70//! ```rust
71//! # struct Example(i32);
72//! #
73//! # impl Example {
74//! # fn add(mut self, arg: i32) -> Self {
75//! # self.0 += arg;
76//! # self
77//! # }
78//! #
79//! # fn modify(mut self, f: impl FnOnce(i32) -> i32) -> Self {
80//! # self.0 = f(self.0);
81//! # self
82//! # }
83//! # }
84//! #
85//! let x = Example(0).add(15).modify(|b| b.clamp(20, 50)).add(15);
86//!
87//! assert_eq!(x.0, 35)
88//! ```
89//!
90//! # Chaining in non-method things
91//!
92//! Although it looks a bit weird, you can write casts and operations with this syntax:
93//!
94//! ```rust
95//! # #[macro_use] extern crate fluid_macro;
96//! # use fluid_macro::fluid;
97//! #
98//! # fn main() {
99//! let x = fluid!(5i32, {
100//! [+ 5];
101//! [as u8];
102//! to_string();
103//! });
104//!
105//! assert_eq!(x, "10");
106//! # }
107//! ```
108//!
109//! The entire chained expression so far will be placed in the first position of the partial
110//! expression in square brackets.
111//!
112//! The above expands to:
113//!
114//! ```rust
115//! let x = ((5i32 + 5) as u8).to_string();
116//!
117//! assert_eq!(x, "10");
118//! ```
119//!
120//! # Known limitations
121//!
122//! It's not very friendly to the IDE whilst writing. You will have to already know the names
123//! of methods you want to use. After compilation, however, symbol lookup and the like works fine.
124
125/// General-purpose macro that allows you to write long method chains as a series of
126/// statements. See the crate documentation for more details.
127#[macro_export]
128macro_rules! fluid {
129 // Base case: There's no more calls to combine, so just resolve to the final builder.
130 ($expr:expr, {}) => { $expr };
131
132 // Nesting case: Use this macro recursively in order to handle each nested branch.
133 ($expr:expr, { $block:ident$(::<$($typeargs:ty),+>)?($($args:expr),*) { $($children:tt)+ } $($next:tt)* }) => {
134 $crate::fluid!(
135 $expr.$block$(::<$($typeargs),+>)?($($args,)* |b| $crate::fluid!(b, { $($children)+ })),
136 { $($next)* }
137 )
138 };
139
140 // Expression-shaped calls.
141 ($expr:expr, { [$($items:tt)+]; $($next:tt)* }) => {
142 $crate::fluid!(($expr $($items)+), { $($next)*} )
143 };
144
145 // Default case: Take one line and turn it into a chained call.
146 ($expr:expr, { $command:ident$(::<$($typeargs:ty),+>)?($($args:expr),*); $($next:tt)* }) => {
147 $crate::fluid!($expr.$command$(::<$($typeargs),+>)?($($args),*), { $($next)* })
148 };
149}