From a5eb819dd30e21e43c46f0af8c64fbbab2a2e619 Mon Sep 17 00:00:00 2001 From: CosminPerRam Date: Wed, 11 Sep 2024 20:53:47 +0300 Subject: [PATCH] feat: Add support for custom exception codes (#279) --- CHANGELOG.md | 8 ++++++ src/codec/mod.rs | 25 +----------------- src/frame/mod.rs | 67 ++++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 65 insertions(+), 35 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d8f2b80..9aeb7bf3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,14 @@ # Changelog +## v0.15.0 (Unreleased) + +- Added `Exception::Custom` and `Exception::new`. + +### Breaking Changes + +- Removed `TryFrom` and `#[repr(u8)]` for Exception. + ## v0.14.1 (2024-09-10) - Implement `Report Server ID` (function code 17). diff --git a/src/codec/mod.rs b/src/codec/mod.rs index 6e641287..5965297a 100644 --- a/src/codec/mod.rs +++ b/src/codec/mod.rs @@ -385,7 +385,7 @@ impl TryFrom for ExceptionResponse { )); } let function = fn_err_code - 0x80; - let exception = Exception::try_from(rdr.read_u8()?)?; + let exception = Exception::new(rdr.read_u8()?); Ok(ExceptionResponse { function: FunctionCode::new(function), exception, @@ -393,29 +393,6 @@ impl TryFrom for ExceptionResponse { } } -impl TryFrom for Exception { - type Error = Error; - - fn try_from(code: u8) -> Result { - use crate::frame::Exception::*; - let ex = match code { - 0x01 => IllegalFunction, - 0x02 => IllegalDataAddress, - 0x03 => IllegalDataValue, - 0x04 => ServerDeviceFailure, - 0x05 => Acknowledge, - 0x06 => ServerDeviceBusy, - 0x08 => MemoryParityError, - 0x0A => GatewayPathUnavailable, - 0x0B => GatewayTargetDevice, - _ => { - return Err(Error::new(ErrorKind::InvalidData, "Invalid exception code")); - } - }; - Ok(ex) - } -} - impl TryFrom for ResponsePdu { type Error = Error; diff --git a/src/frame/mod.rs b/src/frame/mod.rs index 69db93ce..a221bf3e 100644 --- a/src/frame/mod.rs +++ b/src/frame/mod.rs @@ -387,26 +387,70 @@ impl Response { /// A server (slave) exception. #[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[repr(u8)] pub enum Exception { - IllegalFunction = 0x01, - IllegalDataAddress = 0x02, - IllegalDataValue = 0x03, - ServerDeviceFailure = 0x04, - Acknowledge = 0x05, - ServerDeviceBusy = 0x06, - MemoryParityError = 0x08, - GatewayPathUnavailable = 0x0A, - GatewayTargetDevice = 0x0B, + /// 0x01 + IllegalFunction, + /// 0x02 + IllegalDataAddress, + /// 0x03 + IllegalDataValue, + /// 0x04 + ServerDeviceFailure, + /// 0x05 + Acknowledge, + /// 0x06 + ServerDeviceBusy, + /// 0x08 + MemoryParityError, + /// 0x0A + GatewayPathUnavailable, + /// 0x0B + GatewayTargetDevice, + /// None of the above. + /// + /// Although encoding one of the predefined values as this is possible, it is not recommended. + /// Instead, prefer to use [`Self::new()`] to prevent such ambiguities. + Custom(u8), } impl From for u8 { fn from(from: Exception) -> Self { - from as u8 + use crate::frame::Exception::*; + match from { + IllegalFunction => 0x01, + IllegalDataAddress => 0x02, + IllegalDataValue => 0x03, + ServerDeviceFailure => 0x04, + Acknowledge => 0x05, + ServerDeviceBusy => 0x06, + MemoryParityError => 0x08, + GatewayPathUnavailable => 0x0A, + GatewayTargetDevice => 0x0B, + Custom(code) => code, + } } } impl Exception { + /// Create a new [`Exception`] with `value`. + #[must_use] + pub const fn new(value: u8) -> Self { + use crate::frame::Exception::*; + + match value { + 0x01 => IllegalFunction, + 0x02 => IllegalDataAddress, + 0x03 => IllegalDataValue, + 0x04 => ServerDeviceFailure, + 0x05 => Acknowledge, + 0x06 => ServerDeviceBusy, + 0x08 => MemoryParityError, + 0x0A => GatewayPathUnavailable, + 0x0B => GatewayTargetDevice, + other => Custom(other), + } + } + pub(crate) fn description(&self) -> &str { use crate::frame::Exception::*; @@ -420,6 +464,7 @@ impl Exception { MemoryParityError => "Memory parity error", GatewayPathUnavailable => "Gateway path unavailable", GatewayTargetDevice => "Gateway target device failed to respond", + Custom(_) => "Custom", } } }