Skip to main content

google_cloud_compute_v1/
errors.rs

1// Copyright 2025 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use crate::model::{
16    InstancesBulkInsertOperationMetadata, SetCommonInstanceMetadataOperationMetadata,
17};
18
19impl crate::model::Operation {
20    pub fn to_result(self) -> std::result::Result<Self, OperationError> {
21        if self.error.is_some()
22            || self.http_error_status_code.is_some()
23            || self.http_error_message.is_some()
24        {
25            let error = GenericOperationError::new();
26            let error = self.error.into_iter().fold(error, |e, v| e.set_details(v));
27            let error = self
28                .http_error_status_code
29                .into_iter()
30                .fold(error, |e, v| e.set_status_code(v));
31            let error = self
32                .http_error_message
33                .into_iter()
34                .fold(error, |e, v| e.set_message(v));
35            return Err(OperationError::Generic(error));
36        }
37        if let Some(metadata) = self.instances_bulk_insert_operation_metadata.as_ref() {
38            let found = metadata
39                .per_location_status
40                .iter()
41                .any(|(_, v)| v.failed_to_create_vm_count.is_some_and(|c| c != 0));
42            if found {
43                let error = InstanceBulkInsertOperationError::new()
44                    .set_metadata(self.instances_bulk_insert_operation_metadata.unwrap());
45                return Err(OperationError::InstancesBulkInsert(error));
46            }
47        }
48        if let Some(metadata) = self
49            .set_common_instance_metadata_operation_metadata
50            .as_ref()
51        {
52            let found = metadata
53                .per_location_operations
54                .iter()
55                .any(|(_, v)| v.error.is_some());
56            if found {
57                let error = SetCommonInstanceMetadataOperationError::new().set_metadata(
58                    self.set_common_instance_metadata_operation_metadata
59                        .unwrap(),
60                );
61                return Err(OperationError::SetCommonInstanceMetadata(error));
62            }
63        }
64        Ok(self)
65    }
66}
67
68/// Possible errors returned by an operation.
69///
70/// The Compute API long running operations return different errors depending on
71/// the operation being called.
72#[derive(Clone, Debug, PartialEq)]
73#[non_exhaustive]
74pub enum OperationError {
75    /// A HTTP error with additional details.
76    Generic(GenericOperationError),
77    /// A partial failure when inserting instances (VMs) in bulk.
78    InstancesBulkInsert(InstanceBulkInsertOperationError),
79    /// A partial failure when setting the common metadata for all instances.
80    SetCommonInstanceMetadata(SetCommonInstanceMetadataOperationError),
81}
82
83impl std::fmt::Display for OperationError {
84    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
85        match self {
86            Self::Generic(d) => write!(f, "the long-running operation failed with {d:?}"),
87            Self::InstancesBulkInsert(d) => {
88                write!(
89                    f,
90                    "the long-running operation to insert instances in bulk failed with {d:?}"
91                )
92            }
93            Self::SetCommonInstanceMetadata(d) => write!(
94                f,
95                "the long-running operation to set the common instance metadata fialed with {d:?}"
96            ),
97        }
98    }
99}
100
101impl std::error::Error for OperationError {}
102
103/// Details about a generic long-running operation error.
104#[derive(Clone, Debug, Default, PartialEq)]
105#[non_exhaustive]
106pub struct GenericOperationError {
107    /// The HTTP error message.
108    pub message: Option<String>,
109
110    /// The HTTP error status code.
111    pub status_code: Option<i32>,
112
113    /// The errors generated while processing the operation.
114    pub details: Option<crate::model::operation::Error>,
115}
116
117impl GenericOperationError {
118    /// Create a new instance.
119    pub fn new() -> Self {
120        Self::default()
121    }
122
123    /// Set the [message][Self::message] field.
124    ///
125    /// # Example
126    /// ```
127    /// # use google_cloud_compute_v1::errors::GenericOperationError;
128    /// let error = GenericOperationError::new().set_message("useful in mocks");
129    /// ```
130    pub fn set_message<V: Into<String>>(mut self, v: V) -> Self {
131        self.message = Some(v.into());
132        self
133    }
134
135    /// Set the [status_code][Self::status_code] field.
136    ///
137    /// # Example
138    /// ```
139    /// # use google_cloud_compute_v1::errors::GenericOperationError;
140    /// let error = GenericOperationError::new().set_status_code(503);
141    /// ```
142    pub fn set_status_code(mut self, v: i32) -> Self {
143        self.status_code = Some(v);
144        self
145    }
146
147    /// Set the [details][Self::details] field.
148    ///
149    /// # Example
150    /// ```
151    /// # use google_cloud_compute_v1::errors::GenericOperationError;
152    /// use google_cloud_compute_v1::model::operation::{Error, error::Errors};
153    /// let error = GenericOperationError::new().set_details(
154    ///     Error::new().set_errors([
155    ///         Errors::new()
156    ///             .set_code("MOCK_ERROR_CODE")
157    ///             .set_location("some_field")
158    ///             .set_message("a mocked error"),
159    ///         ]),
160    /// );
161    /// ```
162    pub fn set_details<V: Into<crate::model::operation::Error>>(mut self, v: V) -> Self {
163        self.details = Some(v.into());
164        self
165    }
166}
167
168/// Details about an [instance bulk insert] long-runing operation error.
169///
170/// [instance bulk insert]: crate::client::Instances::bulk_insert
171#[derive(Clone, Debug, Default, PartialEq)]
172#[non_exhaustive]
173pub struct InstanceBulkInsertOperationError {
174    /// The information about all zonal actions and their state.
175    pub metadata: InstancesBulkInsertOperationMetadata,
176}
177
178impl InstanceBulkInsertOperationError {
179    /// Create a new instance.
180    pub fn new() -> Self {
181        Self::default()
182    }
183
184    /// Set the [metadata][Self::metadata] field.
185    ///
186    /// # Example
187    /// ```
188    /// # use google_cloud_compute_v1::errors::InstanceBulkInsertOperationError;
189    /// use google_cloud_compute_v1::model::{BulkInsertOperationStatus, InstancesBulkInsertOperationMetadata};
190    /// let error = InstanceBulkInsertOperationError::new().set_metadata(
191    ///     InstancesBulkInsertOperationMetadata::new().set_per_location_status([
192    ///         ("zones/us-central1-a", BulkInsertOperationStatus::new()
193    ///                 .set_failed_to_create_vm_count(42))
194    ///     ])
195    /// );
196    /// ```
197    pub fn set_metadata(mut self, v: InstancesBulkInsertOperationMetadata) -> Self {
198        self.metadata = v;
199        self
200    }
201}
202
203/// Details about an [set common instance metadata] long-runing operation error.
204///
205/// [set common instance metadata]: crate::client::Projects::set_common_instance_metadata
206#[derive(Clone, Debug, Default, PartialEq)]
207#[non_exhaustive]
208pub struct SetCommonInstanceMetadataOperationError {
209    /// The information about all zonal actions and their state.
210    pub metadata: SetCommonInstanceMetadataOperationMetadata,
211}
212
213impl SetCommonInstanceMetadataOperationError {
214    /// Create a new instance.
215    pub fn new() -> Self {
216        Self::default()
217    }
218
219    /// Set the [metadata][Self::metadata] field.
220    ///
221    /// # Example
222    /// ```
223    /// # use google_cloud_compute_v1::errors::SetCommonInstanceMetadataOperationError;
224    /// use google_cloud_compute_v1::model::{
225    ///     SetCommonInstanceMetadataOperationMetadata, SetCommonInstanceMetadataOperationMetadataPerLocationOperationInfo,
226    ///     Status, set_common_instance_metadata_operation_metadata_per_location_operation_info::State};
227    /// let error = SetCommonInstanceMetadataOperationError::new().set_metadata(
228    ///     SetCommonInstanceMetadataOperationMetadata::new().set_per_location_operations([
229    ///         ("zones/us-central1-a", SetCommonInstanceMetadataOperationMetadataPerLocationOperationInfo::new()
230    ///             .set_state(State::Abandoned)
231    ///             .set_error(Status::new().set_message("NOT FOUND")))
232    ///     ])
233    /// );
234    /// ```
235    pub fn set_metadata(mut self, v: SetCommonInstanceMetadataOperationMetadata) -> Self {
236        self.metadata = v;
237        self
238    }
239}
240
241#[cfg(test)]
242mod tests {
243    use super::*;
244    use crate::model::{
245        BulkInsertOperationStatus, Operation,
246        SetCommonInstanceMetadataOperationMetadataPerLocationOperationInfo, operation::Error,
247    };
248
249    #[test]
250    fn to_result() {
251        let operation = Operation::new().set_client_operation_id("abc");
252        let got = operation.clone().to_result();
253        assert!(matches!(got, Ok(ref o) if o == &operation), "{got:?}");
254
255        let operation = Operation::new().set_http_error_message("uh-oh");
256        let got = operation.clone().to_result();
257        assert!(
258            matches!(got, Err(OperationError::Generic(ref e)) if e == &GenericOperationError::new().set_message("uh-oh")),
259            "{got:?}"
260        );
261
262        let operation = Operation::new().set_http_error_status_code(503);
263        let got = operation.clone().to_result();
264        assert!(
265            matches!(got, Err(OperationError::Generic(ref e)) if e == &GenericOperationError::new().set_status_code(503)),
266            "{got:?}"
267        );
268
269        let operation = Operation::new().set_error(Error::new());
270        let got = operation.clone().to_result();
271        assert!(
272            matches!(got, Err(OperationError::Generic(ref e)) if e == &GenericOperationError::new().set_details(Error::new())),
273            "{got:?}"
274        );
275    }
276
277    #[test]
278    fn to_result_instances_bulk_insert() {
279        let metadata = InstancesBulkInsertOperationMetadata::new();
280        let operation = Operation::new().set_instances_bulk_insert_operation_metadata(metadata);
281        let got = operation.clone().to_result();
282        assert!(matches!(got, Ok(ref op) if op == &operation), "{got:?}");
283
284        let metadata = InstancesBulkInsertOperationMetadata::new().set_per_location_status([(
285            "zones/us-central1-a",
286            BulkInsertOperationStatus::new().set_created_vm_count(42),
287        )]);
288        let operation = Operation::new().set_instances_bulk_insert_operation_metadata(metadata);
289        let got = operation.clone().to_result();
290        assert!(matches!(got, Ok(ref op) if op == &operation), "{got:?}");
291
292        let metadata = InstancesBulkInsertOperationMetadata::new().set_per_location_status([
293            (
294                "zones/us-central1-a",
295                BulkInsertOperationStatus::new().set_created_vm_count(42),
296            ),
297            (
298                "zones/us-central1-f",
299                BulkInsertOperationStatus::new().set_failed_to_create_vm_count(42),
300            ),
301        ]);
302        let operation =
303            Operation::new().set_instances_bulk_insert_operation_metadata(metadata.clone());
304        let got = operation.to_result();
305        assert!(
306            matches!(got, Err(OperationError::InstancesBulkInsert(ref e)) if e.metadata == metadata),
307            "{got:?}"
308        );
309    }
310
311    #[test]
312    fn to_result_set_common_instances_metadata() {
313        use crate::model::Status;
314        use crate::model::set_common_instance_metadata_operation_metadata_per_location_operation_info::State;
315
316        let metadata = SetCommonInstanceMetadataOperationMetadata::new();
317        let operation =
318            Operation::new().set_set_common_instance_metadata_operation_metadata(metadata);
319        let got = operation.clone().to_result();
320        assert!(matches!(got, Ok(ref op) if op == &operation), "{got:?}");
321
322        let metadata = SetCommonInstanceMetadataOperationMetadata::new()
323            .set_per_location_operations([(
324                "zones/us-central1-a",
325                SetCommonInstanceMetadataOperationMetadataPerLocationOperationInfo::new()
326                    .set_state(State::Done),
327            )]);
328        let operation =
329            Operation::new().set_set_common_instance_metadata_operation_metadata(metadata);
330        let got = operation.clone().to_result();
331        assert!(matches!(got, Ok(ref op) if op == &operation), "{got:?}");
332
333        let metadata = SetCommonInstanceMetadataOperationMetadata::new()
334            .set_per_location_operations([
335                (
336                    "zones/us-central1-a",
337                    SetCommonInstanceMetadataOperationMetadataPerLocationOperationInfo::new()
338                        .set_state(State::Done),
339                ),
340                (
341                    "zones/us-central1-f",
342                    SetCommonInstanceMetadataOperationMetadataPerLocationOperationInfo::new()
343                        .set_state(State::Abandoned)
344                        .set_error(Status::new().set_message("uh-oh")),
345                ),
346            ]);
347        let operation =
348            Operation::new().set_set_common_instance_metadata_operation_metadata(metadata.clone());
349        let got = operation.to_result();
350        assert!(
351            matches!(got, Err(OperationError::SetCommonInstanceMetadata(ref e)) if e.metadata == metadata),
352            "{got:?}"
353        );
354    }
355
356    #[test]
357    fn display() {
358        let input =
359            OperationError::Generic(GenericOperationError::new().set_message("test-message"));
360        let got = input.to_string();
361        assert!(got.contains("test-message"), "{input:?} => {got}");
362
363        let input = OperationError::InstancesBulkInsert(
364            InstanceBulkInsertOperationError::new().set_metadata(
365                InstancesBulkInsertOperationMetadata::new().set_per_location_status([(
366                    "zones/us-central1-a",
367                    BulkInsertOperationStatus::new().set_created_vm_count(123456),
368                )]),
369            ),
370        );
371        let got = input.to_string();
372        assert!(got.contains("zones/us-central1-a"), "{input:?} => {got}");
373        assert!(got.contains("123456"), "{input:?} => {got}");
374
375        let input = OperationError::SetCommonInstanceMetadata(
376            SetCommonInstanceMetadataOperationError::new().set_metadata(
377                SetCommonInstanceMetadataOperationMetadata::new().set_per_location_operations([(
378                    "zones/us-central1-a",
379                    SetCommonInstanceMetadataOperationMetadataPerLocationOperationInfo::new()
380                        .set_error(crate::model::Status::new().set_message("error-message")),
381                )]),
382            ),
383        );
384        let got = input.to_string();
385        assert!(got.contains("zones/us-central1-a"), "{input:?} => {got}");
386        assert!(got.contains("error-message"), "{input:?} => {got}");
387    }
388
389    #[test]
390    fn generic_operation_setters() {
391        use crate::model::LocalizedMessage;
392        use crate::model::operation::{Error, error::Errors, error::errors::ErrorDetails};
393        let got = GenericOperationError::new().set_message("abc");
394        assert_eq!(got.message.as_deref(), Some("abc"));
395
396        let got = GenericOperationError::new().set_status_code(123);
397        assert_eq!(got.status_code, Some(123));
398
399        let details =
400            Error::new().set_errors([Errors::new()
401                .set_error_details([ErrorDetails::new().set_localized_message(
402                    LocalizedMessage::new().set_locale("C").set_message("uh-oh"),
403                )])]);
404        let got = GenericOperationError::new().set_details(details.clone());
405        assert_eq!(got.details, Some(details));
406    }
407
408    #[test]
409    fn instances_bulk_insert_setters() {
410        let metadata = InstancesBulkInsertOperationMetadata::new().set_per_location_status([(
411            "zones/us-central1-a",
412            BulkInsertOperationStatus::new().set_created_vm_count(123456),
413        )]);
414        let got = InstanceBulkInsertOperationError::new().set_metadata(metadata.clone());
415        assert_eq!(got.metadata, metadata);
416    }
417
418    #[test]
419    fn set_common_instance_metadata_setters() {
420        let metadata = SetCommonInstanceMetadataOperationMetadata::new()
421            .set_per_location_operations([(
422                "zones/us-central1-a",
423                SetCommonInstanceMetadataOperationMetadataPerLocationOperationInfo::new()
424                    .set_error(crate::model::Status::new().set_message("error-message")),
425            )]);
426        let got = SetCommonInstanceMetadataOperationError::new().set_metadata(metadata.clone());
427        assert_eq!(got.metadata, metadata);
428    }
429}