From 56d032afdba32319cfc63eb512ff119e52d7ae65 Mon Sep 17 00:00:00 2001 From: "glm-5.1" Date: Tue, 2 Jun 2026 09:16:23 +0000 Subject: [PATCH] Define error types for transport, auth, channel, and config layers --- crates/wraith-core/src/error.rs | 153 ++++++++++++++++++++++++++++++++ crates/wraith-core/src/lib.rs | 7 +- 2 files changed, 159 insertions(+), 1 deletion(-) diff --git a/crates/wraith-core/src/error.rs b/crates/wraith-core/src/error.rs index e69de29..459a204 100644 --- a/crates/wraith-core/src/error.rs +++ b/crates/wraith-core/src/error.rs @@ -0,0 +1,153 @@ +use std::io; + +#[derive(Debug, thiserror::Error)] +pub enum TransportError { + #[error("connection failed")] + ConnectionFailed, + #[error("handshake failed")] + HandshakeFailed { + #[source] + source: io::Error, + }, + #[error("transport timeout")] + Timeout, + #[error("proxy failed")] + ProxyFailed { + #[source] + source: io::Error, + }, +} + +#[derive(Debug, thiserror::Error)] +pub enum AuthError { + #[error("key rejected")] + KeyRejected, + #[error("certificate invalid")] + CertInvalid, + #[error("certificate expired")] + CertExpired, + #[error("certificate principal mismatch")] + CertPrincipalMismatch, + #[error("no matching key")] + NoMatchingKey, +} + +#[derive(Debug, thiserror::Error)] +pub enum ChannelError { + #[error("target unreachable")] + TargetUnreachable, + #[error("proxy connect failed")] + ProxyConnectFailed { + #[source] + source: io::Error, + }, + #[error("channel closed")] + ChannelClosed, +} + +#[derive(Debug, thiserror::Error)] +pub enum ConfigError { + #[error("invalid flag: {name}")] + InvalidFlag { name: String }, + #[error("key file not found: {path}")] + KeyFileNotFound { path: String }, + #[error("bind failed")] + BindFailed { + #[source] + source: io::Error, + }, + #[error("incompatible options")] + IncompatibleOptions, +} + +#[cfg(test)] +mod tests { + use super::*; + use std::error::Error; + + #[test] + fn transport_error_display() { + assert_eq!(TransportError::ConnectionFailed.to_string(), "connection failed"); + assert_eq!( + TransportError::HandshakeFailed { + source: io::Error::new(io::ErrorKind::ConnectionRefused, "tls failed") + } + .to_string(), + "handshake failed" + ); + assert_eq!(TransportError::Timeout.to_string(), "transport timeout"); + assert_eq!( + TransportError::ProxyFailed { + source: io::Error::new(io::ErrorKind::ConnectionRefused, "proxy err") + } + .to_string(), + "proxy failed" + ); + } + + #[test] + fn auth_error_display() { + assert_eq!(AuthError::KeyRejected.to_string(), "key rejected"); + assert_eq!(AuthError::CertInvalid.to_string(), "certificate invalid"); + assert_eq!(AuthError::CertExpired.to_string(), "certificate expired"); + assert_eq!(AuthError::CertPrincipalMismatch.to_string(), "certificate principal mismatch"); + assert_eq!(AuthError::NoMatchingKey.to_string(), "no matching key"); + } + + #[test] + fn channel_error_display() { + assert_eq!(ChannelError::TargetUnreachable.to_string(), "target unreachable"); + assert_eq!( + ChannelError::ProxyConnectFailed { + source: io::Error::new(io::ErrorKind::ConnectionRefused, "refused") + } + .to_string(), + "proxy connect failed" + ); + assert_eq!(ChannelError::ChannelClosed.to_string(), "channel closed"); + } + + #[test] + fn config_error_display() { + assert_eq!( + ConfigError::InvalidFlag { + name: "--bad".to_string() + } + .to_string(), + "invalid flag: --bad" + ); + assert_eq!( + ConfigError::KeyFileNotFound { + path: "/missing".to_string() + } + .to_string(), + "key file not found: /missing" + ); + assert_eq!( + ConfigError::BindFailed { + source: io::Error::new(io::ErrorKind::AddrInUse, "in use") + } + .to_string(), + "bind failed" + ); + assert_eq!(ConfigError::IncompatibleOptions.to_string(), "incompatible options"); + } + + #[test] + fn error_source_chaining() { + let io_err = io::Error::new(io::ErrorKind::ConnectionRefused, "refused"); + let transport_err = TransportError::HandshakeFailed { source: io_err }; + assert!(transport_err.source().is_some()); + + let io_err = io::Error::new(io::ErrorKind::ConnectionRefused, "proxy"); + let channel_err = ChannelError::ProxyConnectFailed { source: io_err }; + assert!(channel_err.source().is_some()); + + let io_err = io::Error::new(io::ErrorKind::AddrInUse, "addr"); + let config_err = ConfigError::BindFailed { source: io_err }; + assert!(config_err.source().is_some()); + + let plain = AuthError::KeyRejected; + assert!(plain.source().is_none()); + } +} \ No newline at end of file diff --git a/crates/wraith-core/src/lib.rs b/crates/wraith-core/src/lib.rs index 4b38593..88111fc 100644 --- a/crates/wraith-core/src/lib.rs +++ b/crates/wraith-core/src/lib.rs @@ -3,4 +3,9 @@ pub mod client; pub mod server; pub mod auth; pub mod socks5; -pub mod error; \ No newline at end of file +pub mod error; + +#[cfg(feature = "testutil")] +pub mod testutil; + +pub use error::{AuthError, ChannelError, ConfigError, TransportError}; \ No newline at end of file