use std::fmt::Display;
use prost::EncodeError;
pub use prost_types::Any;
use thiserror::Error;
pub use super::Code;
use crate::io::SendError;
use crate::types::encoding::DecodeError;
use crate::types::flags::Flags;
use crate::types::message::MessageType;
#[derive(Clone, PartialEq, prost::Message, Error)]
#[error("Error code {}: {message}", code_to_str(*.code))]
pub struct Status {
#[prost(enumeration = "Code")]
pub code: i32,
#[prost(string)]
pub message: String,
#[prost(message, repeated)]
pub details: Vec<Any>,
}
impl Status {
pub fn new(code: Code, message: impl Into<String>) -> Self {
let code = code as i32;
let message = message.into();
let details = vec![];
Self {
code,
message,
details,
}
}
fn invalid_argument(message: impl Into<String>) -> Self {
Status {
code: Code::InvalidArgument as i32,
message: message.into(),
details: vec![],
}
}
fn not_found(message: impl Into<String>) -> Self {
Status {
code: Code::NotFound as i32,
message: message.into(),
details: vec![],
}
}
fn internal(message: impl Into<String>) -> Self {
Status {
code: Code::Internal as i32,
message: message.into(),
details: vec![],
}
}
fn deadline_exceeded(message: impl Into<String>) -> Self {
Status {
code: Code::DeadlineExceeded as i32,
message: message.into(),
details: vec![],
}
}
fn unknown(message: impl Into<String>) -> Self {
Status {
code: Code::Unknown as i32,
message: message.into(),
details: vec![],
}
}
fn aborted(message: impl Into<String>) -> Self {
Status {
code: Code::Aborted as i32,
message: message.into(),
details: vec![],
}
}
pub(crate) fn stream_in_use(stream_id: u32) -> Self {
Self::invalid_argument(format!("Stream `{stream_id}` is already in use"))
}
pub(crate) fn invalid_stream_id(stream_id: u32) -> Self {
Self::invalid_argument(format!("Stream id must be odd, found `{stream_id}`"))
}
pub(crate) fn stream_closed(stream_id: u32) -> Self {
Self::invalid_argument(format!("Channel on stream `{stream_id}` is closed"))
}
pub(crate) fn channel_closed() -> Self {
Self::aborted("Channel closed")
}
pub(crate) fn expected_request(stream_id: u32, ty: MessageType) -> Self {
const TY: MessageType = MessageType::Request;
let msg = format!("Invalid message type {ty:?} on stream `{stream_id}`, expected {TY:?}",);
Self::invalid_argument(msg)
}
pub(crate) fn method_not_found(service: impl Display, method: impl Display) -> Self {
let msg = format!("/{service}/{method} is not supported");
Self::not_found(msg)
}
pub(crate) fn failed_to_encode(err: &EncodeError) -> Self {
Status::invalid_argument(format!("Error encoding message: {err}"))
}
pub(crate) fn failed_to_decode(err: &DecodeError) -> Self {
Status::invalid_argument(format!("Error decoding message: {err}"))
}
pub(crate) fn invalid_request_flags(expected: Flags, actual: Flags) -> Self {
Status::invalid_argument(format!(
"Invalid request flags. Expected {expected:?}, found {actual:?}"
))
}
pub(crate) fn timeout() -> Self {
Status::deadline_exceeded("Request timed out")
}
pub fn from_error(err: impl std::error::Error) -> Self {
Status::unknown(err.to_string())
}
}
impl From<DecodeError> for Status {
fn from(value: DecodeError) -> Self {
Self::failed_to_decode(&value)
}
}
impl From<EncodeError> for Status {
fn from(value: EncodeError) -> Self {
Self::failed_to_encode(&value)
}
}
impl From<SendError> for Status {
fn from(value: SendError) -> Self {
Self::internal(format!("{value}"))
}
}
fn code_to_str(code: i32) -> &'static str {
let Ok(code) = Code::try_from(code) else {
return "<None>";
};
code.as_str_name()
}