tencent_sdk/services/
cvm.rs

1use crate::{
2    client::{TencentCloudAsync, TencentCloudBlocking},
3    core::{Endpoint, TencentCloudResult},
4    services::Filter,
5};
6use serde::{Deserialize, Serialize};
7use serde_json::{json, Value};
8use std::borrow::Cow;
9use std::collections::HashMap;
10
11#[derive(Debug, Deserialize)]
12pub struct DescribeInstancesResponse {
13    #[serde(rename = "Response")]
14    pub response: DescribeInstancesResult,
15}
16
17#[derive(Debug, Deserialize)]
18pub struct DescribeInstancesResult {
19    #[serde(rename = "TotalCount")]
20    pub total_count: Option<u64>,
21    #[serde(rename = "InstanceSet")]
22    #[serde(default)]
23    pub instance_set: Vec<InstanceSummary>,
24    #[serde(rename = "RequestId")]
25    pub request_id: String,
26}
27
28#[derive(Debug, Deserialize)]
29pub struct InstanceSummary {
30    #[serde(rename = "InstanceId")]
31    pub instance_id: Option<String>,
32    #[serde(rename = "InstanceName")]
33    pub instance_name: Option<String>,
34    #[serde(rename = "InstanceState")]
35    pub instance_state: Option<String>,
36    #[serde(rename = "InstanceType")]
37    pub instance_type: Option<String>,
38    #[serde(rename = "Cpu")]
39    pub cpu: Option<u64>,
40    #[serde(rename = "Memory")]
41    pub memory: Option<u64>,
42    #[serde(rename = "PrivateIpAddresses")]
43    pub private_ip_addresses: Option<Vec<String>>,
44    #[serde(rename = "PublicIpAddresses")]
45    pub public_ip_addresses: Option<Vec<String>>,
46    #[serde(default)]
47    pub placement: Option<InstancePlacement>,
48    #[serde(default)]
49    pub system_disk: Option<DiskSummary>,
50    #[serde(default)]
51    pub data_disks: Option<Vec<DiskSummary>>,
52    #[serde(flatten, default)]
53    pub extra: HashMap<String, Value>,
54}
55
56#[derive(Debug, Deserialize)]
57pub struct InstancePlacement {
58    #[serde(rename = "Zone")]
59    pub zone: Option<String>,
60    #[serde(rename = "ProjectId")]
61    pub project_id: Option<i64>,
62    #[serde(flatten, default)]
63    pub extra: HashMap<String, Value>,
64}
65
66#[derive(Debug, Deserialize)]
67pub struct DiskSummary {
68    #[serde(rename = "DiskType")]
69    pub disk_type: Option<String>,
70    #[serde(rename = "DiskSize")]
71    pub disk_size: Option<u64>,
72    #[serde(flatten, default)]
73    pub extra: HashMap<String, Value>,
74}
75
76#[derive(Serialize)]
77#[serde(rename_all = "PascalCase")]
78struct DescribeInstancesPayload<'a> {
79    #[serde(skip_serializing_if = "Option::is_none")]
80    filters: Option<&'a [Filter<'a>]>,
81    #[serde(skip_serializing_if = "Option::is_none")]
82    limit: Option<u32>,
83    #[serde(skip_serializing_if = "Option::is_none")]
84    offset: Option<u32>,
85}
86
87/// Request wrapper for CVM `DescribeInstances`.
88pub struct DescribeInstances<'a> {
89    pub region: Option<&'a str>,
90    pub filters: Option<Vec<Filter<'a>>>,
91    pub limit: Option<u32>,
92    pub offset: Option<u32>,
93}
94
95impl<'a> Default for DescribeInstances<'a> {
96    fn default() -> Self {
97        Self::new()
98    }
99}
100
101impl<'a> DescribeInstances<'a> {
102    /// Create an empty request configuration.
103    pub fn new() -> Self {
104        Self {
105            region: None,
106            filters: None,
107            limit: None,
108            offset: None,
109        }
110    }
111
112    /// Set the target region for the request.
113    pub fn with_region(mut self, region: &'a str) -> Self {
114        self.region = Some(region);
115        self
116    }
117
118    /// Add a filter to the request.
119    pub fn push_filter(mut self, filter: Filter<'a>) -> Self {
120        self.filters.get_or_insert_with(Vec::new).push(filter);
121        self
122    }
123
124    /// Override the maximum number of items to return.
125    pub fn with_limit(mut self, limit: u32) -> Self {
126        self.limit = Some(limit);
127        self
128    }
129
130    /// Provide an offset for pagination.
131    pub fn with_offset(mut self, offset: u32) -> Self {
132        self.offset = Some(offset);
133        self
134    }
135}
136
137impl<'a> Endpoint for DescribeInstances<'a> {
138    type Output = DescribeInstancesResponse;
139
140    fn service(&self) -> Cow<'static, str> {
141        Cow::Borrowed("cvm")
142    }
143
144    fn action(&self) -> Cow<'static, str> {
145        Cow::Borrowed("DescribeInstances")
146    }
147
148    fn version(&self) -> Cow<'static, str> {
149        Cow::Borrowed("2017-03-12")
150    }
151
152    fn region(&self) -> Option<Cow<'_, str>> {
153        self.region.map(Cow::Borrowed)
154    }
155
156    fn payload(&self) -> Value {
157        let filters = self.filters.as_deref();
158        serde_json::to_value(DescribeInstancesPayload {
159            filters,
160            limit: self.limit,
161            offset: self.offset,
162        })
163        .expect("serialize DescribeInstances payload")
164    }
165}
166
167#[derive(Debug, Deserialize)]
168pub struct GenericActionResponse {
169    #[serde(rename = "Response")]
170    pub response: GenericActionResult,
171}
172
173#[derive(Debug, Deserialize)]
174pub struct GenericActionResult {
175    #[serde(rename = "RequestId")]
176    pub request_id: String,
177}
178
179/// Request payload for `ResetInstancesPassword`.
180pub struct ResetInstancesPassword<'a> {
181    pub region: &'a str,
182    pub instance_ids: &'a [&'a str],
183    pub password: &'a str,
184    pub username: Option<&'a str>,
185    pub force_stop: Option<bool>,
186}
187
188impl<'a> Endpoint for ResetInstancesPassword<'a> {
189    type Output = GenericActionResponse;
190
191    fn service(&self) -> Cow<'static, str> {
192        Cow::Borrowed("cvm")
193    }
194
195    fn action(&self) -> Cow<'static, str> {
196        Cow::Borrowed("ResetInstancesPassword")
197    }
198
199    fn version(&self) -> Cow<'static, str> {
200        Cow::Borrowed("2017-03-12")
201    }
202
203    fn region(&self) -> Option<Cow<'_, str>> {
204        Some(Cow::Borrowed(self.region))
205    }
206
207    fn payload(&self) -> Value {
208        let mut payload = json!({
209            "InstanceIds": self.instance_ids,
210            "Password": self.password
211        });
212
213        if let Some(username) = self.username {
214            payload["UserName"] = json!(username);
215        }
216        if let Some(force_stop) = self.force_stop {
217            payload["ForceStop"] = json!(force_stop);
218        }
219        payload
220    }
221}
222
223#[derive(Debug, Deserialize)]
224pub struct DescribeInstanceVncUrlResponse {
225    #[serde(rename = "Response")]
226    pub response: DescribeInstanceVncUrlResult,
227}
228
229#[derive(Debug, Deserialize)]
230pub struct DescribeInstanceVncUrlResult {
231    #[serde(rename = "InstanceVncUrl")]
232    pub instance_vnc_url: Option<String>,
233    #[serde(rename = "RequestId")]
234    pub request_id: String,
235}
236
237/// Request payload for `DescribeInstanceVncUrl`.
238pub struct DescribeInstanceVncUrl<'a> {
239    pub region: &'a str,
240    pub instance_id: &'a str,
241}
242
243impl<'a> Endpoint for DescribeInstanceVncUrl<'a> {
244    type Output = DescribeInstanceVncUrlResponse;
245
246    fn service(&self) -> Cow<'static, str> {
247        Cow::Borrowed("cvm")
248    }
249
250    fn action(&self) -> Cow<'static, str> {
251        Cow::Borrowed("DescribeInstanceVncUrl")
252    }
253
254    fn version(&self) -> Cow<'static, str> {
255        Cow::Borrowed("2017-03-12")
256    }
257
258    fn region(&self) -> Option<Cow<'_, str>> {
259        Some(Cow::Borrowed(self.region))
260    }
261
262    fn payload(&self) -> Value {
263        json!({ "InstanceId": self.instance_id })
264    }
265}
266
267#[derive(Debug, Deserialize)]
268pub struct RunInstancesResponse {
269    #[serde(rename = "Response")]
270    pub response: RunInstancesResult,
271}
272
273#[derive(Debug, Deserialize)]
274pub struct RunInstancesResult {
275    #[serde(rename = "InstanceIdSet")]
276    pub instance_id_set: Option<Vec<String>>,
277    #[serde(rename = "RequestId")]
278    pub request_id: String,
279}
280
281#[derive(Serialize)]
282#[serde(rename_all = "PascalCase")]
283struct RunInstancesPayload<'a> {
284    image_id: &'a str,
285    instance_type: &'a str,
286    #[serde(skip_serializing_if = "Option::is_none")]
287    instance_name: Option<&'a str>,
288    #[serde(skip_serializing_if = "Option::is_none")]
289    instance_count: Option<u32>,
290    #[serde(skip_serializing_if = "Option::is_none")]
291    client_token: Option<&'a str>,
292    #[serde(skip_serializing_if = "Option::is_none")]
293    subnet_id: Option<&'a str>,
294    #[serde(skip_serializing_if = "Option::is_none")]
295    vpc_id: Option<&'a str>,
296    #[serde(skip_serializing_if = "Option::is_none")]
297    security_group_ids: Option<&'a [&'a str]>,
298}
299
300/// Request payload for `RunInstances`.
301pub struct RunInstances<'a> {
302    pub region: &'a str,
303    pub image_id: &'a str,
304    pub instance_type: &'a str,
305    pub instance_name: Option<&'a str>,
306    pub instance_count: Option<u32>,
307    pub client_token: Option<&'a str>,
308    pub subnet_id: Option<&'a str>,
309    pub vpc_id: Option<&'a str>,
310    pub security_group_ids: Option<Vec<&'a str>>,
311}
312
313impl<'a> RunInstances<'a> {
314    pub fn new(region: &'a str, image_id: &'a str, instance_type: &'a str) -> Self {
315        Self {
316            region,
317            image_id,
318            instance_type,
319            instance_name: None,
320            instance_count: None,
321            client_token: None,
322            subnet_id: None,
323            vpc_id: None,
324            security_group_ids: None,
325        }
326    }
327
328    pub fn with_instance_name(mut self, name: &'a str) -> Self {
329        self.instance_name = Some(name);
330        self
331    }
332
333    pub fn with_instance_count(mut self, count: u32) -> Self {
334        self.instance_count = Some(count);
335        self
336    }
337
338    pub fn with_client_token(mut self, token: &'a str) -> Self {
339        self.client_token = Some(token);
340        self
341    }
342
343    pub fn with_subnet(mut self, subnet_id: &'a str) -> Self {
344        self.subnet_id = Some(subnet_id);
345        self
346    }
347
348    pub fn with_vpc(mut self, vpc_id: &'a str) -> Self {
349        self.vpc_id = Some(vpc_id);
350        self
351    }
352
353    pub fn with_security_groups<I>(mut self, groups: I) -> Self
354    where
355        I: IntoIterator<Item = &'a str>,
356    {
357        self.security_group_ids = Some(groups.into_iter().collect());
358        self
359    }
360}
361
362impl<'a> Endpoint for RunInstances<'a> {
363    type Output = RunInstancesResponse;
364
365    fn service(&self) -> Cow<'static, str> {
366        Cow::Borrowed("cvm")
367    }
368
369    fn action(&self) -> Cow<'static, str> {
370        Cow::Borrowed("RunInstances")
371    }
372
373    fn version(&self) -> Cow<'static, str> {
374        Cow::Borrowed("2017-03-12")
375    }
376
377    fn region(&self) -> Option<Cow<'_, str>> {
378        Some(Cow::Borrowed(self.region))
379    }
380
381    fn payload(&self) -> Value {
382        let security_group_ids = self.security_group_ids.as_ref().map(|ids| ids.to_vec());
383
384        let payload = RunInstancesPayload {
385            image_id: self.image_id,
386            instance_type: self.instance_type,
387            instance_name: self.instance_name,
388            instance_count: self.instance_count,
389            client_token: self.client_token,
390            subnet_id: self.subnet_id,
391            vpc_id: self.vpc_id,
392            security_group_ids: security_group_ids.as_deref(),
393        };
394
395        serde_json::to_value(payload).expect("serialize RunInstances payload")
396    }
397}
398
399/// Request payload for `StartInstances`.
400pub struct StartInstances<'a> {
401    pub region: &'a str,
402    pub instance_ids: &'a [&'a str],
403}
404
405impl<'a> Endpoint for StartInstances<'a> {
406    type Output = GenericActionResponse;
407
408    fn service(&self) -> Cow<'static, str> {
409        Cow::Borrowed("cvm")
410    }
411
412    fn action(&self) -> Cow<'static, str> {
413        Cow::Borrowed("StartInstances")
414    }
415
416    fn version(&self) -> Cow<'static, str> {
417        Cow::Borrowed("2017-03-12")
418    }
419
420    fn region(&self) -> Option<Cow<'_, str>> {
421        Some(Cow::Borrowed(self.region))
422    }
423
424    fn payload(&self) -> Value {
425        json!({ "InstanceIds": self.instance_ids })
426    }
427}
428
429/// Request payload for `RebootInstances`.
430pub struct RebootInstances<'a> {
431    pub region: &'a str,
432    pub instance_ids: &'a [&'a str],
433    pub force_reboot: Option<bool>,
434}
435
436impl<'a> Endpoint for RebootInstances<'a> {
437    type Output = GenericActionResponse;
438
439    fn service(&self) -> Cow<'static, str> {
440        Cow::Borrowed("cvm")
441    }
442
443    fn action(&self) -> Cow<'static, str> {
444        Cow::Borrowed("RebootInstances")
445    }
446
447    fn version(&self) -> Cow<'static, str> {
448        Cow::Borrowed("2017-03-12")
449    }
450
451    fn region(&self) -> Option<Cow<'_, str>> {
452        Some(Cow::Borrowed(self.region))
453    }
454
455    fn payload(&self) -> Value {
456        let mut payload = json!({ "InstanceIds": self.instance_ids });
457        if let Some(force_reboot) = self.force_reboot {
458            payload["ForceReboot"] = json!(force_reboot);
459        }
460        payload
461    }
462}
463
464/// Request payload for `StopInstances`.
465pub struct StopInstances<'a> {
466    pub region: &'a str,
467    pub instance_ids: &'a [&'a str],
468    pub stop_type: Option<&'a str>,
469}
470
471impl<'a> Endpoint for StopInstances<'a> {
472    type Output = GenericActionResponse;
473
474    fn service(&self) -> Cow<'static, str> {
475        Cow::Borrowed("cvm")
476    }
477
478    fn action(&self) -> Cow<'static, str> {
479        Cow::Borrowed("StopInstances")
480    }
481
482    fn version(&self) -> Cow<'static, str> {
483        Cow::Borrowed("2017-03-12")
484    }
485
486    fn region(&self) -> Option<Cow<'_, str>> {
487        Some(Cow::Borrowed(self.region))
488    }
489
490    fn payload(&self) -> Value {
491        let mut payload = json!({ "InstanceIds": self.instance_ids });
492        if let Some(stop_type) = self.stop_type {
493            payload["StopType"] = json!(stop_type);
494        }
495        payload
496    }
497}
498
499/// Request payload for `ModifyInstancesProject`.
500pub struct ModifyInstancesProject<'a> {
501    pub region: &'a str,
502    pub instance_ids: &'a [&'a str],
503    pub project_id: u64,
504}
505
506impl<'a> Endpoint for ModifyInstancesProject<'a> {
507    type Output = GenericActionResponse;
508
509    fn service(&self) -> Cow<'static, str> {
510        Cow::Borrowed("cvm")
511    }
512
513    fn action(&self) -> Cow<'static, str> {
514        Cow::Borrowed("ModifyInstancesProject")
515    }
516
517    fn version(&self) -> Cow<'static, str> {
518        Cow::Borrowed("2017-03-12")
519    }
520
521    fn region(&self) -> Option<Cow<'_, str>> {
522        Some(Cow::Borrowed(self.region))
523    }
524
525    fn payload(&self) -> Value {
526        json!({
527            "InstanceIds": self.instance_ids,
528            "ProjectId": self.project_id
529        })
530    }
531}
532
533#[derive(Debug, Deserialize)]
534pub struct TerminateInstancesResponse {
535    #[serde(rename = "Response")]
536    pub response: GenericActionResult,
537}
538
539/// Request payload for `TerminateInstances`.
540pub struct TerminateInstances<'a> {
541    pub region: &'a str,
542    pub instance_ids: &'a [&'a str],
543}
544
545impl<'a> Endpoint for TerminateInstances<'a> {
546    type Output = TerminateInstancesResponse;
547
548    fn service(&self) -> Cow<'static, str> {
549        Cow::Borrowed("cvm")
550    }
551
552    fn action(&self) -> Cow<'static, str> {
553        Cow::Borrowed("TerminateInstances")
554    }
555
556    fn version(&self) -> Cow<'static, str> {
557        Cow::Borrowed("2017-03-12")
558    }
559
560    fn region(&self) -> Option<Cow<'_, str>> {
561        Some(Cow::Borrowed(self.region))
562    }
563
564    fn payload(&self) -> Value {
565        json!({ "InstanceIds": self.instance_ids })
566    }
567}
568
569#[derive(Debug, Deserialize)]
570pub struct DescribeImagesResponse {
571    #[serde(rename = "Response")]
572    pub response: DescribeImagesResult,
573}
574
575#[derive(Debug, Deserialize)]
576pub struct DescribeImagesResult {
577    #[serde(rename = "TotalCount")]
578    pub total_count: Option<u64>,
579    #[serde(rename = "ImageSet")]
580    pub image_set: Vec<ImageSummary>,
581    #[serde(rename = "RequestId")]
582    pub request_id: String,
583}
584
585#[derive(Debug, Deserialize)]
586pub struct ImageSummary {
587    #[serde(rename = "ImageId")]
588    pub image_id: Option<String>,
589    #[serde(rename = "ImageName")]
590    pub image_name: Option<String>,
591    #[serde(rename = "ImageType")]
592    pub image_type: Option<String>,
593    #[serde(rename = "CreatedTime")]
594    pub created_time: Option<String>,
595    #[serde(flatten)]
596    pub extra: HashMap<String, Value>,
597}
598
599#[derive(Serialize)]
600#[serde(rename_all = "PascalCase")]
601struct DescribeImagesPayload<'a> {
602    #[serde(skip_serializing_if = "Option::is_none")]
603    image_ids: Option<&'a [&'a str]>,
604    #[serde(skip_serializing_if = "Option::is_none")]
605    filters: Option<&'a [Filter<'a>]>,
606    #[serde(skip_serializing_if = "Option::is_none")]
607    limit: Option<u32>,
608    #[serde(skip_serializing_if = "Option::is_none")]
609    offset: Option<u32>,
610}
611
612pub struct DescribeImages<'a> {
613    pub region: Option<&'a str>,
614    pub image_ids: Option<Vec<&'a str>>,
615    pub filters: Option<Vec<Filter<'a>>>,
616    pub limit: Option<u32>,
617    pub offset: Option<u32>,
618}
619
620impl<'a> Default for DescribeImages<'a> {
621    fn default() -> Self {
622        Self::new()
623    }
624}
625
626impl<'a> DescribeImages<'a> {
627    pub fn new() -> Self {
628        Self {
629            region: None,
630            image_ids: None,
631            filters: None,
632            limit: None,
633            offset: None,
634        }
635    }
636
637    pub fn with_region(mut self, region: &'a str) -> Self {
638        self.region = Some(region);
639        self
640    }
641
642    pub fn push_image_id(mut self, id: &'a str) -> Self {
643        self.image_ids.get_or_insert_with(Vec::new).push(id);
644        self
645    }
646
647    pub fn push_filter(mut self, filter: Filter<'a>) -> Self {
648        self.filters.get_or_insert_with(Vec::new).push(filter);
649        self
650    }
651
652    pub fn with_limit(mut self, limit: u32) -> Self {
653        self.limit = Some(limit);
654        self
655    }
656
657    pub fn with_offset(mut self, offset: u32) -> Self {
658        self.offset = Some(offset);
659        self
660    }
661}
662
663impl<'a> Endpoint for DescribeImages<'a> {
664    type Output = DescribeImagesResponse;
665
666    fn service(&self) -> Cow<'static, str> {
667        Cow::Borrowed("cvm")
668    }
669
670    fn action(&self) -> Cow<'static, str> {
671        Cow::Borrowed("DescribeImages")
672    }
673
674    fn version(&self) -> Cow<'static, str> {
675        Cow::Borrowed("2017-03-12")
676    }
677
678    fn region(&self) -> Option<Cow<'_, str>> {
679        self.region.map(Cow::Borrowed)
680    }
681
682    fn payload(&self) -> Value {
683        let payload = DescribeImagesPayload {
684            image_ids: self.image_ids.as_deref(),
685            filters: self.filters.as_deref(),
686            limit: self.limit,
687            offset: self.offset,
688        };
689
690        serde_json::to_value(payload).expect("serialize DescribeImages payload")
691    }
692}
693
694/// Invoke `DescribeInstances` asynchronously.
695pub async fn describe_instances_async(
696    client: &TencentCloudAsync,
697    request: &DescribeInstances<'_>,
698) -> TencentCloudResult<DescribeInstancesResponse> {
699    client.request(request).await
700}
701
702/// Invoke `DescribeInstances` with the blocking client.
703pub fn describe_instances_blocking(
704    client: &TencentCloudBlocking,
705    request: &DescribeInstances<'_>,
706) -> TencentCloudResult<DescribeInstancesResponse> {
707    client.request(request)
708}
709
710/// Reset instances password asynchronously.
711pub async fn reset_instances_password_async(
712    client: &TencentCloudAsync,
713    request: &ResetInstancesPassword<'_>,
714) -> TencentCloudResult<GenericActionResponse> {
715    client.request(request).await
716}
717
718/// Reset instances password with the blocking client.
719pub fn reset_instances_password_blocking(
720    client: &TencentCloudBlocking,
721    request: &ResetInstancesPassword<'_>,
722) -> TencentCloudResult<GenericActionResponse> {
723    client.request(request)
724}
725
726/// Fetch CVM VNC URL asynchronously.
727pub async fn describe_instance_vnc_url_async(
728    client: &TencentCloudAsync,
729    request: &DescribeInstanceVncUrl<'_>,
730) -> TencentCloudResult<DescribeInstanceVncUrlResponse> {
731    client.request(request).await
732}
733
734/// Fetch CVM VNC URL with the blocking client.
735pub fn describe_instance_vnc_url_blocking(
736    client: &TencentCloudBlocking,
737    request: &DescribeInstanceVncUrl<'_>,
738) -> TencentCloudResult<DescribeInstanceVncUrlResponse> {
739    client.request(request)
740}
741
742/// Start CVM instances asynchronously.
743pub async fn start_instances_async(
744    client: &TencentCloudAsync,
745    request: &StartInstances<'_>,
746) -> TencentCloudResult<GenericActionResponse> {
747    client.request(request).await
748}
749
750/// Start CVM instances with the blocking client.
751pub fn start_instances_blocking(
752    client: &TencentCloudBlocking,
753    request: &StartInstances<'_>,
754) -> TencentCloudResult<GenericActionResponse> {
755    client.request(request)
756}
757
758/// Reboot CVM instances asynchronously.
759pub async fn reboot_instances_async(
760    client: &TencentCloudAsync,
761    request: &RebootInstances<'_>,
762) -> TencentCloudResult<GenericActionResponse> {
763    client.request(request).await
764}
765
766/// Reboot CVM instances with the blocking client.
767pub fn reboot_instances_blocking(
768    client: &TencentCloudBlocking,
769    request: &RebootInstances<'_>,
770) -> TencentCloudResult<GenericActionResponse> {
771    client.request(request)
772}
773
774/// Stop CVM instances asynchronously.
775pub async fn stop_instances_async(
776    client: &TencentCloudAsync,
777    request: &StopInstances<'_>,
778) -> TencentCloudResult<GenericActionResponse> {
779    client.request(request).await
780}
781
782/// Stop CVM instances with the blocking client.
783pub fn stop_instances_blocking(
784    client: &TencentCloudBlocking,
785    request: &StopInstances<'_>,
786) -> TencentCloudResult<GenericActionResponse> {
787    client.request(request)
788}
789
790/// Change the project of CVM instances asynchronously.
791pub async fn modify_instances_project_async(
792    client: &TencentCloudAsync,
793    request: &ModifyInstancesProject<'_>,
794) -> TencentCloudResult<GenericActionResponse> {
795    client.request(request).await
796}
797
798/// Change the project of CVM instances with the blocking client.
799pub fn modify_instances_project_blocking(
800    client: &TencentCloudBlocking,
801    request: &ModifyInstancesProject<'_>,
802) -> TencentCloudResult<GenericActionResponse> {
803    client.request(request)
804}
805
806/// Run new CVM instances asynchronously.
807pub async fn run_instances_async(
808    client: &TencentCloudAsync,
809    request: &RunInstances<'_>,
810) -> TencentCloudResult<RunInstancesResponse> {
811    client.request(request).await
812}
813
814/// Run new CVM instances with the blocking client.
815pub fn run_instances_blocking(
816    client: &TencentCloudBlocking,
817    request: &RunInstances<'_>,
818) -> TencentCloudResult<RunInstancesResponse> {
819    client.request(request)
820}
821
822/// Terminate CVM instances asynchronously.
823pub async fn terminate_instances_async(
824    client: &TencentCloudAsync,
825    request: &TerminateInstances<'_>,
826) -> TencentCloudResult<TerminateInstancesResponse> {
827    client.request(request).await
828}
829
830/// Terminate CVM instances with the blocking client.
831pub fn terminate_instances_blocking(
832    client: &TencentCloudBlocking,
833    request: &TerminateInstances<'_>,
834) -> TencentCloudResult<TerminateInstancesResponse> {
835    client.request(request)
836}
837
838/// Describe machine images asynchronously.
839pub async fn describe_images_async(
840    client: &TencentCloudAsync,
841    request: &DescribeImages<'_>,
842) -> TencentCloudResult<DescribeImagesResponse> {
843    client.request(request).await
844}
845
846/// Describe machine images with the blocking client.
847pub fn describe_images_blocking(
848    client: &TencentCloudBlocking,
849    request: &DescribeImages<'_>,
850) -> TencentCloudResult<DescribeImagesResponse> {
851    client.request(request)
852}
853
854#[cfg(test)]
855mod tests {
856    use super::*;
857    use crate::services::Filter;
858
859    #[test]
860    fn describe_instances_payload_supports_filters() {
861        let filters = vec![
862            Filter::new("instance-id", ["ins-123"]),
863            Filter::new("zone", ["ap-shanghai-1"]),
864        ];
865        let request = DescribeInstances {
866            region: Some("ap-shanghai"),
867            filters: Some(filters.clone()),
868            limit: Some(20),
869            offset: Some(0),
870        };
871
872        let payload = request.payload();
873        assert_eq!(payload["Filters"][0]["Name"], json!("instance-id"));
874        assert_eq!(payload["Filters"][1]["Values"], json!(["ap-shanghai-1"]));
875        assert_eq!(payload["Limit"], json!(20));
876        assert_eq!(payload["Offset"], json!(0));
877    }
878
879    #[test]
880    fn deserialize_generic_action_response() {
881        let payload = r#"{
882            "Response": {
883                "RequestId": "req-abc"
884            }
885        }"#;
886        let parsed: GenericActionResponse = serde_json::from_str(payload).unwrap();
887        assert_eq!(parsed.response.request_id, "req-abc");
888    }
889
890    #[test]
891    fn deserialize_vnc_url_response() {
892        let payload = r#"{
893            "Response": {
894                "InstanceVncUrl": "https://example.com",
895                "RequestId": "req-xyz"
896            }
897        }"#;
898        let parsed: DescribeInstanceVncUrlResponse = serde_json::from_str(payload).unwrap();
899        assert_eq!(
900            parsed.response.instance_vnc_url.as_deref(),
901            Some("https://example.com")
902        );
903    }
904
905    #[test]
906    fn describe_instances_builder_accumulates_filters() {
907        let request = DescribeInstances::new()
908            .with_region("ap-guangzhou")
909            .push_filter(Filter::new("zone", ["ap-guangzhou-1"]))
910            .with_limit(10)
911            .with_offset(5);
912
913        assert_eq!(request.region, Some("ap-guangzhou"));
914        let filters = request.filters.as_ref().expect("filters set");
915        assert_eq!(filters.len(), 1);
916        assert_eq!(filters[0].name, "zone");
917        assert_eq!(filters[0].values[0], "ap-guangzhou-1");
918        assert_eq!(request.limit, Some(10));
919        assert_eq!(request.offset, Some(5));
920    }
921
922    #[test]
923    fn run_instances_payload_includes_optional_fields() {
924        let request = RunInstances::new("ap-beijing", "img-123", "S4.SMALL1")
925            .with_instance_name("demo")
926            .with_instance_count(2)
927            .with_client_token("token")
928            .with_subnet("subnet-123")
929            .with_security_groups(["sg-1", "sg-2"]);
930
931        let payload = request.payload();
932        assert_eq!(payload["ImageId"], json!("img-123"));
933        assert_eq!(payload["InstanceType"], json!("S4.SMALL1"));
934        assert_eq!(payload["InstanceName"], json!("demo"));
935        assert_eq!(payload["InstanceCount"], json!(2));
936        assert_eq!(payload["ClientToken"], json!("token"));
937        assert_eq!(payload["SubnetId"], json!("subnet-123"));
938        assert_eq!(payload["SecurityGroupIds"], json!(["sg-1", "sg-2"]));
939    }
940
941    #[test]
942    fn describe_images_payload_supports_filters() {
943        let request = DescribeImages::new()
944            .with_region("ap-beijing")
945            .push_image_id("img-123")
946            .push_filter(Filter::new("image-type", ["PUBLIC_IMAGE"]))
947            .with_limit(10);
948
949        let payload = request.payload();
950        assert_eq!(payload["ImageIds"], json!(["img-123"]));
951        assert_eq!(
952            payload["Filters"],
953            json!([{ "Name": "image-type", "Values": ["PUBLIC_IMAGE"] }])
954        );
955        assert_eq!(payload["Limit"], json!(10));
956    }
957
958    #[test]
959    fn deserialize_run_instances_response() {
960        let payload = r#"{
961            "Response": {
962                "InstanceIdSet": ["ins-1", "ins-2"],
963                "RequestId": "req-789"
964            }
965        }"#;
966        let parsed: RunInstancesResponse = serde_json::from_str(payload).unwrap();
967        assert_eq!(
968            parsed.response.instance_id_set.unwrap(),
969            vec!["ins-1".to_string(), "ins-2".to_string()]
970        );
971    }
972
973    #[test]
974    fn deserialize_describe_images_response() {
975        let payload = r#"{
976            "Response": {
977                "TotalCount": 1,
978                "ImageSet": [{
979                    "ImageId": "img-1",
980                    "ImageName": "test"
981                }],
982                "RequestId": "req-111"
983            }
984        }"#;
985        let parsed: DescribeImagesResponse = serde_json::from_str(payload).unwrap();
986        assert_eq!(parsed.response.total_count, Some(1));
987        assert_eq!(
988            parsed.response.image_set[0].image_id.as_deref(),
989            Some("img-1")
990        );
991    }
992}