tencent_sdk/types/
vpc.rs

1use crate::{
2    Error,
3    client::endpoint::Endpoint,
4    types::{DomainName, Filter, Region, RequestId, SubnetId, Tag, VpcId},
5};
6use serde::{Deserialize, Serialize};
7use serde_json::Value;
8use std::collections::HashMap;
9
10#[derive(Debug, Deserialize)]
11pub struct DescribeVpcsResponse {
12    #[serde(rename = "Response")]
13    pub response: DescribeVpcsResult,
14}
15
16#[derive(Debug, Deserialize)]
17pub struct DescribeVpcsResult {
18    #[serde(rename = "TotalCount")]
19    pub total_count: Option<u64>,
20    #[serde(rename = "VpcSet")]
21    #[serde(default)]
22    pub vpc_set: Vec<VpcSummary>,
23    #[serde(rename = "RequestId")]
24    pub request_id: RequestId,
25}
26
27#[derive(Debug, Deserialize)]
28pub struct VpcSummary {
29    #[serde(rename = "VpcId")]
30    pub vpc_id: Option<VpcId>,
31    #[serde(rename = "VpcName")]
32    pub vpc_name: Option<String>,
33    #[serde(rename = "CidrBlock")]
34    pub cidr_block: Option<String>,
35    #[serde(rename = "IsDefault")]
36    pub is_default: Option<bool>,
37    #[serde(rename = "EnableMulticast")]
38    pub enable_multicast: Option<bool>,
39    #[serde(rename = "TagSet")]
40    pub tag_set: Option<Vec<ResourceTag>>,
41    #[serde(rename = "CreatedTime")]
42    pub created_time: Option<String>,
43    #[serde(rename = "VpcIdString")]
44    pub vpc_id_string: Option<String>,
45    #[serde(default)]
46    pub ipv6_cidr_block: Option<String>,
47    #[serde(flatten, default)]
48    pub extra: HashMap<String, Value>,
49}
50
51#[derive(Debug, Deserialize)]
52pub struct ResourceTag {
53    #[serde(rename = "Key")]
54    pub key: Option<String>,
55    #[serde(rename = "Value")]
56    pub value: Option<String>,
57}
58
59#[derive(Serialize)]
60#[serde(rename_all = "PascalCase")]
61struct DescribeVpcsPayload<'a> {
62    #[serde(skip_serializing_if = "Option::is_none")]
63    vpc_ids: Option<&'a [VpcId]>,
64    #[serde(skip_serializing_if = "Option::is_none")]
65    filters: Option<&'a [Filter]>,
66    #[serde(skip_serializing_if = "Option::is_none")]
67    limit: Option<u32>,
68    #[serde(skip_serializing_if = "Option::is_none")]
69    offset: Option<u32>,
70}
71
72pub struct DescribeVpcsRequest {
73    region: Option<Region>,
74    filters: Vec<Filter>,
75    vpc_ids: Vec<VpcId>,
76    limit: Option<u32>,
77    offset: Option<u32>,
78}
79
80impl Default for DescribeVpcsRequest {
81    fn default() -> Self {
82        Self::new()
83    }
84}
85
86impl DescribeVpcsRequest {
87    pub fn new() -> Self {
88        Self {
89            region: None,
90            filters: Vec::new(),
91            vpc_ids: Vec::new(),
92            limit: None,
93            offset: None,
94        }
95    }
96
97    pub fn region(mut self, region: impl Into<Region>) -> Self {
98        self.region = Some(region.into());
99        self
100    }
101
102    pub fn push_filter(mut self, filter: Filter) -> Self {
103        self.filters.push(filter);
104        self
105    }
106
107    pub fn push_vpc_id(mut self, vpc_id: impl Into<VpcId>) -> Self {
108        self.vpc_ids.push(vpc_id.into());
109        self
110    }
111
112    pub fn limit(mut self, limit: u32) -> Self {
113        self.limit = Some(limit);
114        self
115    }
116
117    pub fn offset(mut self, offset: u32) -> Self {
118        self.offset = Some(offset);
119        self
120    }
121}
122
123impl Endpoint for DescribeVpcsRequest {
124    type Output = DescribeVpcsResponse;
125
126    fn service(&self) -> &'static str {
127        "vpc"
128    }
129
130    fn action(&self) -> &'static str {
131        "DescribeVpcs"
132    }
133
134    fn version(&self) -> &'static str {
135        "2017-03-12"
136    }
137
138    fn region(&self) -> Option<&Region> {
139        self.region.as_ref()
140    }
141
142    fn payload(&self) -> Result<Option<Value>, Error> {
143        let vpc_ids = (!self.vpc_ids.is_empty()).then_some(self.vpc_ids.as_slice());
144        let filters = (!self.filters.is_empty()).then_some(self.filters.as_slice());
145        let payload = DescribeVpcsPayload {
146            vpc_ids,
147            filters,
148            limit: self.limit,
149            offset: self.offset,
150        };
151
152        let value = serde_json::to_value(payload).map_err(|source| {
153            Error::invalid_request_with_source(
154                "failed to serialize DescribeVpcs request payload",
155                Box::new(source),
156            )
157        })?;
158        Ok(Some(value))
159    }
160}
161
162#[derive(Debug, Deserialize)]
163pub struct CreateVpcResponse {
164    #[serde(rename = "Response")]
165    pub response: CreateVpcResult,
166}
167
168#[derive(Debug, Deserialize)]
169pub struct CreateVpcResult {
170    #[serde(rename = "Vpc")]
171    pub vpc: Option<VpcSummary>,
172    #[serde(rename = "RequestId")]
173    pub request_id: RequestId,
174}
175
176#[derive(Serialize)]
177#[serde(rename_all = "PascalCase")]
178struct CreateVpcPayload<'a> {
179    vpc_name: &'a str,
180    cidr_block: &'a str,
181    #[serde(skip_serializing_if = "Option::is_none")]
182    enable_multicast: Option<bool>,
183    #[serde(skip_serializing_if = "Option::is_none")]
184    dns_servers: Option<&'a [String]>,
185    #[serde(skip_serializing_if = "Option::is_none")]
186    domain_name: Option<&'a DomainName>,
187    #[serde(skip_serializing_if = "Option::is_none")]
188    tags: Option<&'a [Tag]>,
189}
190
191pub struct CreateVpcRequest {
192    region: Option<Region>,
193    vpc_name: String,
194    cidr_block: String,
195    enable_multicast: Option<bool>,
196    dns_servers: Vec<String>,
197    domain_name: Option<DomainName>,
198    tags: Vec<Tag>,
199}
200
201impl CreateVpcRequest {
202    pub fn new(vpc_name: impl Into<String>, cidr_block: impl Into<String>) -> Self {
203        Self {
204            region: None,
205            vpc_name: vpc_name.into(),
206            cidr_block: cidr_block.into(),
207            enable_multicast: None,
208            dns_servers: Vec::new(),
209            domain_name: None,
210            tags: Vec::new(),
211        }
212    }
213
214    pub fn region(mut self, region: impl Into<Region>) -> Self {
215        self.region = Some(region.into());
216        self
217    }
218
219    pub fn enable_multicast(mut self, enabled: bool) -> Self {
220        self.enable_multicast = Some(enabled);
221        self
222    }
223
224    pub fn dns_servers<I, S>(mut self, dns_servers: I) -> Self
225    where
226        I: IntoIterator<Item = S>,
227        S: Into<String>,
228    {
229        self.dns_servers = dns_servers.into_iter().map(Into::into).collect();
230        self
231    }
232
233    pub fn domain_name(mut self, domain_name: impl Into<DomainName>) -> Self {
234        self.domain_name = Some(domain_name.into());
235        self
236    }
237
238    pub fn push_tag(mut self, tag: Tag) -> Self {
239        self.tags.push(tag);
240        self
241    }
242}
243
244impl Endpoint for CreateVpcRequest {
245    type Output = CreateVpcResponse;
246
247    fn service(&self) -> &'static str {
248        "vpc"
249    }
250
251    fn action(&self) -> &'static str {
252        "CreateVpc"
253    }
254
255    fn version(&self) -> &'static str {
256        "2017-03-12"
257    }
258
259    fn region(&self) -> Option<&Region> {
260        self.region.as_ref()
261    }
262
263    fn payload(&self) -> Result<Option<Value>, Error> {
264        let dns_servers = (!self.dns_servers.is_empty()).then_some(self.dns_servers.as_slice());
265        let tags = (!self.tags.is_empty()).then_some(self.tags.as_slice());
266        let payload = CreateVpcPayload {
267            vpc_name: &self.vpc_name,
268            cidr_block: &self.cidr_block,
269            enable_multicast: self.enable_multicast,
270            dns_servers,
271            domain_name: self.domain_name.as_ref(),
272            tags,
273        };
274
275        let value = serde_json::to_value(payload).map_err(|source| {
276            Error::invalid_request_with_source(
277                "failed to serialize CreateVpc request payload",
278                Box::new(source),
279            )
280        })?;
281        Ok(Some(value))
282    }
283}
284
285#[derive(Debug, Deserialize)]
286pub struct CreateSubnetResponse {
287    #[serde(rename = "Response")]
288    pub response: CreateSubnetResult,
289}
290
291#[derive(Debug, Deserialize)]
292pub struct CreateSubnetResult {
293    #[serde(rename = "Subnet")]
294    pub subnet: Option<SubnetSummary>,
295    #[serde(rename = "RequestId")]
296    pub request_id: RequestId,
297}
298
299#[derive(Debug, Deserialize)]
300pub struct SubnetSummary {
301    #[serde(rename = "SubnetId")]
302    pub subnet_id: Option<SubnetId>,
303    #[serde(rename = "SubnetName")]
304    pub subnet_name: Option<String>,
305    #[serde(rename = "CidrBlock")]
306    pub cidr_block: Option<String>,
307    #[serde(rename = "IsDefault")]
308    pub is_default: Option<bool>,
309    #[serde(rename = "Zone")]
310    pub zone: Option<String>,
311    #[serde(flatten, default)]
312    pub extra: HashMap<String, Value>,
313}
314
315#[derive(Serialize)]
316#[serde(rename_all = "PascalCase")]
317struct CreateSubnetPayload<'a> {
318    vpc_id: &'a VpcId,
319    subnet_name: &'a str,
320    cidr_block: &'a str,
321    zone: &'a str,
322    #[serde(skip_serializing_if = "Option::is_none")]
323    is_default: Option<bool>,
324    #[serde(skip_serializing_if = "Option::is_none")]
325    tags: Option<&'a [Tag]>,
326}
327
328pub struct CreateSubnetRequest {
329    region: Option<Region>,
330    vpc_id: VpcId,
331    subnet_name: String,
332    cidr_block: String,
333    zone: String,
334    is_default: Option<bool>,
335    tags: Vec<Tag>,
336}
337
338impl CreateSubnetRequest {
339    pub fn new(
340        vpc_id: impl Into<VpcId>,
341        subnet_name: impl Into<String>,
342        cidr_block: impl Into<String>,
343        zone: impl Into<String>,
344    ) -> Self {
345        Self {
346            region: None,
347            vpc_id: vpc_id.into(),
348            subnet_name: subnet_name.into(),
349            cidr_block: cidr_block.into(),
350            zone: zone.into(),
351            is_default: None,
352            tags: Vec::new(),
353        }
354    }
355
356    pub fn region(mut self, region: impl Into<Region>) -> Self {
357        self.region = Some(region.into());
358        self
359    }
360
361    pub fn is_default(mut self, is_default: bool) -> Self {
362        self.is_default = Some(is_default);
363        self
364    }
365
366    pub fn push_tag(mut self, tag: Tag) -> Self {
367        self.tags.push(tag);
368        self
369    }
370}
371
372impl Endpoint for CreateSubnetRequest {
373    type Output = CreateSubnetResponse;
374
375    fn service(&self) -> &'static str {
376        "vpc"
377    }
378
379    fn action(&self) -> &'static str {
380        "CreateSubnet"
381    }
382
383    fn version(&self) -> &'static str {
384        "2017-03-12"
385    }
386
387    fn region(&self) -> Option<&Region> {
388        self.region.as_ref()
389    }
390
391    fn payload(&self) -> Result<Option<Value>, Error> {
392        let tags = (!self.tags.is_empty()).then_some(self.tags.as_slice());
393        let payload = CreateSubnetPayload {
394            vpc_id: &self.vpc_id,
395            subnet_name: &self.subnet_name,
396            cidr_block: &self.cidr_block,
397            zone: &self.zone,
398            is_default: self.is_default,
399            tags,
400        };
401
402        let value = serde_json::to_value(payload).map_err(|source| {
403            Error::invalid_request_with_source(
404                "failed to serialize CreateSubnet request payload",
405                Box::new(source),
406            )
407        })?;
408        Ok(Some(value))
409    }
410}
411
412#[derive(Debug, Deserialize)]
413pub struct DescribeSubnetsResponse {
414    #[serde(rename = "Response")]
415    pub response: DescribeSubnetsResult,
416}
417
418#[derive(Debug, Deserialize)]
419pub struct DescribeSubnetsResult {
420    #[serde(rename = "TotalCount")]
421    pub total_count: Option<u64>,
422    #[serde(rename = "SubnetSet")]
423    #[serde(default)]
424    pub subnet_set: Vec<SubnetSummary>,
425    #[serde(rename = "RequestId")]
426    pub request_id: RequestId,
427}
428
429#[derive(Serialize)]
430#[serde(rename_all = "PascalCase")]
431struct DescribeSubnetsPayload<'a> {
432    #[serde(skip_serializing_if = "Option::is_none")]
433    subnet_ids: Option<&'a [SubnetId]>,
434    #[serde(skip_serializing_if = "Option::is_none")]
435    filters: Option<&'a [Filter]>,
436    #[serde(skip_serializing_if = "Option::is_none")]
437    vpc_id: Option<&'a VpcId>,
438    #[serde(skip_serializing_if = "Option::is_none")]
439    limit: Option<u32>,
440    #[serde(skip_serializing_if = "Option::is_none")]
441    offset: Option<u32>,
442}
443
444pub struct DescribeSubnetsRequest {
445    region: Option<Region>,
446    filters: Vec<Filter>,
447    subnet_ids: Vec<SubnetId>,
448    vpc_id: Option<VpcId>,
449    limit: Option<u32>,
450    offset: Option<u32>,
451}
452
453impl Default for DescribeSubnetsRequest {
454    fn default() -> Self {
455        Self::new()
456    }
457}
458
459impl DescribeSubnetsRequest {
460    pub fn new() -> Self {
461        Self {
462            region: None,
463            filters: Vec::new(),
464            subnet_ids: Vec::new(),
465            vpc_id: None,
466            limit: None,
467            offset: None,
468        }
469    }
470
471    pub fn region(mut self, region: impl Into<Region>) -> Self {
472        self.region = Some(region.into());
473        self
474    }
475
476    pub fn push_filter(mut self, filter: Filter) -> Self {
477        self.filters.push(filter);
478        self
479    }
480
481    pub fn push_subnet_id(mut self, subnet_id: impl Into<SubnetId>) -> Self {
482        self.subnet_ids.push(subnet_id.into());
483        self
484    }
485
486    pub fn vpc_id(mut self, vpc_id: impl Into<VpcId>) -> Self {
487        self.vpc_id = Some(vpc_id.into());
488        self
489    }
490
491    pub fn limit(mut self, limit: u32) -> Self {
492        self.limit = Some(limit);
493        self
494    }
495
496    pub fn offset(mut self, offset: u32) -> Self {
497        self.offset = Some(offset);
498        self
499    }
500}
501
502impl Endpoint for DescribeSubnetsRequest {
503    type Output = DescribeSubnetsResponse;
504
505    fn service(&self) -> &'static str {
506        "vpc"
507    }
508
509    fn action(&self) -> &'static str {
510        "DescribeSubnets"
511    }
512
513    fn version(&self) -> &'static str {
514        "2017-03-12"
515    }
516
517    fn region(&self) -> Option<&Region> {
518        self.region.as_ref()
519    }
520
521    fn payload(&self) -> Result<Option<Value>, Error> {
522        let subnet_ids = (!self.subnet_ids.is_empty()).then_some(self.subnet_ids.as_slice());
523        let filters = (!self.filters.is_empty()).then_some(self.filters.as_slice());
524        let payload = DescribeSubnetsPayload {
525            subnet_ids,
526            filters,
527            vpc_id: self.vpc_id.as_ref(),
528            limit: self.limit,
529            offset: self.offset,
530        };
531
532        let value = serde_json::to_value(payload).map_err(|source| {
533            Error::invalid_request_with_source(
534                "failed to serialize DescribeSubnets request payload",
535                Box::new(source),
536            )
537        })?;
538        Ok(Some(value))
539    }
540}
541
542#[cfg(test)]
543mod tests {
544    use super::*;
545    use serde_json::json;
546
547    #[test]
548    fn describe_vpcs_payload_supports_filters() {
549        let request = DescribeVpcsRequest::new()
550            .region("ap-guangzhou")
551            .push_vpc_id("vpc-123")
552            .push_vpc_id("vpc-456")
553            .push_filter(Filter::new("vpc-name", ["prod"]))
554            .limit(50)
555            .offset(10);
556
557        let payload = request.payload().unwrap().unwrap();
558        assert_eq!(payload["VpcIds"], json!(["vpc-123", "vpc-456"]));
559        assert_eq!(payload["Filters"][0]["Name"], json!("vpc-name"));
560        assert_eq!(payload["Filters"][0]["Values"], json!(["prod"]));
561        assert_eq!(payload["Limit"], json!(50));
562        assert_eq!(payload["Offset"], json!(10));
563    }
564
565    #[test]
566    fn create_vpc_payload_includes_options() {
567        let request = CreateVpcRequest::new("demo", "10.0.0.0/16")
568            .region("ap-guangzhou")
569            .enable_multicast(true)
570            .dns_servers(["1.1.1.1", "8.8.8.8"])
571            .domain_name("local")
572            .push_tag(Tag::new("env", "test"));
573
574        let payload = request.payload().unwrap().unwrap();
575        assert_eq!(payload["VpcName"], json!("demo"));
576        assert_eq!(payload["CidrBlock"], json!("10.0.0.0/16"));
577        assert_eq!(payload["EnableMulticast"], json!(true));
578        assert_eq!(payload["DnsServers"], json!(["1.1.1.1", "8.8.8.8"]));
579        assert_eq!(payload["DomainName"], json!("local"));
580        assert_eq!(payload["Tags"], json!([{"Key": "env", "Value": "test"}]));
581    }
582
583    #[test]
584    fn create_subnet_payload_includes_tags() {
585        let request =
586            CreateSubnetRequest::new("vpc-123", "subnet-1", "10.0.1.0/24", "ap-beijing-1")
587                .region("ap-beijing")
588                .is_default(false)
589                .push_tag(Tag::new("team", "core"));
590
591        let payload = request.payload().unwrap().unwrap();
592        assert_eq!(payload["VpcId"], json!("vpc-123"));
593        assert_eq!(payload["SubnetName"], json!("subnet-1"));
594        assert_eq!(payload["CidrBlock"], json!("10.0.1.0/24"));
595        assert_eq!(payload["Zone"], json!("ap-beijing-1"));
596        assert_eq!(payload["IsDefault"], json!(false));
597        assert_eq!(payload["Tags"], json!([{"Key": "team", "Value": "core"}]));
598    }
599
600    #[test]
601    fn deserialize_create_vpc_response() {
602        let payload = r#"{
603            "Response": {
604                "Vpc": { "VpcId": "vpc-abc" },
605                "RequestId": "req-123"
606            }
607        }"#;
608        let parsed: CreateVpcResponse = serde_json::from_str(payload).unwrap();
609        assert_eq!(parsed.response.request_id.as_str(), "req-123");
610        assert_eq!(
611            parsed
612                .response
613                .vpc
614                .unwrap()
615                .vpc_id
616                .as_ref()
617                .map(VpcId::as_str),
618            Some("vpc-abc")
619        );
620    }
621
622    #[test]
623    fn deserialize_create_subnet_response() {
624        let payload = r#"{
625            "Response": {
626                "Subnet": { "SubnetId": "subnet-xyz" },
627                "RequestId": "req-456"
628            }
629        }"#;
630        let parsed: CreateSubnetResponse = serde_json::from_str(payload).unwrap();
631        assert_eq!(parsed.response.request_id.as_str(), "req-456");
632        assert_eq!(
633            parsed
634                .response
635                .subnet
636                .unwrap()
637                .subnet_id
638                .as_ref()
639                .map(SubnetId::as_str),
640            Some("subnet-xyz")
641        );
642    }
643
644    #[test]
645    fn describe_subnets_payload_contains_vpc_id() {
646        let request = DescribeSubnetsRequest::new()
647            .region("ap-hongkong")
648            .push_subnet_id("subnet-aaa")
649            .vpc_id("vpc-zzz");
650
651        let payload = request.payload().unwrap().unwrap();
652        assert_eq!(payload["SubnetIds"], json!(["subnet-aaa"]));
653        assert_eq!(payload["VpcId"], json!("vpc-zzz"));
654    }
655
656    #[test]
657    fn describe_subnets_builder_eases_filters() {
658        let request = DescribeSubnetsRequest::new()
659            .region("ap-hongkong")
660            .push_filter(Filter::new("subnet-name", ["blue"]))
661            .push_subnet_id("subnet-aaa")
662            .vpc_id("vpc-zzz");
663
664        assert_eq!(
665            request.region.as_ref().map(Region::as_str),
666            Some("ap-hongkong")
667        );
668        assert_eq!(request.filters[0].name, "subnet-name");
669        assert_eq!(request.subnet_ids[0].as_str(), "subnet-aaa");
670        assert_eq!(request.vpc_id.as_ref().map(VpcId::as_str), Some("vpc-zzz"));
671    }
672}