use core::fmt;
use smoltcp::socket::{
icmp::BindError as IcmpBindError,
raw::BindError as RawBindError,
tcp::{ConnectError, ListenError, SendError as TcpSendError},
udp::{BindError as UdpBindError, SendError as UdpSendError},
};
use crate::command;
#[derive(thiserror::Error, Debug, Clone, Copy, Eq, PartialEq)]
pub enum Error {
#[error("invalid request ({0})")]
BadRequest(BadRequestReason),
#[error("an internal error occurred: {0}")]
Internal(InternalErrorKind),
#[error("connection reset")]
ConnectionReset,
}
impl Error {
pub fn wrong_type() -> Self {
Error::Internal(InternalErrorKind::InternalResponseMismatch)
}
pub fn invalid_socket_state() -> Self {
Error::Internal(InternalErrorKind::InvalidSocketState)
}
pub fn missing_listener() -> Self {
Error::Internal(InternalErrorKind::BadListenerHandle)
}
pub fn missing_socket() -> Self {
Error::Internal(InternalErrorKind::BadSocketHandle)
}
pub fn big_packet() -> Self {
Error::BadRequest(BadRequestReason::BigPacket)
}
pub fn buffer_full() -> Self {
Error::Internal(InternalErrorKind::BufferFull)
}
pub fn unaddressable() -> Self {
Error::BadRequest(BadRequestReason::Unaddressable)
}
pub fn zero_buffer() -> Self {
Error::BadRequest(BadRequestReason::ZeroSizeBuffer)
}
}
#[non_exhaustive]
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum InternalErrorKind {
InvalidSocketState,
InternalResponseMismatch,
InternalChannelClosed,
BadListenerHandle,
BadSocketHandle,
BufferFull,
}
impl fmt::Display for InternalErrorKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
InternalErrorKind::InvalidSocketState => write!(f, "invalid socket state"),
InternalErrorKind::InternalResponseMismatch => {
write!(f, "response type mismatched to request type")
}
InternalErrorKind::InternalChannelClosed => write!(f, "channel closed"),
InternalErrorKind::BadListenerHandle => write!(f, "handle to invalid TCP listener"),
InternalErrorKind::BadSocketHandle => write!(f, "handle to invalid socket"),
InternalErrorKind::BufferFull => write!(f, "buffer full"),
}
}
}
#[non_exhaustive]
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum BadRequestReason {
Unaddressable,
BigPacket,
ZeroSizeBuffer,
}
impl fmt::Display for BadRequestReason {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
BadRequestReason::Unaddressable => write!(f, "packet unaddressable"),
BadRequestReason::BigPacket => {
write!(f, "packet is too large to ever fit in socket buffer")
}
BadRequestReason::ZeroSizeBuffer => {
write!(f, "the size of a user-supplied buffer is zero")
}
}
}
}
impl From<Error> for command::Response {
fn from(value: Error) -> Self {
command::Response::Error(value)
}
}
impl<T> From<flume::SendError<T>> for Error {
fn from(_: flume::SendError<T>) -> Self {
Error::Internal(InternalErrorKind::InternalChannelClosed)
}
}
impl From<flume::RecvError> for Error {
fn from(_: flume::RecvError) -> Self {
Error::Internal(InternalErrorKind::InternalChannelClosed)
}
}
impl From<ListenError> for Error {
fn from(value: ListenError) -> Self {
match value {
ListenError::InvalidState => Error::invalid_socket_state(),
ListenError::Unaddressable => Error::unaddressable(),
}
}
}
impl From<ConnectError> for Error {
fn from(value: ConnectError) -> Self {
match value {
ConnectError::InvalidState => Error::invalid_socket_state(),
ConnectError::Unaddressable => Error::unaddressable(),
}
}
}
impl From<UdpBindError> for Error {
fn from(value: UdpBindError) -> Self {
match value {
UdpBindError::InvalidState => Error::invalid_socket_state(),
UdpBindError::Unaddressable => Error::unaddressable(),
}
}
}
impl From<RawBindError> for Error {
fn from(value: RawBindError) -> Self {
match value {
RawBindError::InvalidState => Error::invalid_socket_state(),
RawBindError::Unaddressable => Error::unaddressable(),
}
}
}
impl From<IcmpBindError> for Error {
fn from(value: IcmpBindError) -> Self {
match value {
IcmpBindError::InvalidState => Error::invalid_socket_state(),
IcmpBindError::Unaddressable => Error::unaddressable(),
}
}
}
impl From<TcpSendError> for Error {
fn from(value: TcpSendError) -> Self {
match value {
TcpSendError::InvalidState => Error::ConnectionReset,
}
}
}
impl From<UdpSendError> for Error {
fn from(value: UdpSendError) -> Self {
match value {
UdpSendError::Unaddressable => Error::unaddressable(),
UdpSendError::BufferFull => Error::buffer_full(),
}
}
}
#[cfg(feature = "std")]
impl From<Error> for std::io::Error {
fn from(value: Error) -> Self {
match value {
Error::BadRequest(_) => std::io::ErrorKind::InvalidInput.into(),
Error::ConnectionReset => std::io::ErrorKind::ConnectionReset.into(),
other => std::io::Error::other(other),
}
}
}