1use core::fmt;
2
3use http::header::{HeaderMap, HeaderValue};
4use percent_encoding::{AsciiSet, NON_ALPHANUMERIC, utf8_percent_encode};
5
6use super::status::GrpcStatus;
7
8const GRPC_STATUS: http::header::HeaderName = http::header::HeaderName::from_static("grpc-status");
9const GRPC_MESSAGE: http::header::HeaderName = http::header::HeaderName::from_static("grpc-message");
10
11const GRPC_MESSAGE_ENCODE_SET: &AsciiSet = &NON_ALPHANUMERIC.remove(b'-').remove(b'_').remove(b'.').remove(b'~');
13
14#[derive(Debug)]
16pub enum ProtocolError {
17 IncompleteFrame,
19 MessageTooLarge { size: usize, limit: usize },
21 Decode(prost::DecodeError),
23 Encode(prost::EncodeError),
25 CompressedWithoutEncoding,
27 Compress(String),
29 CompressUnsupported,
31}
32
33impl fmt::Display for ProtocolError {
34 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
35 match *self {
36 Self::IncompleteFrame => f.write_str("incomplete grpc frame"),
37 Self::MessageTooLarge { size, limit } => {
38 write!(f, "grpc message size {size} exceeds limit {limit}")
39 }
40 Self::Decode(ref e) => write!(f, "protobuf decode: {e}"),
41 Self::Encode(ref e) => write!(f, "protobuf encode: {e}"),
42 Self::CompressedWithoutEncoding => f.write_str("compressed flag set but no grpc-encoding configured"),
43 Self::Compress(ref e) => write!(f, "compression error: {e}"),
44 Self::CompressUnsupported => f.write_str("grpc compression not supported"),
45 }
46 }
47}
48
49impl core::error::Error for ProtocolError {}
50
51pub struct GrpcError {
54 pub status: GrpcStatus,
55 pub message: Option<String>,
56}
57
58impl GrpcError {
59 pub fn new(status: GrpcStatus, msg: impl Into<String>) -> Self {
60 Self {
61 status,
62 message: Some(msg.into()),
63 }
64 }
65
66 pub fn status(status: GrpcStatus) -> Self {
67 Self { status, message: None }
68 }
69
70 pub fn trailers(&self) -> HeaderMap {
72 let mut map = HeaderMap::with_capacity(2);
73 map.insert(GRPC_STATUS, HeaderValue::from(self.status as u16));
74 if let Some(ref msg) = self.message {
75 let encoded = utf8_percent_encode(msg, GRPC_MESSAGE_ENCODE_SET).to_string();
76 if let Ok(v) = HeaderValue::from_str(&encoded) {
77 map.insert(GRPC_MESSAGE, v);
78 }
79 }
80 map
81 }
82}
83
84impl fmt::Debug for GrpcError {
85 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
86 f.debug_struct("GrpcError")
87 .field("status", &self.status)
88 .field("message", &self.message)
89 .finish()
90 }
91}
92
93impl fmt::Display for GrpcError {
94 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
95 write!(f, "gRPC error {:?}", self.status)?;
96 if let Some(ref msg) = self.message {
97 write!(f, ": {msg}")?;
98 }
99 Ok(())
100 }
101}
102
103impl core::error::Error for GrpcError {}