use thiserror::Error;
#[derive(Debug, Error)]
pub enum NetworkError {
#[error("Connection failed to {addr}: {reason}")]
ConnectionFailed { addr: String, reason: String },
#[error("Connection timeout to {addr} after {timeout_ms}ms")]
ConnectionTimeout { addr: String, timeout_ms: u64 },
#[error("Connection closed: {addr}")]
ConnectionClosed { addr: String },
#[error("Send failed to {addr}: {reason}")]
SendFailed { addr: String, reason: String },
#[error("Receive failed from {addr}: {reason}")]
ReceiveFailed { addr: String, reason: String },
#[error("Invalid address format: {addr}")]
InvalidAddress { addr: String },
#[error("DNS resolution failed for {host}: {reason}")]
DnsResolutionFailed { host: String, reason: String },
#[error("Too many requests to {addr}, limit: {limit}")]
TooManyRequests { addr: String, limit: usize },
#[error("Request timeout to {addr} after {timeout_ms}ms")]
RequestTimeout { addr: String, timeout_ms: u64 },
}
impl NetworkError {
#[inline]
pub fn connection_failed(addr: impl Into<String>, reason: impl Into<String>) -> Self {
Self::ConnectionFailed {
addr: addr.into(),
reason: reason.into(),
}
}
#[inline]
pub fn connection_timeout(addr: impl Into<String>, timeout_ms: u64) -> Self {
Self::ConnectionTimeout {
addr: addr.into(),
timeout_ms,
}
}
#[inline]
pub fn send_failed(addr: impl Into<String>, reason: impl Into<String>) -> Self {
Self::SendFailed {
addr: addr.into(),
reason: reason.into(),
}
}
#[inline]
pub fn request_timeout(addr: impl Into<String>, timeout_ms: u64) -> Self {
Self::RequestTimeout {
addr: addr.into(),
timeout_ms,
}
}
pub fn addr(&self) -> &str {
match self {
Self::ConnectionFailed { addr, .. }
| Self::ConnectionTimeout { addr, .. }
| Self::ConnectionClosed { addr }
| Self::SendFailed { addr, .. }
| Self::ReceiveFailed { addr, .. }
| Self::InvalidAddress { addr }
| Self::TooManyRequests { addr, .. }
| Self::RequestTimeout { addr, .. } => addr,
Self::DnsResolutionFailed { host, .. } => host,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_network_error_creation() {
let err = NetworkError::connection_failed("127.0.0.1:9876", "timeout");
assert_eq!(err.addr(), "127.0.0.1:9876");
assert!(err.to_string().contains("Connection failed"));
}
#[test]
fn test_network_error_display() {
let err = NetworkError::ConnectionTimeout {
addr: "localhost:10911".to_string(),
timeout_ms: 3000,
};
assert_eq!(err.to_string(), "Connection timeout to localhost:10911 after 3000ms");
}
#[test]
fn test_network_error_connection_closed() {
let err = NetworkError::ConnectionClosed {
addr: "localhost:10911".to_string(),
};
assert_eq!(err.to_string(), "Connection closed: localhost:10911");
}
#[test]
fn test_network_error_send_failed() {
let err = NetworkError::SendFailed {
addr: "localhost:10911".to_string(),
reason: "internal error".to_string(),
};
assert_eq!(err.to_string(), "Send failed to localhost:10911: internal error");
}
#[test]
fn test_network_error_receive_failed() {
let err = NetworkError::ReceiveFailed {
addr: "localhost:10911".to_string(),
reason: "internal error".to_string(),
};
assert_eq!(err.to_string(), "Receive failed from localhost:10911: internal error");
}
#[test]
fn test_network_error_invalid_address() {
let err = NetworkError::InvalidAddress {
addr: "localhost:10911".to_string(),
};
assert_eq!(err.to_string(), "Invalid address format: localhost:10911");
}
#[test]
fn test_network_error_too_many_requests() {
let err = NetworkError::TooManyRequests {
addr: "localhost:10911".to_string(),
limit: 5,
};
assert_eq!(err.to_string(), "Too many requests to localhost:10911, limit: 5");
}
#[test]
fn test_network_error_request_timeout() {
let err = NetworkError::RequestTimeout {
addr: "localhost:10911".to_string(),
timeout_ms: 100,
};
assert_eq!(err.to_string(), "Request timeout to localhost:10911 after 100ms");
}
#[test]
fn test_network_error_dns_resolution_failed() {
let err = NetworkError::DnsResolutionFailed {
host: "example.com".to_string(),
reason: "host not found".to_string(),
};
assert_eq!(err.to_string(), "DNS resolution failed for example.com: host not found");
}
}