tencent_sdk/services/
vpc.rs

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