1use thiserror::Error;
4
5#[derive(Debug, Error)]
7pub enum TunnelError {
8 #[error("Protocol error: {message}")]
10 Protocol {
11 message: String,
13 },
14
15 #[error("Authentication error: {reason}")]
17 Auth {
18 reason: String,
20 },
21
22 #[error("Connection error: {source}")]
24 Connection {
25 #[from]
27 source: std::io::Error,
28 },
29
30 #[error("Registry error: {message}")]
32 Registry {
33 message: String,
35 },
36
37 #[error("Configuration error: {message}")]
39 Config {
40 message: String,
42 },
43
44 #[error("Operation timed out")]
46 Timeout,
47
48 #[error("Service is shutting down")]
50 Shutdown,
51}
52
53impl TunnelError {
54 #[must_use]
56 pub fn protocol(message: impl Into<String>) -> Self {
57 Self::Protocol {
58 message: message.into(),
59 }
60 }
61
62 #[must_use]
64 pub fn auth(reason: impl Into<String>) -> Self {
65 Self::Auth {
66 reason: reason.into(),
67 }
68 }
69
70 #[must_use]
72 pub fn registry(message: impl Into<String>) -> Self {
73 Self::Registry {
74 message: message.into(),
75 }
76 }
77
78 #[must_use]
80 pub fn config(message: impl Into<String>) -> Self {
81 Self::Config {
82 message: message.into(),
83 }
84 }
85
86 #[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 #[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 #[must_use]
104 pub fn timeout() -> Self {
105 Self::Timeout
106 }
107}
108
109pub 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}