Skip to main content

zlayer_tunnel/
error.rs

1//! Error types for tunnel operations
2
3use thiserror::Error;
4
5/// Errors that can occur during tunnel operations
6#[derive(Debug, Error)]
7pub enum TunnelError {
8    /// Protocol-level error (invalid message format, decode failure)
9    #[error("Protocol error: {message}")]
10    Protocol {
11        /// Error message describing the protocol violation
12        message: String,
13    },
14
15    /// Authentication error (invalid token, expired, unauthorized)
16    #[error("Authentication error: {reason}")]
17    Auth {
18        /// Reason for authentication failure
19        reason: String,
20    },
21
22    /// Connection error (connection refused, timeout, closed)
23    #[error("Connection error: {source}")]
24    Connection {
25        /// Underlying I/O error
26        #[from]
27        source: std::io::Error,
28    },
29
30    /// Registry error (tunnel not found, service not found, already exists)
31    #[error("Registry error: {message}")]
32    Registry {
33        /// Error message describing the registry issue
34        message: String,
35    },
36
37    /// Configuration error (invalid config, missing required field)
38    #[error("Configuration error: {message}")]
39    Config {
40        /// Error message describing the configuration issue
41        message: String,
42    },
43
44    /// Operation timed out
45    #[error("Operation timed out")]
46    Timeout,
47
48    /// Service is shutting down
49    #[error("Service is shutting down")]
50    Shutdown,
51}
52
53impl TunnelError {
54    /// Create a new protocol error
55    #[must_use]
56    pub fn protocol(message: impl Into<String>) -> Self {
57        Self::Protocol {
58            message: message.into(),
59        }
60    }
61
62    /// Create a new authentication error
63    #[must_use]
64    pub fn auth(reason: impl Into<String>) -> Self {
65        Self::Auth {
66            reason: reason.into(),
67        }
68    }
69
70    /// Create a new registry error
71    #[must_use]
72    pub fn registry(message: impl Into<String>) -> Self {
73        Self::Registry {
74            message: message.into(),
75        }
76    }
77
78    /// Create a new configuration error
79    #[must_use]
80    pub fn config(message: impl Into<String>) -> Self {
81        Self::Config {
82            message: message.into(),
83        }
84    }
85
86    /// Create a new connection error from any error type
87    #[must_use]
88    pub fn connection<E: std::error::Error>(err: E) -> Self {
89        Self::Connection {
90            source: std::io::Error::other(err.to_string()),
91        }
92    }
93
94    /// Create a new connection error with a message
95    #[must_use]
96    pub fn connection_msg(message: impl Into<String>) -> Self {
97        Self::Connection {
98            source: std::io::Error::other(message.into()),
99        }
100    }
101
102    /// Create a timeout error
103    #[must_use]
104    pub fn timeout() -> Self {
105        Self::Timeout
106    }
107}
108
109/// Result type alias for tunnel operations
110pub type Result<T> = std::result::Result<T, TunnelError>;
111
112#[cfg(test)]
113mod tests {
114    use super::*;
115
116    #[test]
117    fn test_error_display() {
118        let err = TunnelError::protocol("invalid message type");
119        assert_eq!(err.to_string(), "Protocol error: invalid message type");
120
121        let err = TunnelError::auth("token expired");
122        assert_eq!(err.to_string(), "Authentication error: token expired");
123
124        let err = TunnelError::registry("tunnel not found");
125        assert_eq!(err.to_string(), "Registry error: tunnel not found");
126
127        let err = TunnelError::config("missing server_url");
128        assert_eq!(err.to_string(), "Configuration error: missing server_url");
129
130        let err = TunnelError::Timeout;
131        assert_eq!(err.to_string(), "Operation timed out");
132
133        let err = TunnelError::Shutdown;
134        assert_eq!(err.to_string(), "Service is shutting down");
135    }
136
137    #[test]
138    fn test_io_error_conversion() {
139        let io_err = std::io::Error::new(std::io::ErrorKind::ConnectionRefused, "refused");
140        let tunnel_err: TunnelError = io_err.into();
141        assert!(matches!(tunnel_err, TunnelError::Connection { .. }));
142    }
143}