use core::fmt;
use http::header::{HeaderMap, HeaderValue};
use percent_encoding::{AsciiSet, NON_ALPHANUMERIC, utf8_percent_encode};
use super::status::GrpcStatus;
const GRPC_STATUS: http::header::HeaderName = http::header::HeaderName::from_static("grpc-status");
const GRPC_MESSAGE: http::header::HeaderName = http::header::HeaderName::from_static("grpc-message");
const GRPC_MESSAGE_ENCODE_SET: &AsciiSet = &NON_ALPHANUMERIC.remove(b'-').remove(b'_').remove(b'.').remove(b'~');
#[derive(Debug)]
pub enum ProtocolError {
IncompleteFrame,
MessageTooLarge { size: usize, limit: usize },
Decode(prost::DecodeError),
Encode(prost::EncodeError),
CompressedWithoutEncoding,
Compress(String),
CompressUnsupported,
}
impl fmt::Display for ProtocolError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Self::IncompleteFrame => f.write_str("incomplete grpc frame"),
Self::MessageTooLarge { size, limit } => {
write!(f, "grpc message size {size} exceeds limit {limit}")
}
Self::Decode(ref e) => write!(f, "protobuf decode: {e}"),
Self::Encode(ref e) => write!(f, "protobuf encode: {e}"),
Self::CompressedWithoutEncoding => f.write_str("compressed flag set but no grpc-encoding configured"),
Self::Compress(ref e) => write!(f, "compression error: {e}"),
Self::CompressUnsupported => f.write_str("grpc compression not supported"),
}
}
}
impl core::error::Error for ProtocolError {}
pub struct GrpcError {
pub status: GrpcStatus,
pub message: Option<String>,
}
impl GrpcError {
pub fn new(status: GrpcStatus, msg: impl Into<String>) -> Self {
Self {
status,
message: Some(msg.into()),
}
}
pub fn status(status: GrpcStatus) -> Self {
Self { status, message: None }
}
pub fn trailers(&self) -> HeaderMap {
let mut map = HeaderMap::with_capacity(2);
map.insert(GRPC_STATUS, HeaderValue::from(self.status as u16));
if let Some(ref msg) = self.message {
let encoded = utf8_percent_encode(msg, GRPC_MESSAGE_ENCODE_SET).to_string();
if let Ok(v) = HeaderValue::from_str(&encoded) {
map.insert(GRPC_MESSAGE, v);
}
}
map
}
}
impl fmt::Debug for GrpcError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("GrpcError")
.field("status", &self.status)
.field("message", &self.message)
.finish()
}
}
impl fmt::Display for GrpcError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "gRPC error {:?}", self.status)?;
if let Some(ref msg) = self.message {
write!(f, ": {msg}")?;
}
Ok(())
}
}
impl core::error::Error for GrpcError {}