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}