1use std::fmt;
2
3#[derive(Debug)]
10pub enum GrpcurlError {
11 NotFound(String),
14
15 ReflectionNotSupported,
18
19 InvalidArgument(String),
21
22 Io(std::io::Error),
24
25 Proto(String),
27
28 GrpcStatus(tonic::Status),
30
31 Other(Box<dyn std::error::Error + Send + Sync>),
33}
34
35impl fmt::Display for GrpcurlError {
36 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
37 match self {
38 GrpcurlError::NotFound(name) => write!(f, "Symbol not found: {name}"),
39 GrpcurlError::ReflectionNotSupported => {
40 write!(f, "server does not support the reflection API")
41 }
42 GrpcurlError::InvalidArgument(msg) => write!(f, "invalid argument: {msg}"),
43 GrpcurlError::Io(err) => write!(f, "I/O error: {err}"),
44 GrpcurlError::Proto(msg) => write!(f, "proto error: {msg}"),
45 GrpcurlError::GrpcStatus(status) => {
46 write!(f, "gRPC error: {} - {}", status.code(), status.message())
47 }
48 GrpcurlError::Other(err) => write!(f, "{err}"),
49 }
50 }
51}
52
53impl std::error::Error for GrpcurlError {
54 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
55 match self {
56 GrpcurlError::Io(err) => Some(err),
57 GrpcurlError::Other(err) => Some(err.as_ref()),
58 _ => None,
59 }
60 }
61}
62
63impl From<std::io::Error> for GrpcurlError {
64 fn from(err: std::io::Error) -> Self {
65 GrpcurlError::Io(err)
66 }
67}
68
69impl From<tonic::Status> for GrpcurlError {
70 fn from(status: tonic::Status) -> Self {
71 GrpcurlError::GrpcStatus(status)
72 }
73}
74
75pub type Result<T> = std::result::Result<T, GrpcurlError>;
77
78#[cfg(test)]
79mod tests {
80 use super::*;
81
82 fn is_not_found_error(err: &GrpcurlError) -> bool {
87 match err {
88 GrpcurlError::NotFound(_) => true,
89 GrpcurlError::GrpcStatus(status) => status.code() == tonic::Code::NotFound,
90 _ => false,
91 }
92 }
93
94 #[test]
95 fn not_found_error_detected() {
96 let err = GrpcurlError::NotFound("my.Service".into());
97 assert!(is_not_found_error(&err));
98 }
99
100 #[test]
101 fn grpc_not_found_detected() {
102 let status = tonic::Status::not_found("service not found");
103 let err = GrpcurlError::GrpcStatus(status);
104 assert!(is_not_found_error(&err));
105 }
106
107 #[test]
108 fn other_errors_not_detected_as_not_found() {
109 let err = GrpcurlError::InvalidArgument("bad input".into());
110 assert!(!is_not_found_error(&err));
111
112 let err = GrpcurlError::ReflectionNotSupported;
113 assert!(!is_not_found_error(&err));
114 }
115
116 #[test]
117 fn display_formatting() {
118 let err = GrpcurlError::NotFound("my.Service".into());
119 assert_eq!(err.to_string(), "Symbol not found: my.Service");
120
121 let err = GrpcurlError::ReflectionNotSupported;
122 assert_eq!(
123 err.to_string(),
124 "server does not support the reflection API"
125 );
126 }
127
128 #[test]
129 fn io_error_conversion() {
130 let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "file missing");
131 let err: GrpcurlError = io_err.into();
132 assert!(matches!(err, GrpcurlError::Io(_)));
133 }
134}