cascade-1.0.1/.cargo_vcs_info.json0000644000000001360000000000100124160ustar { "git": { "sha1": "b1ee4a1633e7b59e35892aef9c8a8d2e9a7e88d2" }, "path_in_vcs": "" }cascade-1.0.1/.github/workflows/rust.yml000064400000000000000000000003511046102023000163220ustar 00000000000000name: Rust on: [push] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Build run: cargo build --verbose - name: Run examples run: cargo run --example basic_cascades cascade-1.0.1/.gitignore000064400000000000000000000000551046102023000131760ustar 00000000000000 /target/ **/*.rs.bk Cargo.lock /.idea/ *.imlcascade-1.0.1/Cargo.lock0000644000000002120000000000100103640ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. [[package]] name = "cascade" version = "1.0.1" cascade-1.0.1/Cargo.toml0000644000000013400000000000100104120ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] name = "cascade" version = "1.0.1" authors = ["Jane Lewis "] description = "Dart-like cascade macro for Rust" readme = "README.md" license = "MIT" repository = "https://github.com/inquisitivepenguin/cascade" [dependencies] cascade-1.0.1/Cargo.toml.orig000064400000000000000000000004151046102023000140750ustar 00000000000000[package] name = "cascade" version = "1.0.1" authors = ["Jane Lewis "] description = "Dart-like cascade macro for Rust" license = "MIT" readme = "README.md" repository = "https://github.com/inquisitivepenguin/cascade" [dependencies] cascade-1.0.1/LICENSE000064400000000000000000000020341046102023000122120ustar 00000000000000Copyright 2018 Jackson Lewis Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.cascade-1.0.1/README.md000064400000000000000000000074631046102023000124770ustar 00000000000000# `cascade`: Cascade expressions in Rust! `cascade` is a macro library for Rust that makes it easy and ergonomic to use cascade-like expressions, similar to Dart. ```rust #[macro_use] extern crate cascade; fn main() { let cascaded_list = cascade! { Vec::new(); ..push("Cascades"); ..push("reduce"); ..push("boilerplate"); }; println!("{:?}", cascaded_list); // Will print '["Cascades", "reduce", "boilerplate"]' } ``` This is only a small example of what `cascade` lets you do: the `basic_cascades` example in this repository covers the other cool features of the `cascade!` macro. #### Why does this need to exist? Cascades reduce boilerplate by eliminating the need for a 'temporary' variable when making several method calls in a row, and it also helps make struct member assignments look more ergonomic. For example: ```rust #[macro_use] extern crate cascade; #[derive(Clone, Debug)] struct Person { pub name: String, pub age: u32, pub height: u32, pub friend_names: Vec } fn main() { // Without cascades let person = Person { name: "John Smith", age: 17, height: 68, // 5' 8" friend_names: { let mut tmp_names = Vec::new(); tmp_names.push("James Smith".to_string()); tmp_names.push("Bob Jones".to_string()); tmp_names.push("Billy Jones".to_string()); tmp_names } }; // With cascades let person = Person { name: "John Smith", age: 17, height: 68, friend_names: cascade! { Vec::new(); ..push("James Smith".to_string()); ..push("Bob Jones".to_string()); ..push("Billy Jones".to_string()); } }; // Cascades also let you do cool stuff like this let person_one_year_later = cascade! { person; ..age += 1; ..height += 2; }; } ``` In addition, cascades make it easier to design fluent interfaces. No more returning `self` with every single function! ### Changelog **1.0.0**: `cascade` has reached 1.0! Here are some of the cool new features and syntax changes made as a part of this: - The syntax for binding variables has been changed to use `let` syntax. This makes it more in-line with Rust syntax and also allows you to specify the type of a cascaded expression. ```rust cascade! { // If you don't need to bind the statement to an identifier, you can use _ let _: Vec = vec![1,2,3].into_iter().map(|x| x + 1).collect(); ..push(1); } ``` - Statements no longer need `|` in front of them. You can just put the statement right in there, no prefix needed. - You can return expressions from cascades, just like normal Rust blocks. By default, cascades will already return the value of the cascaded variable. But if you want to return a custom expression, you can put it at the end of a cascade block, with no semicolon. ```rust let has_more_than_three_elements = cascade! { let v = vec![1,2,3]; ..push(4); v.len() > 3 }; println!("{}", cascade! { vec![1,2,3]; ..push(4); ..into_iter().fold(0, |acc, x| acc + x) }); ``` - Finally, you can have nested blocks within a cascade block. For example: ```rust cascade! { vec![1,2,3]; { let a = 1; ..push(a); }; } ``` I hope you enjoy cascade `1.0`! Remember to leave any complaints or suggestions on [the issue tracker](https://github.com/InquisitivePenguin/cascade/issues). **0.1.3**: The ? operator now works with cascades, for scenarios like this: ```rust fn file_read() -> Result { cascade! { SomeFileClass::create_file_reader("test.txt"); ..load()?; ..check_validity()?; } } ``` **0.1.2**: You can now chain methods together, like this: ```rust fn chain_example() { cascade! { FnChainTest::new(); ..chain().chain().chain(); } } ``` ### Credits Written by Jane Lewis cascade-1.0.1/examples/basic_cascades.rs000064400000000000000000000064641046102023000163130ustar 00000000000000#[macro_use] extern crate cascade; #[derive(Clone, Debug)] struct Person { name: String, age: u32, height: u32, } impl Person { pub fn blank() -> Person { Person { name: "".to_string(), age: 0, height: 0, } } } #[derive(Clone, Debug)] struct Chain { links: Vec, } impl Chain { fn blank() -> Chain { Chain { links: vec![] } } fn add(mut self, link: u32) -> Self { self.links.push(link); self } } #[allow(unused)] fn main() { // Cascades can be used recursively! let people = cascade! { Vec::new(); ..push(cascade! { Person::blank(); ..name = "John Smith".to_string(); ..height = 72; // 6 feet, or 72 inches tall ..age = 34; }); // This is what an expanded cascade looks like. ..push({ let mut __tmp = Person::blank(); __tmp.name = "Jason Smith".to_string(); __tmp.height = 64; __tmp.age = 34; __tmp }); }; // Any expression works as the first statement of a cascade. let other_person = cascade! { people[0].clone(); ..name = "Bob Smith".to_string(); ..height = 66; }; // You can also use +=, -=, *=, /= for operators let another_person = cascade! { other_person.clone(); ..name = "Joan Smith".to_string(); ..age += 3; ..height -= 4; }; // You can put regular statements inside of a cascade macro let yet_another_person = cascade! { people[0].clone(); ..name = "James Smith".to_string(); ..age = 27; println!("Cascades in Rust are cool!"); ..height -= 3; }; // You can bind the initial value of the cascade to an identifier, which reflects the current state of the cascaded value. let one_more_person = cascade! { let person = people[0].clone(); println!("'person' was equal to: {:?}", person); ..name = "James Smith".to_string(); ..height = ((person.height as f32) * 0.8) as u32; println!("'person' is now equal to: {:?}", person); }; // As of version 0.1.2, you can also chain methods together. Observe: let method_chain_example = cascade! { let ch = Chain::blank(); ..add(5).add(6).add(7); // In this case, ch is consumed here. So we have to shadow ch to avoid an error. Obviously, this isn't the most useful way to method-chain. let ch = (); }; // You can have nested blocks within the cascade let block_example = cascade! { Vec::new(); ..push(1); ..push(2); }; let has_more_than_three_elements = cascade! { let v = vec![1,2,3]; ..push(4); v.len() > 3 }; println!("{}", cascade! { vec![1,2,3]; ..push(4); ..into_iter().fold(0, |acc, x| acc + x) }); cascade! { let _: Vec = vec![1,2,3].into_iter().map(|x| x + 1).collect(); ..push(1); }; option_cascade_test().unwrap().unwrap(); } // As of version 0.1.3, you can use the ? operator after a .. statement. fn option_cascade_test() -> Result, ()> { let question_mark_operator_example: Result, ()> = cascade! { Ok(Ok(())); ..unwrap()?; }; return question_mark_operator_example; } cascade-1.0.1/src/lib.rs000064400000000000000000000110271046102023000131120ustar 00000000000000/// 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. /// /// You can also put statements without a `..` in a cascade macro: /// ``` /// 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 using `let`: /// ``` /// use cascade::cascade; /// /// let vector = cascade! { /// let 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. #[macro_export] macro_rules! cascade { (let _ : $t:ty = $e: expr; $($tail: tt)*) => { cascade!(let __tmp: $t = $e; $($tail)*) }; (let $i:ident : $t:ty = $e: expr; $($tail: tt)*) => { { let mut $i: $t = $e; cascade!(@line $i, $($tail)*) } }; (let $i:ident = $e: expr; $($tail: tt)*) => { { let mut $i = $e; cascade!(@line $i, $($tail)*) } }; ($e: expr; $($tail: tt)*) => { cascade!(let __tmp = $e; $($tail)*) }; (@line $i: ident, .. $v: ident = $e: expr; $($tail: tt)*) => { { $i.$v = $e; $crate::cascade!(@line $i, $($tail)*) } }; (@line $i:ident, .. $v:ident += $e:expr; $($tail:tt)*) => { { $i.$v += $e; $crate::cascade!(@line $i, $($tail)*) } }; (@line $i:ident, .. $v:ident -= $e:expr; $($tail:tt)*) => { { $i.$v -= $e; $crate::cascade!(@line $i, $($tail)*) } }; (@line $i:ident, .. $v:ident *= $e:expr; $($tail:tt)*) => { { $i.$v *= $e; $crate::cascade!(@line $i, $($tail)*) } }; (@line $i:ident, .. $($q: ident ($($e: expr),*)).+; $($tail: tt)*) => { { $i.$($q($($e),*)).+; $crate::cascade!(@line $i, $($tail)*) } }; (@line $i:ident, .. $($q: ident ($($e: expr),*)).+?; $($tail: tt)*) => { { $i.$($q($($e),*)).+?; $crate::cascade!(@line $i, $($tail)*) } }; (@line $i:ident, { $($t:tt)* }; $($tail: tt)*) => { { { $crate::cascade!(@line $i, $($t)*); } $crate::cascade!(@line $i, $($tail)*) } }; (@line $i:ident, $s: stmt; $($tail: tt)*) => { { $s cascade!(@line $i, $($tail)*) } }; (@line $i:ident, { $($t:tt)* }) => { { $crate::cascade!(@line $i, $($t)*) } }; (@line $i:ident, .. $($q: ident ($($e: expr),*)).+) => { $i.$($q($($e),*)).+ }; (@line $i:ident, $e:expr) => { $e }; (@line $i:ident,) => { $i }; () => {} }