# nade [![Crates.io version](https://img.shields.io/crates/v/nade.svg?style=flat-square)](https://crates.io/crates/nade) [![docs.rs docs](https://img.shields.io/badge/docs-latest-blue.svg?style=flat-square)](https://docs.rs/nade) English | [简体中文](./README-zh_cn.md) `nade` is a attribute macro that adds ***na***med and ***de***fault arguments to Rust functions. ## Usage ```rust // some_crate/src/lib.rs pub use nade::base::*; use nade::nade; pub fn one() -> u32 { 1 } #[nade] pub fn foo( /// You can add doc comments to the parameter. It will be shown in the doc of the macro. /// /// The world is 42. #[nade(42)] a: u32, /// Call a function #[nade(one())] b: u32, /// Default value of u32 #[nade] c: u32, d: u32 ) -> u32 { a + b + c + d } assert_eq!(foo!(1, 2, 3, 4), 10); // foo(1, 2, 3, 4) assert_eq!(foo!(d = 2), 45); // foo(42, one(), Default::default(), 2) assert_eq!(foo!(1, c = 2, b = 3, 4), 10); // foo(1, 3, 2, 4) ``` ## How it works If you write a function like this: ```rust // some_crate/src/lib.rs pub use nade::base::*; use nade::nade; pub fn one() -> u32 { 1 } #[nade] pub fn foo( #[nade(42)] a: u32, #[nade(one())] b: u32, #[nade] c: u32, d: u32 ) -> u32 { a + b + c + d } ``` it will be expanded to: ```rust // some_crate/src/lib.rs // ⓵ pub use nade::base::*; use nade::nade; pub fn one() -> u32 { 1 } pub fn foo(a: u32, b: u32, c: u32, d: u32) -> u32 { a + b + c + d } // ⓶ #[::nade::__internal::macro_v(pub)] macro_rules! foo { ($($arguments:tt)*) => { // ⓷ $crate::nade_helper!( ($($arguments)*) (a: u32 = 42, b: u32 = one(), c: u32 = Default::default(), d: u32) (foo) ) }; } ``` Then, when you call the macro `foo` like this: ```rust use some_crate::{foo, one}; foo!(32, d = 1, c = 2); ``` it will be expanded to: ```rust use some_crate::{foo, one}; foo(32, one(), 2, 1); ``` ### Note As you can see in [How it works](#how-it-works), there are 3 things to be aware of in the code generated by `#[nade]`. - ⓵, ⓷ `nade_helper` is a declarative macro used to generate function call expressions based on arguments, parameters, and function path. Its path defaults is `$crate::nade_helper`, so you need to import the macro in the root of crate using `pub use nade::base::*;` or `pub use nade::base::nade_helper;`. Also you can customize the path of `nade_helper`. ```rust use nade::nade; mod custom_nade_helper { pub use nade::base::nade_helper; } #[nade] #[nade_path(nade_helper = custom_nade_helper)] fn custom_nade_helper_path(a: usize) -> usize { a } ``` - ⓶ `macro_v` is an attribute macro that makes the visibility of the declarative macro the same as the function. see [macro-v](https://github.com/ZihanType/macro-v) for details. Its path defaults is `::nade::__internal::macro_v`. Also you can customize the path of `macro_v`. ```rust use nade::nade; mod custom_macro_v { pub use nade::__internal::macro_v; } #[nade] #[nade_path(macro_v = custom_macro_v)] fn custom_macro_v_path(a: usize) -> usize { a } ``` ## Limitations 1. When you call the macro `foo`, you must use the `use` statement to bring the macro into scope. ```rust // Good use some_crate::{foo, one}; foo!(32, d = 1, c = 2); // Bad use some_crate::one; some_crate::foo!(32, d = 1, c = 2); ``` Because the attribute macro `#[nade]` will generate a macro with the same name as the function, and the macro use the function in an unhygienic way, so you must use the `use` statement to bring the macro **and** the function into scope. 2. The default argument expression must be imported into the scope of the macro call. ```rust // Good use some_crate::{foo, one}; foo!(32, d = 1, c = 2); // Bad use some_crate::foo; foo!(32, d = 1, c = 2); ``` Because the default argument expression is evaluated after the `foo` macro is expanded, so it must be imported into the scope of the macro call. ## How to bypass the limitations 1. You can pass a module path starting with `$crate` for the `#[nade]` attribute macro on the function. ```rust #[nade(module_path = $crate::module)] // <--- here pub fn foo( #[nade(42)] a: u32, #[nade(one())] b: u32, #[nade] c: u32, d: u32 ) -> u32 { a + b + c + d } ``` it will be expanded to: ```rust pub fn foo(a: u32, b: u32, c: u32, d: u32) -> u32 { a + b + c + d } #[::nade::__internal::macro_v(pub)] macro_rules! foo { ($($arguments:tt)*) => { $crate::nade_helper!( ($($arguments)*) (a: u32 = 42, b: u32 = one(), c: u32 = Default::default(), d: u32) ($crate::module::foo) // <--- here ) }; } ``` Then, you can not use the `use` statement to bring the macro and the function into scope, like this: ```rust use some_crate::one; some_crate::foo!(32, d = 1, c = 2); ``` 2. In the `#[nade]` attribute macro on the parameter, you can specify the default argument expression using the full path, either `$crate::a::expr`, or `::a::b::expr`. In fact, when you use `#[nade]` on an parameter, you are using `#[nade(::core::default::Default::default())]`. ```rust pub fn one() -> u32 { 1 } pub static PATH: &str = "a"; #[nade] pub fn foo( #[nade($crate::module::one())] a: T1, #[nade(::std::path::Path::new("a"))] b: T2, #[nade($crate::module::PATH)] c: T3, #[nade("Hello")] d: T4 ) { let _ = (a, b, c, d); } ``` it will be expanded to: ```rust pub fn foo(a: T1, b: T2, c: T3, d: T4) { let _ = (a, b, c, d); } #[::nade::__internal::macro_v(pub)] macro_rules! foo { ($($arguments:tt)*) => { $crate::nade_helper!( ($($arguments)*) ( a: T1 = $crate::module::one(), b: T2 = ::std::path::Path::new("a"), c: T3 = $crate::module::PATH, d: T4 = "Hello", ) (foo) ) }; } ``` Then, you can not use the `use` statement to bring default argument expressions into scope, like this: ```rust use some_crate::foo; foo!(); ``` ## Credits This crate is inspired by these crates: - [default-args](https://github.com/buttercrab/default-args.rs) - [duang](https://github.com/xiaoniu-578fa6bff964d005/duang) - [leptos](https://github.com/leptos-rs/leptos) - [typed-builder](https://github.com/idanarye/rust-typed-builder)