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
/// A macro for chaining together statements that act on an initial expression.
/// # Usage
/// Cascading allows you to run multiple statements on a struct generated by an expression without having
/// to repeatedly type it out. This can be very convinient for modifying multiple properties simultaneously,
/// or running a long series of individual methods on a struct.
///
/// When writing a cascade, the first line specifies the expression that will be operated upon.
/// All following lines modify the struct created by the expression in some way.
/// The most common operator is the `..` member operator, which is borrowed from Dart's syntax.
/// It's a convinient shorthand for accessing a member or method of the struct. For example:
/// ```
/// use cascade::cascade;
///
/// let old_vector = vec!(1, 2, 3);
/// let new_vector = cascade! {
///   old_vector;
///   ..push(4);
///   ..push(5);
///   ..push(6);
/// };
/// ```
///
/// Remember, you can't move the struct, because it gets returned at the end of the cascade. In other words,
/// you can't run a method on a struct with the `..` operator if the method takes `self` or
/// `mut self`. But `&self` or `&mut self` works fine.
///
/// If you want to put statements within the macro, you can use the `|` operator:
/// ```
/// use cascade::cascade;
/// use std::collections::HashMap;
///
/// let hash_map = cascade! {
///   HashMap::new();
///   ..insert("foo", "bar");
///   | println!("Look! You can put statements in a cascade!");
///   | for i in 0..3 {
///     println!("You can put loops in here too! Make sure to put a semicolon at the end!");
///   };
/// };
/// ```
///
/// If you need to reference the expression inside a cascade, you can name it and then use it:
/// ```
/// use cascade::cascade;
///
/// let vector = cascade! {
///   v: Vec::new();
///   ..push(1);
///   | println!("The vector now has {} element(s).", v.len());
///   ..push(2);
/// };
/// ```
/// This will print `The vector now has 1 element(s).`
///
/// Once again, trying to move this value will throw an error.
///
/// Finally, you can also use the member operator (`..`) to set or change members of the cascaded struct:
/// ```
/// use cascade::cascade;
/// struct A {
///   pub b: u32,
///   pub c: u32
/// }
///
/// let a = cascade! {
///   A { b: 0, c: 0 };
///   ..b = 5;
///   ..c += 1;
/// };
/// ```
///
/// More examples of the cascade macro can be found in the examples folder on the Github repository.
///
/// # Common Mistakes
/// Sometimes, you might get a weird error while using this macro. Most likely it will be some
/// variation of `no rules expected the token '@'`. Since these errors are notoriously hard to debug,
/// here are some common mistakes one might make that will result in this error being thrown:
/// 1. **Not putting a semicolon at the end of a line.** Every single cascade statement must end
/// with a semicolon. Yes, even loops! This is because the parser needs to know when the line ends and the
/// next line begins, and the semicolon is used as the delimiter.
/// 2. **Not putting a operator at the beginning of a line.** Even if you are just writing a statement,
/// you need to put the `|` operator at the beginning. Once again, this is due to the way Rust parses macros.
/// It also means that each line of the cascade macro is consistent, with an `<operator> <statement>;` syntax.
#[macro_export]
macro_rules! cascade {
    ($i:ident : $e: expr; $($tail: tt)*) => {
        {
            let mut $i = $e;
            cascade!(@line $i, $($tail)*);
            $i
        };
    };
    ($e: expr; $($tail: tt)*) => {
        {
            let mut __tmp = $e;
            cascade!(@line __tmp, $($tail)*);
            __tmp
        };
    };
    (@line $i:ident, | $s: stmt; $($tail: tt)*) => {
        $s;
        cascade!(@line $i, $($tail)*);
    };
    (@line $i: ident, .. $v: ident = $e: expr; $($tail: tt)*) => {
        $i.$v = $e;
        cascade!(@line $i, $($tail)*);
    };
    (@line $i:ident, .. $v:ident += $e:expr; $($tail:tt)*) => {
        $i.$v += $e;
        cascade!(@line $i, $($tail)*);
    };
    (@line $i:ident, .. $v:ident -= $e:expr; $($tail:tt)*) => {
        $i.$v -= $e;
        cascade!(@line $i, $($tail)*);
    };
    (@line $i:ident, .. $v:ident *= $e:expr; $($tail:tt)*) => {
        $i.$v *= $e;
        cascade!(@line $i, $($tail)*);
    };
    (@line $i:ident, .. $($q: ident ($($e: expr),*)).+; $($tail: tt)*) => {
        $i.$($q($($e),*)).+;
        cascade!(@line $i, $($tail)*);
    };
    (@line $i:ident, .. $($q: ident ($($e: expr),*)).+?; $($tail: tt)*) => {
        $i.$($q($($e),*)).+?;
        cascade!(@line $i, $($tail)*);
    };
    (@line $i:ident,) => {};
    () => {}
}