1use std::error::Error;
15
16use bytes::Bytes;
17
18use crate::SerializationError;
19
20#[derive(Copy, Debug, Clone, PartialEq)]
27#[repr(C)]
28pub enum UCode {
29 Ok = 0,
31 Cancelled = 1,
33 Unknown = 2,
35 InvalidArgument = 3,
37 DeadlineExceeded = 4,
39 NotFound = 5,
41 AlreadyExists = 6,
43 PermissionDenied = 7,
45 ResourceExhausted = 8,
47 FailedPrecondition = 9,
49 Aborted = 10,
51 OutOfRange = 11,
53 Unimplemented = 12,
55 Internal = 13,
57 Unavailable = 14,
59 DataLoss = 15,
61 Unauthenticated = 16,
63}
64
65impl UCode {
66 pub fn try_from_i32(value: i32) -> Result<Self, SerializationError> {
72 match value {
73 x if x == UCode::Ok as i32 => Ok(UCode::Ok),
74 x if x == UCode::Cancelled as i32 => Ok(UCode::Cancelled),
75 x if x == UCode::Unknown as i32 => Ok(UCode::Unknown),
76 x if x == UCode::InvalidArgument as i32 => Ok(UCode::InvalidArgument),
77 x if x == UCode::DeadlineExceeded as i32 => Ok(UCode::DeadlineExceeded),
78 x if x == UCode::NotFound as i32 => Ok(UCode::NotFound),
79 x if x == UCode::AlreadyExists as i32 => Ok(UCode::AlreadyExists),
80 x if x == UCode::PermissionDenied as i32 => Ok(UCode::PermissionDenied),
81 x if x == UCode::ResourceExhausted as i32 => Ok(UCode::ResourceExhausted),
82 x if x == UCode::FailedPrecondition as i32 => Ok(UCode::FailedPrecondition),
83 x if x == UCode::Aborted as i32 => Ok(UCode::Aborted),
84 x if x == UCode::OutOfRange as i32 => Ok(UCode::OutOfRange),
85 x if x == UCode::Unimplemented as i32 => Ok(UCode::Unimplemented),
86 x if x == UCode::Internal as i32 => Ok(UCode::Internal),
87 x if x == UCode::Unavailable as i32 => Ok(UCode::Unavailable),
88 x if x == UCode::DataLoss as i32 => Ok(UCode::DataLoss),
89 x if x == UCode::Unauthenticated as i32 => Ok(UCode::Unauthenticated),
90 _ => Err(SerializationError::new(format!(
91 "unknown UCode value: {}",
92 value
93 ))),
94 }
95 }
96
97 #[must_use]
98 pub fn value(&self) -> i32 {
100 *self as i32
101 }
102}
103
104#[derive(Debug, Clone, PartialEq)]
105#[repr(C)]
106pub struct UAny {
108 type_url: String,
109 value: Bytes,
110}
111
112impl UAny {
113 pub fn new<V: Into<Bytes>>(type_url: String, value: V) -> Self {
115 UAny {
116 type_url,
117 value: value.into(),
118 }
119 }
120
121 pub fn type_url(&self) -> &str {
123 &self.type_url
124 }
125 #[deprecated(since = "0.11.0", note = "renamed to `type_url`")]
127 pub fn get_type_url(&self) -> &str {
128 self.type_url()
129 }
130
131 pub fn value(&self) -> &[u8] {
133 &self.value
134 }
135 #[deprecated(since = "0.11.0", note = "renamed to `value`")]
137 pub fn get_value(&self) -> &[u8] {
138 self.value()
139 }
140}
141
142#[derive(Debug, Clone, PartialEq)]
143#[repr(C)]
144pub struct UStatus {
146 code: UCode,
147 message: Option<String>,
148 details: Vec<UAny>,
149}
150
151impl UStatus {
152 #[must_use]
163 pub fn ok() -> Self {
164 UStatus {
165 code: UCode::Ok,
166 message: None,
167 details: vec![],
168 }
169 }
170
171 pub fn fail_with_code<M: Into<std::string::String>>(code: UCode, msg: M) -> Self {
183 Self::new(code, Some(msg), None)
184 }
185
186 pub fn new<M: Into<std::string::String>>(
199 code: UCode,
200 msg: Option<M>,
201 details: Option<Vec<UAny>>,
202 ) -> Self {
203 UStatus {
204 code,
205 message: msg.map(|m| m.into()),
206 details: details.unwrap_or_default(),
207 }
208 }
209
210 #[must_use]
224 pub fn is_failed(&self) -> bool {
225 self.code() != UCode::Ok
226 }
227
228 #[must_use]
242 pub fn is_success(&self) -> bool {
243 self.code() == UCode::Ok
244 }
245
246 #[must_use]
264 pub fn message(&self) -> Option<&str> {
265 self.message.as_deref()
266 }
267 #[deprecated(since = "0.11.0", note = "renamed to `message`")]
269 pub fn get_message(&self) -> Option<&str> {
270 self.message()
271 }
272
273 #[must_use]
295 pub fn message_or_default<'a>(&'a self, default: &'a str) -> &'a str {
296 self.message().unwrap_or(default)
297 }
298 #[deprecated(since = "0.11.0", note = "renamed to `message_or_default`")]
300 pub fn get_message_or_default<'a>(&'a self, default: &'a str) -> &'a str {
301 self.message_or_default(default)
302 }
303
304 #[must_use]
315 pub fn code(&self) -> UCode {
316 self.code
317 }
318 #[deprecated(since = "0.11.0", note = "renamed to `code`")]
320 pub fn get_code(&self) -> UCode {
321 self.code()
322 }
323
324 #[must_use]
339 pub fn details(&self) -> &[UAny] {
340 &self.details
341 }
342 #[deprecated(since = "0.11.0", note = "renamed to `details`")]
344 pub fn get_details(&self) -> &[UAny] {
345 self.details()
346 }
347}
348
349impl std::fmt::Display for UStatus {
350 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
351 let mut s = String::from("UStatus [");
352 s.push_str(&format!("code: {:?}", self.code()));
353 if let Some(msg) = self.message() {
354 s.push_str(&format!(", message: {msg}"));
355 }
356 if !self.details().is_empty() {
357 s.push_str(&format!(", details: {:?}", self.details()));
358 }
359 s.push(']');
360 write!(f, "{}", s)
361 }
362}
363
364impl Error for UStatus {}
365
366#[cfg(feature = "up-core-api")]
367mod core_types_support {
368 use protobuf::{well_known_types::any::Any, Message};
369
370 use super::*;
371
372 use crate::up_core_api::{ucode::UCode as UCodeProto, ustatus::UStatus as UStatusProto};
373 use crate::{ProtobufMappable, SerializationError};
374
375 impl From<UCodeProto> for UCode {
376 fn from(ucode_proto: UCodeProto) -> Self {
377 match ucode_proto {
378 UCodeProto::OK => UCode::Ok,
379 UCodeProto::CANCELLED => UCode::Cancelled,
380 UCodeProto::UNKNOWN => UCode::Unknown,
381 UCodeProto::INVALID_ARGUMENT => UCode::InvalidArgument,
382 UCodeProto::DEADLINE_EXCEEDED => UCode::DeadlineExceeded,
383 UCodeProto::NOT_FOUND => UCode::NotFound,
384 UCodeProto::ALREADY_EXISTS => UCode::AlreadyExists,
385 UCodeProto::PERMISSION_DENIED => UCode::PermissionDenied,
386 UCodeProto::RESOURCE_EXHAUSTED => UCode::ResourceExhausted,
387 UCodeProto::FAILED_PRECONDITION => UCode::FailedPrecondition,
388 UCodeProto::ABORTED => UCode::Aborted,
389 UCodeProto::OUT_OF_RANGE => UCode::OutOfRange,
390 UCodeProto::UNIMPLEMENTED => UCode::Unimplemented,
391 UCodeProto::INTERNAL => UCode::Internal,
392 UCodeProto::UNAVAILABLE => UCode::Unavailable,
393 UCodeProto::DATA_LOSS => UCode::DataLoss,
394 UCodeProto::UNAUTHENTICATED => UCode::Unauthenticated,
395 }
396 }
397 }
398
399 impl From<UCode> for UCodeProto {
400 fn from(ucode: UCode) -> Self {
401 match ucode {
402 UCode::Ok => UCodeProto::OK,
403 UCode::Cancelled => UCodeProto::CANCELLED,
404 UCode::Unknown => UCodeProto::UNKNOWN,
405 UCode::InvalidArgument => UCodeProto::INVALID_ARGUMENT,
406 UCode::DeadlineExceeded => UCodeProto::DEADLINE_EXCEEDED,
407 UCode::NotFound => UCodeProto::NOT_FOUND,
408 UCode::AlreadyExists => UCodeProto::ALREADY_EXISTS,
409 UCode::PermissionDenied => UCodeProto::PERMISSION_DENIED,
410 UCode::ResourceExhausted => UCodeProto::RESOURCE_EXHAUSTED,
411 UCode::FailedPrecondition => UCodeProto::FAILED_PRECONDITION,
412 UCode::Aborted => UCodeProto::ABORTED,
413 UCode::OutOfRange => UCodeProto::OUT_OF_RANGE,
414 UCode::Unimplemented => UCodeProto::UNIMPLEMENTED,
415 UCode::Internal => UCodeProto::INTERNAL,
416 UCode::Unavailable => UCodeProto::UNAVAILABLE,
417 UCode::DataLoss => UCodeProto::DATA_LOSS,
418 UCode::Unauthenticated => UCodeProto::UNAUTHENTICATED,
419 }
420 }
421 }
422
423 impl From<Any> for UAny {
424 fn from(value: Any) -> Self {
425 UAny {
426 type_url: value.type_url,
427 value: value.value.into(),
428 }
429 }
430 }
431
432 impl From<&UAny> for Any {
433 fn from(value: &UAny) -> Self {
434 Any {
435 type_url: value.type_url.clone(),
436 value: value.value.clone().into(),
437 ..Default::default()
438 }
439 }
440 }
441
442 impl TryFrom<UStatusProto> for UStatus {
443 type Error = SerializationError;
444
445 fn try_from(status_proto: UStatusProto) -> Result<Self, Self::Error> {
446 let code = status_proto
449 .code
450 .enum_value()
451 .map_err(|e| SerializationError::new(format!("unsupported UCode {e}")))
452 .map(UCode::from)?;
453 let details = status_proto
454 .details
455 .into_iter()
456 .map(UAny::from)
457 .collect::<Vec<_>>();
458 Ok(UStatus::new(code, status_proto.message, details.into()))
459 }
460 }
461
462 impl From<&UStatus> for UStatusProto {
463 fn from(value: &UStatus) -> Self {
464 UStatusProto {
465 code: UCodeProto::from(value.code()).into(),
466 message: value.message().map(|m| m.to_string()),
467 details: value.details().iter().map(Any::from).collect(),
468 ..Default::default()
469 }
470 }
471 }
472
473 impl ProtobufMappable for UStatus {
474 fn parse_from_protobuf_bytes(proto: &[u8]) -> Result<Self, SerializationError> {
475 let proto = UStatusProto::parse_from_bytes(proto)?;
476 UStatus::try_from(proto)
477 }
478
479 fn parse_from_packed_protobuf_bytes(proto: &[u8]) -> Result<Self, SerializationError> {
480 let any = Any::parse_from_bytes(proto)?;
481 match any.unpack::<UStatusProto>() {
482 Ok(Some(v)) => UStatus::try_from(v),
483 Ok(None) => Err(SerializationError::new(
484 "cannot unpack UStatus, type mismatch",
485 )),
486 Err(e) => Err(SerializationError::from(e)),
487 }
488 }
489
490 fn write_to_protobuf_bytes(&self) -> Result<Vec<u8>, SerializationError> {
491 UStatusProto::from(self)
492 .write_to_bytes()
493 .map_err(SerializationError::from)
494 }
495
496 fn write_to_packed_protobuf_bytes(&self) -> Result<Vec<u8>, SerializationError> {
497 Any::pack(&UStatusProto::from(self))
498 .map_err(SerializationError::from)
499 .and_then(|any| any.write_to_protobuf_bytes())
500 }
501 }
502}
503
504#[cfg(test)]
505mod tests {
506 use super::*;
507
508 const ALL_UCODES: &[UCode] = &[
509 UCode::Ok,
510 UCode::Cancelled,
511 UCode::Unknown,
512 UCode::InvalidArgument,
513 UCode::DeadlineExceeded,
514 UCode::NotFound,
515 UCode::AlreadyExists,
516 UCode::PermissionDenied,
517 UCode::Unauthenticated,
518 UCode::ResourceExhausted,
519 UCode::FailedPrecondition,
520 UCode::Aborted,
521 UCode::OutOfRange,
522 UCode::Unimplemented,
523 UCode::Internal,
524 UCode::Unavailable,
525 UCode::DataLoss,
526 ];
527
528 #[test]
529 fn test_ustatus_fail_with_code() {
531 for code in ALL_UCODES {
532 let ustatus = UStatus::fail_with_code(*code, "the message");
533 assert!(ustatus.code() == *code && ustatus.message() == Some("the message"));
534 }
535 }
536
537 #[test]
538 #[cfg(feature = "up-core-api")]
540 fn test_proto_serialization() {
541 use crate::ProtobufMappable;
542
543 let ustatus = UStatus::fail_with_code(UCode::Cancelled, "the message");
544 let proto = ustatus
545 .write_to_protobuf_bytes()
546 .expect("failed to serialize to protobuf");
547 let deserialized_status = UStatus::parse_from_protobuf_bytes(proto.as_slice())
548 .expect("failed to deserialize protobuf");
549 assert_eq!(ustatus, deserialized_status);
550 }
551
552 #[test]
553 fn test_is_failed() {
554 assert!(!UStatus::ok().is_failed());
555 for code in ALL_UCODES {
556 let ustatus = UStatus::fail_with_code(*code, "the message");
557 assert_eq!(ustatus.is_failed(), *code != UCode::Ok);
558 }
559 }
560
561 #[test]
562 fn test_is_success() {
563 assert!(UStatus::ok().is_success());
564 for code in ALL_UCODES {
565 let ustatus = UStatus::fail_with_code(*code, "the message");
566 assert_eq!(ustatus.is_success(), *code == UCode::Ok);
567 }
568 }
569}