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

/// General-purpose macro that allows you to write long method chains as a series of
/// statements. See the crate documentation for more details.
#[macro_export]
macro_rules! fluid {
    // Base case: There's no more calls to combine, so just resolve to the final builder.
    ($expr:expr, {}) => { $expr };
    // Nesting case: Use this macro recursively in order to handle each nested branch.
    ($expr:expr, { $block:ident($($args:expr),*) { $($children:tt)+ } $($next:tt)* }) => {
        $crate::fluid!(
            $expr.$block($($args,)* |b| $crate::fluid!(b, { $($children)+ })),
            { $($next)* }
        )
    };
    // Expression-shaped calls.
    ($expr:expr, { [$($items:tt)+]; $($next:tt)* }) => {
        $crate::fluid!(($expr $($items)+), { $($next)*} )
    };
    // Default case: Take one line and turn it into a chained call.
    ($expr:expr, { $command:ident($($args:expr),*); $($next:tt)* }) => {
        $crate::fluid!($expr.$command($($args),*), { $($next)* })
    };
}