1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
//! 定义各个 Action 请求
//!
//! 添加具体 Action 时可以通过 `@[url = consts::DNSPOD_URL]` 覆盖掉默认公共参数。可以覆盖的还有 region 和 version
//!
#![allow(non_snake_case)]
use crate::utils::none_to_empty_string;
use crate::data_types::*;
use crate::consts;
/// for `#[serde(crate = "dnspod_lib::serde")]`
mod dnspod_lib {
pub use crate::data_types;
pub use crate::serde;
pub use crate::consts;
}
#[macro_export]
macro_rules! overloading_common_params {
(url = $expr: expr) => {
#[inline] fn url(&self) -> &'static str { $expr }
};
(version = $expr: expr) => {
#[inline] fn version(&self) -> $crate::data_types::Version { $expr }
};
(region = $expr: expr) => {
#[inline] fn region(&self) -> Option<$crate::data_types::Region> { Some($expr) }
};
($($tt: tt)*) => {
compile_error!("This macro only accepts `url` `region` `version`");
};
}
#[macro_export]
macro_rules! impl_define_action_list {
(
$(
$(#[$meta: meta])*
$(@[$($my_meta: tt)*])*
$vis: vis struct $name: ident $body: tt
)*
) => {
$(
$(#[$meta])*
$vis struct $name $body
const _: () = {
use $crate::ExtractCommonParams;
use $crate::serde_json;
impl ExtractCommonParams for $name {
#[inline] fn action(&self) -> &'static str { stringify!($name) }
#[inline] fn body(&self) -> Vec<u8> { serde_json::to_vec(self).unwrap() }
$(
$crate::overloading_common_params! { $($my_meta)* }
)*
}
};
)*
};
(
$(#[$enum_meta: meta])*
$enum_vis: vis enum $enum_name: ident {}
,
$(
$(#[$meta: meta])*
$(@[$($my_meta: tt)*])*
$vis: vis struct $name: ident $body: tt
)*
) => {
$crate::impl_define_action_list! {
$(
$(#[$meta])*
$(@[$($my_meta)*])*
$vis struct $name $body
)*
}
$(
impl From<$name> for $enum_name {
#[inline] fn from(v: $name) -> Self { Self::$name(v) }
}
)*
$(#[$enum_meta])*
$enum_vis enum $enum_name {
$($name($name),)*
}
impl $crate::ExtractCommonParams for $enum_name {
#[inline]
fn action(&self) -> &'static str {
#[allow(unreachable_patterns)]
match self {
$(Self::$name(v) => v.action(),)*
_ => Default::default(),
}
}
#[inline]
fn body(&self) -> Vec<u8> {
#[allow(unreachable_patterns)]
match self {
$(Self::$name(v) => v.body(), )*
_ => Default::default(),
}
}
#[inline]
fn url(&self) -> &'static str {
#[allow(unreachable_patterns)]
match self {
$(Self::$name(v) => v.url(), )*
_ => Default::default(),
}
}
#[inline]
fn version(&self) -> $crate::data_types::Version {
#[allow(unreachable_patterns)]
match self {
$(Self::$name(v) => v.version(), )*
_ => Default::default(),
}
}
#[inline]
fn region(&self) -> Option<$crate::data_types::Region> {
#[allow(unreachable_patterns)]
match self {
$(Self::$name(v) => v.region(), )*
_ => Default::default(),
}
}
}
};
}
/// 供外部 crate 调用
#[macro_export]
macro_rules! define_action_list {
(
$($tt: tt)*
) => {
$crate::custom_meta_struct! {
(
// callback macro
$crate::impl_define_action_list,
// common metas
#[derive(Debug, Clone, $crate::serde::Serialize, $crate::serde::Deserialize)]
#[serde(crate = "dnspod_lib::serde")]
),
$($tt)*
}
};
}
macro_rules! define_action_list_private {
(
$($tt: tt)*
) => {
$crate::impl_define_action_list! {
#[derive(Debug, Clone)]
#[cfg_attr(feature = "clap", derive(clap::Subcommand))]
#[allow(non_snake_case)]
pub enum Action {}
,
$($tt)*
}
};
}
crate::custom_meta_struct! {
(
define_action_list_private, // callback macro
// 公共 meta attribute, 赋给每个 struct
#[cfg_attr(feature = "clap", derive(clap::Parser))]
#[derive(Debug, Clone, crate::serde::Serialize, crate::serde::Deserialize)]
#[serde(crate = "dnspod_lib::serde")]
),
/// 获取域名列表
/// <https://cloud.tencent.com/document/api/1427/56172>
@[url = consts::DNSPOD_URL]
@[version = Version::Version2021_03_23]
pub struct DescribeDomainList {
/// 域名分组类型,默认为ALL
#[cfg_attr(feature = "clap", arg(long, value_enum, default_value_t=Default::default()))]
pub Type: DomainType,
/// 记录开始的偏移, 第一条记录为 0, 依次类推。默认值为0。
/// 示例值:0
#[cfg_attr(feature = "clap", arg(long, default_value_t=0))]
pub Offset: Integer,
/// 要获取的域名数量, 比如获取20个, 则为20。默认值为3000。
/// 示例值:20
#[cfg_attr(feature = "clap", arg(long, default_value_t=3000))]
pub Limit: Integer,
/// 分组ID, 第一个组为 0, 获取指定分组的域名
/// 示例值:1
#[cfg_attr(feature = "clap", arg(long, default_value_t=0))]
pub GroupId: Integer,
/// 根据关键字搜索域名
/// 示例值:qq
#[cfg_attr(feature = "clap", arg(long, default_value=""))]
pub Keyword: Option<String>,
}
/// 添加域名 <https://cloud.tencent.com/document/api/1427/56184>
pub struct CreateDomain {
Domain: String,
}
/// 删除域名 <https://cloud.tencent.com/document/api/1427/56178>
pub struct DeleteDomain {
Domain: String,
}
/// 添加记录 <https://cloud.tencent.com/document/api/1427/56180>
pub struct CreateRecord {
/// 域名
/// 示例值:dnspod.cn
#[cfg_attr(feature = "clap", arg(long))]
pub Domain: String,
/// 主机记录,如 www,如果不传,默认为 @。
/// 示例值:www
#[cfg_attr(feature = "clap", arg(long, default_value = "@"))]
pub SubDomain: String,
/// 记录类型,通过 API 记录类型获得,大写英文,比如:A 。
/// 示例值:A
#[cfg_attr(feature = "clap", arg(long, value_enum, rename_all = "UPPER"))]
pub RecordType: RecordType,
/// 记录线路,通过 API 记录线路获得,中文,比如:默认。
/// 示例值:默认
#[cfg_attr(feature = "clap", arg(long, value_enum, default_value_t=Default::default()))]
pub RecordLine: RecordLine,
/// 记录值,如 IP : 200.200.200.200, CNAME : cname.dnspod.com., MX : mail.dnspod.com.。
/// 示例值:200.200.200.200
#[cfg_attr(feature = "clap", arg(long))]
pub Value: String,
}
/// 删除记录
/// <https://cloud.tencent.com/document/api/1427/56176>
pub struct DeleteRecord {
/// 域名
/// 示例值:dnspod.cn
#[cfg_attr(feature = "clap", arg(long))]
pub Domain: String,
/// 记录 ID 。可以通过接口 [DescribeRecordList] 查到所有的解析记录列表以及对应的 RecordId
/// 示例值:162
#[cfg_attr(feature = "clap", arg(long))]
pub RecordId: u64,
}
/// 获取域名的解析记录列表
/// <https://cloud.tencent.com/document/api/1427/56166>
pub struct DescribeRecordList {
/// 要获取的解析记录所属的域名
/// 示例值:example.com
#[cfg_attr(feature = "clap", arg(long))]
pub Domain: String,
/// 解析记录的主机头,如果传了此参数,则只会返回此主机头对应的解析记录
/// 示例值:www
#[serde(serialize_with = "none_to_empty_string")]
#[cfg_attr(feature = "clap", arg(long, default_value=""))]
pub Subdomain: Option<String>,
/// 通过关键字搜索解析记录,当前支持搜索主机头和记录值
/// 示例值:book
#[serde(skip_serializing_if = "Option::is_none")]
#[cfg_attr(feature = "clap", arg(long, default_value=""))]
pub Keyword: Option<String>,
}
/// 获取记录信息
/// <https://cloud.tencent.com/document/api/1427/56168>
pub struct DescribeRecord {
/// 域名
/// 示例值:dnspod.cn
#[cfg_attr(feature = "clap", arg(long))]
pub Domain: String,
/// 记录 ID 。可以通过接口 [DescribeRecordList] 查到所有的解析记录列表以及对应的 RecordId
/// 示例值:162
#[cfg_attr(feature = "clap", arg(long))]
pub RecordId: u64,
}
/// 获取等级允许的记录类型 <https://cloud.tencent.com/document/api/1427/56165>
pub struct DescribeRecordType {
/// 域名等级。
///
/// + 旧套餐:D_FREE、D_PLUS、D_EXTRA、D_EXPERT、D_ULTRA 分别对应免费套餐、个人豪华、企业1、企业2、企业3。
///
/// + 新套餐:DP_FREE、DP_PLUS、DP_EXTRA、DP_EXPERT、DP_ULTRA 分别对应新免费、个人专业版、企业创业版、企业标准版、企业旗舰版。
///
/// 示例值:DP_Plus
#[cfg_attr(feature = "clap", arg(long))]
pub DomainGrade: DomainGrade,
}
/// 获取等级允许的线路 <https://cloud.tencent.com/document/api/1427/56167>
pub struct DescribeRecordLineList {
/// 域名。
/// 示例值:dnspod.cn
pub Domain: String,
#[cfg_attr(feature = "clap", arg(long))]
/// 域名等级
pub DomainGrade: DomainGrade,
}
/// 更新动态 DNS 记录
/// <https://cloud.tencent.com/document/api/1427/56158>
pub struct ModifyDynamicDNS {
/// 域名
/// 示例值:dnspod.cn
#[cfg_attr(feature = "clap", arg(long))]
pub Domain: String,
/// 主机记录,如 www,如果不传,默认为 @。
/// 示例值:www
#[cfg_attr(feature = "clap", arg(long, default_value="@"))]
pub SubDomain: String,
/// 记录 ID 。可以通过接口 [DescribeRecordList] 查到所有的解析记录列表以及对应的 RecordId
/// 示例值:162
#[cfg_attr(feature = "clap", arg(long))]
pub RecordId: u64,
/// 记录线路,通过 API 记录线路获得,中文,比如:默认。
/// 示例值:默认
#[cfg_attr(feature = "clap", arg(value_enum, default_value_t=Default::default()))]
pub RecordLine: RecordLine,
/// 记录值,如 IP : 200.200.200.200, CNAME : cname.dnspod.com., MX : mail.dnspod.com.。
/// 示例值:200.200.200.200
#[cfg_attr(feature = "clap", arg(long))]
pub Value: String,
/// TTL值,如果不传,默认为域名的TTL值。
/// 示例值:600
#[cfg_attr(feature = "clap", arg(long, default_value_t=600))]
pub Ttl: Integer,
}
/// 修改记录
/// <https://cloud.tencent.com/document/api/1427/56157>
pub struct ModifyRecord {
/// 域名
/// 示例值:dnspod.cn
#[cfg_attr(feature = "clap", arg(long))]
pub Domain: String,
/// 主机记录,如 www,如果不传,默认为 @。
/// 示例值:www
#[cfg_attr(feature = "clap", arg(long, default_value="@"))]
pub SubDomain: String,
/// 记录 ID 。可以通过接口 [DescribeRecordList] 查到所有的解析记录列表以及对应的 RecordId
/// 示例值:162
#[cfg_attr(feature = "clap", arg(long))]
pub RecordId: u64,
/// 记录类型,通过 API 记录类型获得,大写英文,比如:A 。
/// 示例值:A
#[cfg_attr(feature = "clap", arg(long))]
pub RecordType: RecordType,
/// 记录线路,通过 API 记录线路获得,中文,比如:默认。
/// 示例值:默认
#[cfg_attr(feature = "clap", arg(long, value_enum, default_value_t=Default::default()))]
pub RecordLine: RecordLine,
/// 记录值,如 IP : 200.200.200.200, CNAME : cname.dnspod.com., MX : mail.dnspod.com.。
/// 示例值:200.200.200.200
#[cfg_attr(feature = "clap", arg(long))]
pub Value: String,
}
}
#[cfg(test)]
mod tests {
use super::*;
super::super::define_action_list! {}
crate::define_action_list! {
/// fuckme
@[url = "https://hangj.cnblogs.com"]
struct A;
/// hey
struct B;
}
crate::define_action_list! {
/// 获取域名信息
/// <https://cloud.tencent.com/document/api/1427/56173>
@[url = "https://example.com"] // 公共参数可以重载 url, version, region
pub struct DescribeDomain {
/// 域名分组类型,默认为ALL
#[serde(rename = "Domain")]
pub domain: String,
}
@[version = dnspod_lib::data_types::Version::Version2021_03_23]
#[allow(non_snake_case)]
pub struct CustomAction {
/// 域名分组类型,默认为ALL
pub Domain: String,
}
}
}