1 // Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.
2 
3 use std::ffi::CString;
4 use std::{error, fmt, result};
5 
6 use crate::call::RpcStatus;
7 use crate::grpc_sys::grpc_call_error;
8 
9 #[cfg(feature = "protobuf-codec")]
10 use protobuf::ProtobufError;
11 
12 #[cfg(feature = "protobufv3-codec")]
13 use protobufv3::Error as ProtobufError;
14 
15 /// Errors generated from this library.
16 #[derive(Debug)]
17 pub enum Error {
18     /// Codec error.
19     Codec(Box<dyn error::Error + Send + Sync>),
20     /// Failed to start an internal async call.
21     CallFailure(grpc_call_error),
22     /// Rpc request fail.
23     RpcFailure(RpcStatus),
24     /// Try to write to a finished rpc call.
25     RpcFinished(Option<RpcStatus>),
26     /// Remote is stopped.
27     RemoteStopped,
28     /// Failed to shutdown.
29     ShutdownFailed,
30     /// Failed to bind.
31     BindFail(CString),
32     /// gRPC completion queue is shutdown.
33     QueueShutdown,
34     /// Failed to create Google default credentials.
35     GoogleAuthenticationFailed,
36     /// Invalid format of metadata.
37     InvalidMetadata(String),
38 }
39 
40 impl fmt::Display for Error {
fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result41     fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
42         match self {
43             Error::RpcFailure(s) => {
44                 if s.message().is_empty() {
45                     write!(fmt, "RpcFailure: {}", s.code())
46                 } else {
47                     write!(fmt, "RpcFailure: {} {}", s.code(), s.message())
48                 }
49             }
50             other_error => write!(fmt, "{other_error:?}"),
51         }
52     }
53 }
54 
55 impl error::Error for Error {
source(&self) -> Option<&(dyn error::Error + 'static)>56     fn source(&self) -> Option<&(dyn error::Error + 'static)> {
57         match *self {
58             Error::Codec(ref e) => Some(e.as_ref()),
59             _ => None,
60         }
61     }
62 }
63 
64 #[cfg(any(feature = "protobuf-codec", feature = "protobufv3-codec"))]
65 impl From<ProtobufError> for Error {
from(e: ProtobufError) -> Error66     fn from(e: ProtobufError) -> Error {
67         Error::Codec(Box::new(e))
68     }
69 }
70 
71 #[cfg(feature = "prost-codec")]
72 impl From<prost::DecodeError> for Error {
from(e: prost::DecodeError) -> Error73     fn from(e: prost::DecodeError) -> Error {
74         Error::Codec(Box::new(e))
75     }
76 }
77 
78 #[cfg(feature = "prost-codec")]
79 impl From<prost::EncodeError> for Error {
from(e: prost::EncodeError) -> Error80     fn from(e: prost::EncodeError) -> Error {
81         Error::Codec(Box::new(e))
82     }
83 }
84 
85 /// Type alias to use this library's [`Error`] type in a `Result`.
86 pub type Result<T> = result::Result<T, Error>;
87 
88 #[cfg(all(test, feature = "protobuf-codec"))]
89 mod tests {
90     use std::error::Error as StdError;
91 
92     use protobuf::error::WireError;
93     use protobuf::ProtobufError;
94 
95     use super::Error;
96 
97     #[test]
test_convert()98     fn test_convert() {
99         let error = ProtobufError::WireError(WireError::UnexpectedEof);
100         let e: Error = error.into();
101         assert_eq!(e.to_string(), "Codec(WireError(UnexpectedEof))");
102         assert!(e.source().is_some());
103     }
104 }
105