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