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,) => {}; () => {} }