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