# fluent-impl [![Crate](https://img.shields.io/crates/v/fluent-impl.svg)](https://crates.io/crates/fluent-impl) [![Documentation](https://docs.rs/fluent-impl/badge.svg)](https://docs.rs/fluent-impl) [![Travis Build Status](https://api.travis-ci.org/rust-alt/fluent-impl.svg?branch=master)](https://travis-ci.org/rust-alt/fluent-impl) [![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/github/rust-alt/fluent-impl?svg=true&branch=master)](https://ci.appveyor.com/project/MoSal/fluent-impl) ![Minimum Rust version: 1.30](https://img.shields.io/badge/Minimum%20Rust%20Version-1.30-brightgreen.svg) | ___Linux___ | ___OSX___ | ___Windows___ | |:----:|:----:|:----:| | ![linux-nightly](https://badges.herokuapp.com/travis/rust-alt/fluent-impl?env=BADGE=linux-nightly&label=linux%20nightly) | ![osx-nightly](https://badges.herokuapp.com/travis/rust-alt/fluent-impl?env=BADGE=osx-nightly&label=%20%20osx%20nightly) | ![windows-nightly](https://badges.herokuapp.com/travis/rust-alt/fluent-impl?env=BADGE=windows-nightly&label=%20%20windows%20nightly) | | ![linux-beta](https://badges.herokuapp.com/travis/rust-alt/fluent-impl?env=BADGE=linux-beta&label=%20linux%20beta%20%20) | ![osx-beta](https://badges.herokuapp.com/travis/rust-alt/fluent-impl?env=BADGE=osx-beta&label=%20%20%20osx%20beta%20%20) | ![windows-beta](https://badges.herokuapp.com/travis/rust-alt/fluent-impl?env=BADGE=windows-beta&label=%20%20%20windows%20beta%20%20) | | ![linux-stable](https://badges.herokuapp.com/travis/rust-alt/fluent-impl?env=BADGE=linux-stable&label=linux%20stable%20) | ![osx-stable](https://badges.herokuapp.com/travis/rust-alt/fluent-impl?env=BADGE=osx-stable&label=%20osx%20%20stable%20) | ![windows-stable](https://badges.herokuapp.com/travis/rust-alt/fluent-impl?env=BADGE=windows-stable&label=%20windows%20%20stable%20) | A procedural macro that generates chaining methods from non-chaining ones in an impl block. When applied to an impl block, `#[fluent_impl]` will scan all methods in the block in search for chain-able methods, and generate chaining methods from them. Chain-able methods are the ones with `&mut self` as a first argument, and return nothing. That's it, there are no other restrictions. # Usage Add `fluent-impl` to the dependencies in `Cargo.toml`. Then add the following to the top of `src/lib.rs`: ``` rust ignore extern crate fluent_impl; use fluent_impl::{fluent_impl, fluent_impl_opts}; ``` # Examples If we have a simple struct with a simple impl block: ``` rust #[derive(Default, PartialEq, Debug)] pub struct Simple { num: i32, } impl Simple { // ... pub fn add_1(&mut self) { self.num +=1; } } ``` Then we add the macro attribute to the impl block: ``` rust ignore #[fluent_impl] impl Simple { // ... pub fn add_1(&mut self) { self.num +=1; } } ``` The macro will generate a new impl block with the content: ``` rust ignore #[doc = "Chaining (fluent) methods for [`Simple`]."] impl Simple { #[doc = "The chaining (fluent) equivalent of [`add_1()`].\n\n [`add_1`]: Simple::add_1\n [`add_1()`]: Simple::add_1"] pub fn with_add_1(mut self) -> Self { self.add_1(); self } } ``` A full more involved example can be found bellow the *Attribute Configuration* section. # Attribute Configuration `#[fluent_impl]` is configurable with comma-separated options passed to the attribute itself, and options passed to a method-level attribute `#[fluent_impl_opts]`. ## `#[fluent_impl]` Attribute Options *(`inblock`, `non_public`, `prefix`, `impl_doc`, `doc`)* *impl block*-level configuration. ### Example ``` rust ignore #[fluent_impl(inblock, non_public, prefix="chain_")] impl Simple { // ... } ``` ### Options * **`inblock`** (default: unset) By default, a new impl block is generated, and chaining methods are added there. If `inblock` is passed, every chaining method will be generated right below the chain-able one. The order in which methods appear on docs is probably the only reason why you should care about this. There is a corresponding method-level *`inblock`* option which will selectively enable this behavior for individual methods. * **`non_public`** (default: unset) By default, non fully-public methods are skipped. If this option is passed, the macro will generate chaining equivalents for chain-able private or partially-public methods. There is a corresponding method-level *`non_public`* option which will selectively enable this behavior for individual methods. * **`prefix`** (default: "with_") The default chaining method name is this prefix appended by the chain-able method name. * *`prefix`* is not allowed to be an empty string. Check the *`name`* method-level option if you want to name a chaining method to whatever you like. There is a corresponding method-level *`prefix`* option which will selectively override the value set here (or the default). * **`impl_doc`** (default: "Chaining (fluent) methods for [\`%t%\`].") If a new block is generated for the chaining methods, this is the doc string template for it. `%t%` is replaced with the type path. * **`doc`** (default: "The chaining (fluent) equivalent of [\`%f%()\`].") Chaining method doc string template. `%t%` is replaced with the type path. `%f%` is replaced with the chain-able method name. Additionally, the following is effectively appended at the end: ``` text /// /// [`%f%`]: %t%::%f% /// [`%f%()`]: %t%::%f% ``` This allows proper hyper-linking of ``[`%t%`]`` and ``[`%t%()`]``. There is a corresponding method-level *`doc`* option which will selectively override the value set here (or the default). ## `#[fluent_impl_opts]` Attribute Options *(`inblock`, `non_public`, `skip`, `prefix`, `rename`, `name`, `doc`)* Options passed to override block-level defaults, or set method-specific configurations. Unlike `#[fluent_impl]`, this attribute: 1. Applies to methods instead of impl blocks. 2. Can be passed multiple times to the same method if you please. ### Example ``` rust ignore #[fluent_impl] impl Simple { #[fluent_impl_opts(non_public, inblock)] #[fluent_impl_opts(prefix="chain_", rename="added_1")] fn add_1(&mut self) { // ... } } ``` ### Options #### Inherited * **`inblock`** (default: inherit) Set *`inblock`* for this specific method if it's not set for the block already. * **`non_public`** (default: inherit) Set *`non_public`* for this specific method if it's not set for the block already. This allows generating chaining methods for specific private methods, or partially public ones (e.g. `pub(crate)` methods). * **`prefix`** (default: inherit) Override the default, or the block value if set. * *`prefix`* is not allowed to be an empty string. * Method-specific *`prefix`* is not allowed to be set if *`name`*(see below) is set. * **`doc`** (default: inherit) Override the default, or the block value if set. #### Method Specific * **`skip`** (default: unset) Skip this method. Don't generate anything from it. * **`rename`** (default: chain-able name) The default chaining method name is the prefix appended by the chain-able method name. This option allows you to rename the name that gets added to the prefix. * *`rename`* is not allowed to be an empty string. * *`rename`* is not allowed to be set if *`name`*(see below) is set and vise versa. * **`name`** (default: unset) Set the name of the chaining method. * *`name`* is not allowed to be set if method-specific *`prefix`* or *`rename`* is set. # Full Example ``` rust extern crate fluent_impl; pub mod m { use fluent_impl::{fluent_impl, fluent_impl_opts}; use std::borrow::Borrow; use std::ops::AddAssign; #[derive(PartialEq, Debug)] pub struct TCounter(pub u32); #[derive(PartialEq, Debug)] pub struct St { value: A, text: String, } #[fluent_impl] // impl block with generic arguments works impl St { // Constants (or any other items) in impl block are okay pub(crate) const C_TC: u32 = 100; pub fn new(value: A, text: String) -> Self { Self { value, text } } pub fn get_value(&self) -> &A { &self.value } pub fn get_text(&self) -> &str { &self.text } #[fluent_impl_opts(rename = "added_value")] // Destructuring patterns in method arguments are okay pub fn add_value( &mut self, to_be_added: A, TCounter(counter): &mut TCounter, ) { self.value += to_be_added; *counter += 1; } #[fluent_impl_opts(rename = "appended_text")] // Generic method arguments are okay pub fn append_text>(&mut self, arg: S) { self.text += arg.borrow(); } #[fluent_impl_opts(rename = "appended_text_impl_trait")] // Needless to say, impl Trait method arguments are also okay pub fn append_text_impl_trait(&mut self, arg: impl Borrow) { self.text += arg.borrow(); } } } fn main() { use m::{St, TCounter}; // ======== let mut tc1 = TCounter(St::::C_TC); let mut s1 = St::new(0u32, "".into()); s1.append_text("simple "); s1.append_text::<&str>("turbo fish "); s1.append_text_impl_trait("impl trait"); s1.add_value(5, &mut tc1); assert_eq!(s1.get_text(), "simple turbo fish impl trait"); assert_eq!(tc1, TCounter(St::::C_TC + 1)); // ======== let mut tc2 = TCounter(St::::C_TC); let s2 = St::new(0u32, "".into()) .with_appended_text("simple ") .with_appended_text::<&str>("turbo fish ") .with_appended_text_impl_trait("impl trait") .with_added_value(5, &mut tc2); assert_eq!(s2, s1); assert_eq!(tc2, tc1); } ```