custom_meta_struct

Macro custom_meta_struct 

Source
macro_rules! custom_meta_struct {
    ($($tt: tt)*) => { ... };
}
Expand description

混编 #[..]@[..] 整流成 #[..] 在前, @[..] 在后

§custom_meta_struct 宏的用法

§为什么要有这个宏

dnspod 的每个请求(action)的参数都不太一样, 但公共参数(url, version, region 等)又基本都相同, 所以我需要一个宏来帮助我完成下面的任务

use serde::Serialize;
use serde::Deserialize;

macro_rules! my_macro {
    ($($tt: tt)*) => {};
}

trait SomeCommonTrait {}

my_macro! {
    struct ActionA {...}
    
    #[cfg_attr(feature = "clap", arg(long, value_enum, default_value_t=Default::default()))]
    struct ActionB {...}

    @[url = "https://hangj.cnblogs.com"]
    #[cfg(feature = "RustHub")]
    @[version = Version::Version2021_03_23]
    struct ActionC {...}
}

// 自动展开为:
#[derive(Debug, Clone, Serialize, Deserialize)]
struct ActionA {}

impl SomeCommonTrait for ActionA {}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "clap", arg(long, value_enum, default_value_t=Default::default()))]
struct ActionB {}

impl SomeCommonTrait for ActionB {}

#[cfg(feature = "RustHub")]
#[derive(Debug, Clone, Serialize, Deserialize)]
struct ActionC {}

其中 @[..] 为我的自定义属性, 用来重载个别请求(action)的公共参数(url, version, region), 如果我们的宏定义如下:

macro_rules! define_structs {
    (
        $(
            $(#[$meta: meta])*
            $(@[$my_meta: meta])*
            $vis: vis struct $name: ident $body: tt
        )*
    ) => {
        // ...
    };
}

你会发现它并不能匹配到 @[..]#[..] 前面的情况, 更不要说把两者混在一起的情况。 而 custom_meta_struct 让我可以在定义 struct 时, 不需要考虑 #[..]@[..] 的先后顺序, custom_meta_struct 最终会把所有的输入整流成 #[..] 在前, @[..] 在后的格式, 然后传递给「回调宏」

§用法

看个例子:

macro_rules! define_structs {
    (
        $(
            $(#[$meta: meta])*
            $(@[$my_meta: meta])*
            $vis: vis struct $name: ident $body: tt
        )*
    ) => {
        // ...
    };
}

// 通过 custom_meta_struct 的预处理, define_structs 就可以很好的接收原来无法匹配的内容了
dnspod_lib::custom_meta_struct! {
    define_structs,

    struct A;
    struct B;
    // ...
}

如果还想对所有 struct 做一些其它统一的动作, 则可以这样传如公共 meta 属性:

macro_rules! define_structs {
    (
        $(
            $(#[$meta: meta])*
            $(@[$my_meta: meta])*
            $vis: vis struct $name: ident $body: tt
        )*
    ) => {};
}

dnspod_lib::custom_meta_struct! {
    (
        // callback macro
        define_structs,
        // common metas for every struct
        #[derive(Debug)]
        @[url = "https://example.com"]
        #[derive(Clone)]
    ),

    struct A;
    struct B;
    // ...
}

如果你不需要 @[..], 只想添加一些公共的 #[..], 则不需要配置 callback macro

dnspod_lib::custom_meta_struct! {
    (
        #[derive(Debug)]
        #[derive(Clone)]
    ),
    struct A;
    struct B;
}

§实际用例

#[macro_export]
macro_rules! impl_macro {
    (
        $(
            $(#[$meta: meta])*
            $(@[$my_meta: meta])*
            $vis: vis struct $name: ident $body: tt
        )*
    ) => {
        // ...
    };
}

#[macro_export]
macro_rules! public_macro {
    ($($tt: tt)*) => {
        dnspod_lib::custom_meta_struct! {
            $crate::impl_macro,
            $($tt)*
        }
    }
}

// 外部 crate 调用 public_macro 即可

具体可以查看 action.rs