mod code; use base64::Engine as _; pub use code::Code; use serde::{Deserialize, Serialize}; use crate::metadata::Metadata; /// Connect protocol error body /// /// See: https://connectrpc.com/docs/protocol/#error-codes #[derive(Clone, Debug, Serialize, Deserialize)] pub struct Status { /// The error code pub code: Code, /// Optional user-facing error message #[serde(default, skip_serializing_if = "String::is_empty")] pub message: String, /// Optional error details #[serde(default, skip_serializing_if = "Vec::is_empty")] pub details: Vec, } impl Status { pub fn new(code: Code, message: impl Into) -> Self { let mut message = message.into(); if message.is_empty() { message = code.to_string(); } Self { code, message, details: Default::default(), } } } impl std::fmt::Display for Status { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "status code {} ({:?})", self.code as i32, self.code)?; if !self.message.is_empty() { write!(f, ": {}", self.message)?; } Ok(()) } } impl std::error::Error for Status {} /// Connect protocol error response #[derive(Clone, Debug)] pub struct StatusError { pub status: Status, pub metadata: Metadata, } impl StatusError { pub fn new(code: Code, message: impl Into) -> Self { Self { status: Status::new(code, message), metadata: Default::default(), } } } impl std::fmt::Display for StatusError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.status.fmt(f) } } impl std::error::Error for StatusError {} pub type JsonObject = serde_json::Map; /// Error detail, used by [`Status::details`]. #[derive(Clone, Debug, Serialize, Deserialize)] pub struct Detail { /// The fully-qualified protobuf message type name. /// /// Ex: "google.rpc.RetryInfo" #[serde(rename = "type")] pub message_type: String, /// The base64-encoded binary protobuf message. #[serde(rename = "value")] pub value_base64: String, /// Optional debug representation of the message for human consumption. #[serde(default, skip_serializing_if = "Option::is_none")] pub debug: Option, } impl Detail { /// Returns the bytes of the detail value decoded from base64. pub fn decode_value(&self) -> Result, base64::DecodeError> { base64::engine::general_purpose::STANDARD_NO_PAD.decode(&self.value_base64) } }