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)* })
};
}