Skip to main content

vector_api_client/
error.rs

1use snafu::Snafu;
2
3/// Error types for the gRPC client
4#[derive(Debug, Snafu)]
5#[snafu(visibility(pub))]
6pub enum Error {
7    #[snafu(display("Connection error: {}", source))]
8    Connection { source: tonic::transport::Error },
9
10    #[snafu(display("gRPC error: {}", source))]
11    Grpc { source: tonic::Status },
12
13    #[snafu(display("Invalid URL: {}", message))]
14    InvalidUrl { message: String },
15
16    #[snafu(display("Not connected to gRPC server"))]
17    NotConnected,
18
19    #[snafu(display("Server is not serving (status: {})", status))]
20    NotServing { status: i32 },
21
22    #[snafu(display("Stream error: {}", message))]
23    Stream { message: String },
24}
25
26impl Error {
27    /// Returns true if this error is permanent and should not be retried.
28    pub fn is_fatal(&self) -> bool {
29        match self {
30            Error::InvalidUrl { .. } => true,
31            Error::Grpc { source } => matches!(
32                source.code(),
33                tonic::Code::InvalidArgument
34                    | tonic::Code::OutOfRange
35                    | tonic::Code::NotFound
36                    | tonic::Code::PermissionDenied
37                    | tonic::Code::Unauthenticated
38                    | tonic::Code::Unimplemented
39            ),
40            _ => false,
41        }
42    }
43}
44
45impl From<tonic::Status> for Error {
46    fn from(source: tonic::Status) -> Self {
47        Error::Grpc { source }
48    }
49}
50
51impl From<tonic::transport::Error> for Error {
52    fn from(source: tonic::transport::Error) -> Self {
53        Error::Connection { source }
54    }
55}
56
57pub type Result<T> = std::result::Result<T, Error>;
58
59#[cfg(test)]
60mod tests {
61    use super::*;
62
63    #[test]
64    fn test_error_display() {
65        let err = Error::NotConnected;
66        assert_eq!(err.to_string(), "Not connected to gRPC server");
67
68        let err = Error::InvalidUrl {
69            message: "invalid format".to_string(),
70        };
71        assert_eq!(err.to_string(), "Invalid URL: invalid format");
72    }
73
74    #[test]
75    fn test_error_from_tonic_status() {
76        let status = tonic::Status::unavailable("service down");
77        let err: Error = status.into();
78        assert!(matches!(err, Error::Grpc { .. }));
79    }
80}