use thiserror::Error;
#[derive(Debug, Error)]
pub enum TunnelError {
#[error("Protocol error: {message}")]
Protocol {
message: String,
},
#[error("Authentication error: {reason}")]
Auth {
reason: String,
},
#[error("Connection error: {source}")]
Connection {
#[from]
source: std::io::Error,
},
#[error("Registry error: {message}")]
Registry {
message: String,
},
#[error("Configuration error: {message}")]
Config {
message: String,
},
#[error("Operation timed out")]
Timeout,
#[error("Service is shutting down")]
Shutdown,
}
impl TunnelError {
#[must_use]
pub fn protocol(message: impl Into<String>) -> Self {
Self::Protocol {
message: message.into(),
}
}
#[must_use]
pub fn auth(reason: impl Into<String>) -> Self {
Self::Auth {
reason: reason.into(),
}
}
#[must_use]
pub fn registry(message: impl Into<String>) -> Self {
Self::Registry {
message: message.into(),
}
}
#[must_use]
pub fn config(message: impl Into<String>) -> Self {
Self::Config {
message: message.into(),
}
}
#[must_use]
pub fn connection<E: std::error::Error>(err: E) -> Self {
Self::Connection {
source: std::io::Error::other(err.to_string()),
}
}
#[must_use]
pub fn connection_msg(message: impl Into<String>) -> Self {
Self::Connection {
source: std::io::Error::other(message.into()),
}
}
#[must_use]
pub fn timeout() -> Self {
Self::Timeout
}
}
pub type Result<T> = std::result::Result<T, TunnelError>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_error_display() {
let err = TunnelError::protocol("invalid message type");
assert_eq!(err.to_string(), "Protocol error: invalid message type");
let err = TunnelError::auth("token expired");
assert_eq!(err.to_string(), "Authentication error: token expired");
let err = TunnelError::registry("tunnel not found");
assert_eq!(err.to_string(), "Registry error: tunnel not found");
let err = TunnelError::config("missing server_url");
assert_eq!(err.to_string(), "Configuration error: missing server_url");
let err = TunnelError::Timeout;
assert_eq!(err.to_string(), "Operation timed out");
let err = TunnelError::Shutdown;
assert_eq!(err.to_string(), "Service is shutting down");
}
#[test]
fn test_io_error_conversion() {
let io_err = std::io::Error::new(std::io::ErrorKind::ConnectionRefused, "refused");
let tunnel_err: TunnelError = io_err.into();
assert!(matches!(tunnel_err, TunnelError::Connection { .. }));
}
}