dnspod-lib 0.1.11

DNSPod lib
Documentation
# `custom_meta_struct` 宏的用法

## 为什么要有这个宏

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

```rust
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), 
如果我们的宏定义如下:

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

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

## 用法

看个例子:

```rust
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 属性:

```rust
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  

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

## 实际用例

```rust
#[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`