Git Product home page Git Product logo

nade's Introduction

nade

Crates.io version docs.rs docs

English | 简体中文

nade is a attribute macro that adds named and default arguments to Rust functions.

Usage

// 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:

// 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:

// 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:

use some_crate::{foo, one};

foo!(32, d = 1, c = 2);

it will be expanded to:

use some_crate::{foo, one};

foo(32, one(), 2, 1);

Note

As you can see in 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.

    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 for details.

    Its path defaults is ::nade::__internal::macro_v.

    Also you can customize the path of macro_v.

    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.

    // 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.

    // 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.

    #[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:

    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:

    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())].

    pub fn one() -> u32 {
        1
    }
    
    pub static PATH: &str = "a";
    
    #[nade]
    pub fn foo<T1, T2, T3, T4>(
        #[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:

    pub fn foo<T1, T2, T3, T4>(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:

    use some_crate::foo;
    foo!();

Credits

This crate is inspired by these crates:

nade's People

Contributors

zihantype avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

Forkers

wdcodecn

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.