use std::error::Error;
use std::fmt;
use std::io;
#[derive(Debug)]
pub enum CommunicationError {
Io(io::Error),
Protocol(ProtocolError),
Response(ResponseError),
}
impl fmt::Display for CommunicationError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
CommunicationError::Io(e) => write!(f, "I/O error: {}", e),
CommunicationError::Protocol(e) => write!(f, "protocol error: {}", e),
CommunicationError::Response(e) => write!(f, "response error: {}", e),
}
}
}
impl Error for CommunicationError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
CommunicationError::Io(e) => Some(e),
CommunicationError::Protocol(e) => Some(e),
CommunicationError::Response(e) => Some(e),
}
}
}
impl From<io::Error> for CommunicationError {
fn from(e: io::Error) -> Self {
CommunicationError::Io(e)
}
}
impl From<ProtocolError> for CommunicationError {
fn from(e: ProtocolError) -> Self {
CommunicationError::Protocol(e)
}
}
impl From<ResponseError> for CommunicationError {
fn from(e: ResponseError) -> Self {
CommunicationError::Response(e)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ProtocolError {
InvalidPacketHeader,
UnknownCommand(u8),
UnknownServiceType(u8),
PacketTooLarge,
InvalidScanResponse,
InvalidServiceMapResponse,
BufferTooSmall,
InvalidPointFormat,
SequenceMismatch { expected: u16, actual: u16 },
}
impl fmt::Display for ProtocolError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ProtocolError::InvalidPacketHeader => write!(f, "invalid packet header"),
ProtocolError::UnknownCommand(cmd) => write!(f, "unknown command: 0x{:02x}", cmd),
ProtocolError::UnknownServiceType(t) => write!(f, "unknown service type: 0x{:02x}", t),
ProtocolError::PacketTooLarge => write!(f, "packet exceeds maximum UDP payload size"),
ProtocolError::InvalidScanResponse => write!(f, "invalid scan response"),
ProtocolError::InvalidServiceMapResponse => write!(f, "invalid service map response"),
ProtocolError::BufferTooSmall => write!(f, "buffer too small for operation"),
ProtocolError::InvalidPointFormat => write!(f, "invalid point format"),
ProtocolError::SequenceMismatch { expected, actual } => {
write!(
f,
"sequence mismatch: expected {}, got {}",
expected, actual
)
}
}
}
}
impl Error for ProtocolError {}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ResponseError {
Timeout,
UnexpectedResponse,
NotConnected,
Occupied,
Excluded,
InvalidPayload,
GenericError,
UnknownAckCode(i8),
}
impl fmt::Display for ResponseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ResponseError::Timeout => write!(f, "timeout waiting for response"),
ResponseError::UnexpectedResponse => write!(f, "unexpected response"),
ResponseError::NotConnected => write!(f, "not connected to a service"),
ResponseError::Occupied => write!(f, "all sessions are occupied"),
ResponseError::Excluded => write!(f, "client group is excluded from streaming"),
ResponseError::InvalidPayload => write!(f, "invalid payload in request"),
ResponseError::GenericError => write!(f, "generic processing error"),
ResponseError::UnknownAckCode(code) => {
write!(f, "unknown acknowledgment code: {}", code)
}
}
}
}
impl Error for ResponseError {}
impl ResponseError {
pub fn from_ack_code(code: i8) -> Option<Self> {
if code >= 0 {
return None; }
const IDNVAL_RTACK_ERR_NOT_CONNECTED: i8 = -21; const IDNVAL_RTACK_ERR_OCCUPIED: i8 = -20; const IDNVAL_RTACK_ERR_EXCLUDED: i8 = -19; const IDNVAL_RTACK_ERR_PAYLOAD: i8 = -18; const IDNVAL_RTACK_ERR_GENERIC: i8 = -17;
Some(match code {
IDNVAL_RTACK_ERR_NOT_CONNECTED => ResponseError::NotConnected,
IDNVAL_RTACK_ERR_OCCUPIED => ResponseError::Occupied,
IDNVAL_RTACK_ERR_EXCLUDED => ResponseError::Excluded,
IDNVAL_RTACK_ERR_PAYLOAD => ResponseError::InvalidPayload,
IDNVAL_RTACK_ERR_GENERIC => ResponseError::GenericError,
_ => ResponseError::UnknownAckCode(code),
})
}
}
pub type Result<T> = std::result::Result<T, CommunicationError>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_sequence_mismatch_display() {
let err = ProtocolError::SequenceMismatch {
expected: 42,
actual: 99,
};
assert_eq!(err.to_string(), "sequence mismatch: expected 42, got 99");
}
#[test]
fn test_sequence_mismatch_equality() {
let a = ProtocolError::SequenceMismatch {
expected: 1,
actual: 2,
};
let b = ProtocolError::SequenceMismatch {
expected: 1,
actual: 2,
};
let c = ProtocolError::SequenceMismatch {
expected: 1,
actual: 3,
};
assert_eq!(a, b);
assert_ne!(a, c);
}
}