use super::Error;
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub enum ReturnCode {
Ok,
NotOk,
UnknownService,
UnknownMethod,
NotReady,
NotReachable,
Timeout,
WrongProtocolVersion,
WrongInterfaceVersion,
MalformedMessage,
WrongMessageType,
E2ERepeated,
E2EWrongSequence,
E2E,
E2ENotAvailable,
E2ENoNewData,
GenericError(u8),
InterfaceError(u8),
}
impl ReturnCode {
#[must_use]
pub const fn as_u8(self) -> u8 {
match self {
ReturnCode::Ok => 0x00,
ReturnCode::NotOk => 0x01,
ReturnCode::UnknownService => 0x02,
ReturnCode::UnknownMethod => 0x03,
ReturnCode::NotReady => 0x04,
ReturnCode::NotReachable => 0x05,
ReturnCode::Timeout => 0x06,
ReturnCode::WrongProtocolVersion => 0x07,
ReturnCode::WrongInterfaceVersion => 0x08,
ReturnCode::MalformedMessage => 0x09,
ReturnCode::WrongMessageType => 0x0a,
ReturnCode::E2ERepeated => 0x0b,
ReturnCode::E2EWrongSequence => 0x0c,
ReturnCode::E2E => 0x0d,
ReturnCode::E2ENotAvailable => 0x0e,
ReturnCode::E2ENoNewData => 0x0f,
ReturnCode::GenericError(value) | ReturnCode::InterfaceError(value) => value,
}
}
}
impl TryFrom<u8> for ReturnCode {
type Error = Error;
fn try_from(value: u8) -> Result<Self, Error> {
match value {
0x00 => Ok(ReturnCode::Ok),
0x01 => Ok(ReturnCode::NotOk),
0x02 => Ok(ReturnCode::UnknownService),
0x03 => Ok(ReturnCode::UnknownMethod),
0x04 => Ok(ReturnCode::NotReady),
0x05 => Ok(ReturnCode::NotReachable),
0x06 => Ok(ReturnCode::Timeout),
0x07 => Ok(ReturnCode::WrongProtocolVersion),
0x08 => Ok(ReturnCode::WrongInterfaceVersion),
0x09 => Ok(ReturnCode::MalformedMessage),
0x0a => Ok(ReturnCode::WrongMessageType),
0x0b => Ok(ReturnCode::E2ERepeated),
0x0c => Ok(ReturnCode::E2EWrongSequence),
0x0d => Ok(ReturnCode::E2E),
0x0e => Ok(ReturnCode::E2ENotAvailable),
0x0f => Ok(ReturnCode::E2ENoNewData),
0x10..=0x1f => Ok(ReturnCode::GenericError(value)),
0x20..=0x5e => Ok(ReturnCode::InterfaceError(value)),
_ => Err(Error::InvalidReturnCode(value)),
}
}
}
impl From<ReturnCode> for u8 {
fn from(return_code: ReturnCode) -> u8 {
match return_code {
ReturnCode::Ok => 0x00,
ReturnCode::NotOk => 0x01,
ReturnCode::UnknownService => 0x02,
ReturnCode::UnknownMethod => 0x03,
ReturnCode::NotReady => 0x04,
ReturnCode::NotReachable => 0x05,
ReturnCode::Timeout => 0x06,
ReturnCode::WrongProtocolVersion => 0x07,
ReturnCode::WrongInterfaceVersion => 0x08,
ReturnCode::MalformedMessage => 0x09,
ReturnCode::WrongMessageType => 0x0a,
ReturnCode::E2ERepeated => 0x0b,
ReturnCode::E2EWrongSequence => 0x0c,
ReturnCode::E2E => 0x0d,
ReturnCode::E2ENotAvailable => 0x0e,
ReturnCode::E2ENoNewData => 0x0f,
ReturnCode::GenericError(value) | ReturnCode::InterfaceError(value) => value,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn all_valid_u8_values_round_trip() {
for byte in 0x00u8..=0xffu8 {
match ReturnCode::try_from(byte) {
Ok(code) => {
assert!(
byte < 0x5f,
"0x{byte:02X} should be invalid but decoded successfully"
);
assert_eq!(u8::from(code), byte, "round-trip failed for 0x{byte:02X}");
}
Err(_) => {
assert!(
byte >= 0x5f,
"0x{byte:02X} should be valid but failed to decode"
);
}
}
}
}
#[test]
fn named_variants_decode_correctly() {
let cases = [
(0x00, ReturnCode::Ok),
(0x01, ReturnCode::NotOk),
(0x02, ReturnCode::UnknownService),
(0x03, ReturnCode::UnknownMethod),
(0x04, ReturnCode::NotReady),
(0x05, ReturnCode::NotReachable),
(0x06, ReturnCode::Timeout),
(0x07, ReturnCode::WrongProtocolVersion),
(0x08, ReturnCode::WrongInterfaceVersion),
(0x09, ReturnCode::MalformedMessage),
(0x0a, ReturnCode::WrongMessageType),
(0x0b, ReturnCode::E2ERepeated),
(0x0c, ReturnCode::E2EWrongSequence),
(0x0d, ReturnCode::E2E),
(0x0e, ReturnCode::E2ENotAvailable),
(0x0f, ReturnCode::E2ENoNewData),
];
for (byte, expected) in cases {
assert_eq!(ReturnCode::try_from(byte).unwrap(), expected);
}
}
#[test]
fn generic_error_range_decodes_and_preserves_value() {
for byte in 0x10u8..=0x1f {
assert_eq!(
ReturnCode::try_from(byte).unwrap(),
ReturnCode::GenericError(byte)
);
}
}
#[test]
fn interface_error_range_decodes_and_preserves_value() {
for byte in 0x20u8..=0x5e {
assert_eq!(
ReturnCode::try_from(byte).unwrap(),
ReturnCode::InterfaceError(byte)
);
}
}
#[test]
fn invalid_values_return_error() {
for byte in [0x5f, 0x60, 0xff] {
assert!(
ReturnCode::try_from(byte).is_err(),
"0x{byte:02X} should be invalid"
);
}
}
#[test]
fn generic_error_as_u8_preserves_value() {
for byte in 0x10u8..=0x1f {
assert_eq!(ReturnCode::GenericError(byte).as_u8(), byte);
}
}
#[test]
fn interface_error_as_u8_preserves_value() {
for byte in 0x20u8..=0x5e {
assert_eq!(ReturnCode::InterfaceError(byte).as_u8(), byte);
}
}
#[test]
fn from_u8_for_generic_and_interface_errors() {
assert_eq!(u8::from(ReturnCode::GenericError(0x15)), 0x15);
assert_eq!(u8::from(ReturnCode::InterfaceError(0x30)), 0x30);
}
#[test]
fn as_u8_all_named_variants() {
assert_eq!(ReturnCode::Ok.as_u8(), 0x00);
assert_eq!(ReturnCode::NotOk.as_u8(), 0x01);
assert_eq!(ReturnCode::UnknownService.as_u8(), 0x02);
assert_eq!(ReturnCode::UnknownMethod.as_u8(), 0x03);
assert_eq!(ReturnCode::NotReady.as_u8(), 0x04);
assert_eq!(ReturnCode::NotReachable.as_u8(), 0x05);
assert_eq!(ReturnCode::Timeout.as_u8(), 0x06);
assert_eq!(ReturnCode::WrongProtocolVersion.as_u8(), 0x07);
assert_eq!(ReturnCode::WrongInterfaceVersion.as_u8(), 0x08);
assert_eq!(ReturnCode::MalformedMessage.as_u8(), 0x09);
assert_eq!(ReturnCode::WrongMessageType.as_u8(), 0x0a);
assert_eq!(ReturnCode::E2ERepeated.as_u8(), 0x0b);
assert_eq!(ReturnCode::E2EWrongSequence.as_u8(), 0x0c);
assert_eq!(ReturnCode::E2E.as_u8(), 0x0d);
assert_eq!(ReturnCode::E2ENotAvailable.as_u8(), 0x0e);
assert_eq!(ReturnCode::E2ENoNewData.as_u8(), 0x0f);
}
}