up_rust/ustatus.rs
1/********************************************************************************
2 * Copyright (c) 2023 Contributors to the Eclipse Foundation
3 *
4 * See the NOTICE file(s) distributed with this work for additional
5 * information regarding copyright ownership.
6 *
7 * This program and the accompanying materials are made available under the
8 * terms of the Apache License Version 2.0 which is available at
9 * https://www.apache.org/licenses/LICENSE-2.0
10 *
11 * SPDX-License-Identifier: Apache-2.0
12 ********************************************************************************/
13
14use std::error::Error;
15
16pub use crate::up_core_api::ucode::UCode;
17pub use crate::up_core_api::ustatus::UStatus;
18
19impl UStatus {
20    /// Creates a status representing a success.
21    ///
22    /// # Examples
23    ///
24    /// ```rust
25    /// use up_rust::{UCode, UStatus};
26    ///
27    /// let status = UStatus::ok();
28    /// assert_eq!(status.code.unwrap(), UCode::OK);
29    /// ```
30    pub fn ok() -> Self {
31        UStatus {
32            code: UCode::OK.into(),
33            ..Default::default()
34        }
35    }
36
37    /// Creates a status representing a failure.
38    ///
39    /// # Examples
40    ///
41    /// ```rust
42    /// use up_rust::UStatus;
43    ///
44    /// let status = UStatus::fail("something went wrong");
45    /// assert_eq!(status.message.unwrap(), "something went wrong");
46    /// ```
47    pub fn fail<M: Into<String>>(msg: M) -> Self {
48        UStatus {
49            code: UCode::UNKNOWN.into(),
50            message: Some(msg.into()),
51            ..Default::default()
52        }
53    }
54
55    /// Creates a status representing a failure.
56    ///
57    /// # Examples
58    ///
59    /// ```rust
60    /// use up_rust::{UCode, UStatus};
61    ///
62    /// let status = UStatus::fail_with_code(UCode::DATA_LOSS, "something went wrong");
63    /// assert_eq!(status.code.unwrap(), UCode::DATA_LOSS);
64    /// assert_eq!(status.message.unwrap(), "something went wrong");
65    /// ```
66    pub fn fail_with_code<M: Into<String>>(code: UCode, msg: M) -> Self {
67        UStatus {
68            code: code.into(),
69            message: Some(msg.into()),
70            ..Default::default()
71        }
72    }
73
74    /// Checks if this status represents a failure.
75    ///
76    /// # Examples
77    ///
78    /// ```rust
79    /// use up_rust::UStatus;
80    ///
81    /// let failed_status = UStatus::fail("something went wrong");
82    /// assert!(failed_status.is_failed());
83    ///
84    /// let succeeded_status = UStatus::ok();
85    /// assert!(!succeeded_status.is_failed());
86    /// ```
87    pub fn is_failed(&self) -> bool {
88        self.get_code() != UCode::OK
89    }
90
91    /// Checks if this status represents a success.
92    ///
93    /// # Examples
94    ///
95    /// ```rust
96    /// use up_rust::UStatus;
97    ///
98    /// let succeeded_status = UStatus::ok();
99    /// assert!(succeeded_status.is_success());
100    ///
101    /// let failed_status = UStatus::fail("something went wrong");
102    /// assert!(!failed_status.is_success());
103    /// ```
104    pub fn is_success(&self) -> bool {
105        self.get_code() == UCode::OK
106    }
107
108    /// Gets this status' error message.
109    ///
110    /// # Returns
111    ///
112    /// an empty string if this instance has been created without a message,
113    /// i.e. not using one of its factory functions.
114    ///
115    /// # Examples
116    ///
117    /// ```rust
118    /// use up_rust::UStatus;
119    ///
120    /// let failed_status = UStatus::fail("my error message");
121    /// assert_eq!(failed_status.get_message(), "my error message");
122    ///
123    /// let succeeded_status = UStatus::ok();
124    /// assert!(succeeded_status.get_message().is_empty());
125    /// ```
126    pub fn get_message(&self) -> String {
127        match self.message.as_ref() {
128            Some(msg) => msg.to_owned(),
129            None => String::default(),
130        }
131    }
132
133    /// Gets this status' error code.
134    ///
135    /// # Returns
136    ///
137    /// [`UCode::UNKNOWN`] if this status has been created without providing an error code.
138    ///
139    /// # Examples
140    ///
141    /// ```rust
142    /// use up_rust::{UCode, UStatus};
143    ///
144    /// let status = UStatus::fail("my error message");
145    /// assert_eq!(status.get_code(), UCode::UNKNOWN);
146    ///
147    /// let status_with_code = UStatus::fail_with_code(UCode::INTERNAL, "my error message");
148    /// assert_eq!(status_with_code.get_code(), UCode::INTERNAL);
149    /// ```
150    pub fn get_code(&self) -> UCode {
151        self.code.enum_value_or_default()
152    }
153}
154
155impl Error for UStatus {}
156
157#[cfg(test)]
158mod tests {
159    use super::*;
160
161    use protobuf::{well_known_types::any::Any, Enum, EnumOrUnknown, Message};
162
163    #[test]
164    // [utest->req~ustatus-data-model-impl~1]
165    fn test_ustatus_fail_with_code() {
166        let details = vec![Any {
167            type_url: "type.googleapis.com/google.protobuf.Timestamp".to_string(),
168            ..Default::default()
169        }];
170        UCode::VALUES.iter().for_each(|code| {
171            let mut ustatus = UStatus::fail_with_code(*code, "the message");
172            // just make sure that the field exists and we can assign a value to it
173            ustatus.details = details.clone();
174            assert!(
175                ustatus.code.enum_value().is_ok_and(|v| v == *code)
176                    && ustatus.message.is_some_and(|v| v == "the message")
177            );
178        });
179    }
180
181    #[test]
182    // [utest->req~ustatus-data-model-proto~1]
183    fn test_proto_serialization() {
184        let ustatus = UStatus {
185            code: UCode::CANCELLED.into(),
186            message: Some("the message".to_string()),
187            details: vec![Any {
188                type_url: "type.googleapis.com/google.protobuf.Timestamp".to_string(),
189                ..Default::default()
190            }],
191            ..Default::default()
192        };
193        let proto = ustatus
194            .write_to_bytes()
195            .expect("failed to serialize to protobuf");
196        let deserialized_status =
197            UStatus::parse_from_bytes(proto.as_slice()).expect("failed to deserialize protobuf");
198        assert_eq!(ustatus, deserialized_status);
199    }
200
201    #[test]
202    fn test_is_failed() {
203        assert!(!UStatus {
204            ..Default::default()
205        }
206        .is_failed());
207        UCode::VALUES.iter().for_each(|code| {
208            let ustatus = UStatus {
209                code: EnumOrUnknown::from(*code),
210                ..Default::default()
211            };
212            assert_eq!(ustatus.is_failed(), *code != UCode::OK);
213        });
214    }
215
216    #[test]
217    fn test_is_success() {
218        assert!(UStatus {
219            ..Default::default()
220        }
221        .is_success());
222        UCode::VALUES.iter().for_each(|code| {
223            let ustatus = UStatus {
224                code: EnumOrUnknown::from(*code),
225                ..Default::default()
226            };
227            assert_eq!(ustatus.is_success(), *code == UCode::OK);
228        });
229    }
230}