use core::ffi::c_char;
use mbus_core::errors::MbusError;
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MbusStatusCode {
MbusOk = 0,
MbusErrParseError,
MbusErrBasicParseError,
MbusErrTimeout,
MbusErrModbusException,
MbusErrIoError,
MbusErrUnexpected,
MbusErrConnectionLost,
MbusErrUnsupportedFunction,
MbusErrReservedSubFunction,
MbusErrInvalidPduLength,
MbusErrInvalidAduLength,
MbusErrConnectionFailed,
MbusErrConnectionClosed,
MbusErrBufferTooSmall,
MbusErrBufferLenMismatch,
MbusErrSendFailed,
MbusErrInvalidAddress,
MbusErrInvalidOffset,
MbusErrTooManyRequests,
MbusErrInvalidFunctionCode,
MbusErrNoRetriesLeft,
MbusErrTooManyFileReadSubRequests,
MbusErrFileReadPduOverflow,
MbusErrUnexpectedResponse,
MbusErrInvalidTransport,
MbusErrInvalidSlaveAddress,
MbusErrChecksumError,
MbusErrInvalidConfiguration,
MbusErrInvalidNumOfExpectedRsps,
MbusErrInvalidDataLen,
MbusErrInvalidQuantity,
MbusErrInvalidValue,
MbusErrInvalidAndMask,
MbusErrInvalidOrMask,
MbusErrInvalidByteCount,
MbusErrInvalidDeviceIdentification,
MbusErrInvalidDeviceIdCode,
MbusErrInvalidMeiType,
MbusErrInvalidBroadcastAddress,
MbusErrBroadcastNotAllowed,
MbusErrFramingError,
MbusErrNullPointer,
MbusErrInvalidUtf8,
MbusErrInvalidClientId,
MbusErrPoolFull,
MbusErrClientTypeMismatch,
MbusErrBusy,
}
impl From<MbusError> for MbusStatusCode {
fn from(e: MbusError) -> Self {
match e {
MbusError::ParseError => Self::MbusErrParseError,
MbusError::BasicParseError => Self::MbusErrBasicParseError,
MbusError::Timeout => Self::MbusErrTimeout,
MbusError::ModbusException(_) => Self::MbusErrModbusException,
MbusError::IoError => Self::MbusErrIoError,
MbusError::Unexpected => Self::MbusErrUnexpected,
MbusError::ConnectionLost => Self::MbusErrConnectionLost,
MbusError::UnsupportedFunction(_) => Self::MbusErrUnsupportedFunction,
MbusError::ReservedSubFunction(_) => Self::MbusErrReservedSubFunction,
MbusError::InvalidPduLength => Self::MbusErrInvalidPduLength,
MbusError::InvalidAduLength => Self::MbusErrInvalidAduLength,
MbusError::ConnectionFailed => Self::MbusErrConnectionFailed,
MbusError::ConnectionClosed => Self::MbusErrConnectionClosed,
MbusError::BufferTooSmall => Self::MbusErrBufferTooSmall,
MbusError::BufferLenMissmatch => Self::MbusErrBufferLenMismatch,
MbusError::SendFailed => Self::MbusErrSendFailed,
MbusError::InvalidAddress => Self::MbusErrInvalidAddress,
MbusError::InvalidOffset => Self::MbusErrInvalidOffset,
MbusError::TooManyRequests => Self::MbusErrTooManyRequests,
MbusError::InvalidFunctionCode => Self::MbusErrInvalidFunctionCode,
MbusError::NoRetriesLeft => Self::MbusErrNoRetriesLeft,
MbusError::TooManyFileReadSubRequests => Self::MbusErrTooManyFileReadSubRequests,
MbusError::FileReadPduOverflow => Self::MbusErrFileReadPduOverflow,
MbusError::UnexpectedResponse => Self::MbusErrUnexpectedResponse,
MbusError::InvalidTransport => Self::MbusErrInvalidTransport,
MbusError::InvalidSlaveAddress => Self::MbusErrInvalidSlaveAddress,
MbusError::ChecksumError => Self::MbusErrChecksumError,
MbusError::InvalidConfiguration => Self::MbusErrInvalidConfiguration,
MbusError::InvalidNumOfExpectedRsps => Self::MbusErrInvalidNumOfExpectedRsps,
MbusError::InvalidDataLen => Self::MbusErrInvalidDataLen,
MbusError::InvalidQuantity => Self::MbusErrInvalidQuantity,
MbusError::InvalidValue => Self::MbusErrInvalidValue,
MbusError::InvalidAndMask => Self::MbusErrInvalidAndMask,
MbusError::InvalidOrMask => Self::MbusErrInvalidOrMask,
MbusError::InvalidByteCount => Self::MbusErrInvalidByteCount,
MbusError::InvalidDeviceIdentification => Self::MbusErrInvalidDeviceIdentification,
MbusError::InvalidDeviceIdCode => Self::MbusErrInvalidDeviceIdCode,
MbusError::InvalidMeiType => Self::MbusErrInvalidMeiType,
MbusError::InvalidBroadcastAddress => Self::MbusErrInvalidBroadcastAddress,
MbusError::BroadcastNotAllowed => Self::MbusErrBroadcastNotAllowed,
MbusError::FramingError => Self::MbusErrFramingError,
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn mbus_status_str(code: MbusStatusCode) -> *const c_char {
let s: &'static [u8] = match code {
MbusStatusCode::MbusOk => b"OK\0",
MbusStatusCode::MbusErrParseError => b"parse error\0",
MbusStatusCode::MbusErrBasicParseError => b"basic parse error\0",
MbusStatusCode::MbusErrTimeout => b"timeout\0",
MbusStatusCode::MbusErrModbusException => b"modbus exception\0",
MbusStatusCode::MbusErrIoError => b"I/O error\0",
MbusStatusCode::MbusErrUnexpected => b"unexpected error\0",
MbusStatusCode::MbusErrConnectionLost => b"connection lost\0",
MbusStatusCode::MbusErrUnsupportedFunction => b"unsupported function\0",
MbusStatusCode::MbusErrReservedSubFunction => b"reserved sub-function\0",
MbusStatusCode::MbusErrInvalidPduLength => b"invalid PDU length\0",
MbusStatusCode::MbusErrInvalidAduLength => b"invalid ADU length\0",
MbusStatusCode::MbusErrConnectionFailed => b"connection failed\0",
MbusStatusCode::MbusErrConnectionClosed => b"connection closed\0",
MbusStatusCode::MbusErrBufferTooSmall => b"buffer too small\0",
MbusStatusCode::MbusErrBufferLenMismatch => b"buffer length mismatch\0",
MbusStatusCode::MbusErrSendFailed => b"send failed\0",
MbusStatusCode::MbusErrInvalidAddress => b"invalid address\0",
MbusStatusCode::MbusErrInvalidOffset => b"invalid offset\0",
MbusStatusCode::MbusErrTooManyRequests => b"too many in-flight requests\0",
MbusStatusCode::MbusErrInvalidFunctionCode => b"invalid function code\0",
MbusStatusCode::MbusErrNoRetriesLeft => b"no retries left\0",
MbusStatusCode::MbusErrTooManyFileReadSubRequests => b"too many file read sub-requests\0",
MbusStatusCode::MbusErrFileReadPduOverflow => b"file read PDU overflow\0",
MbusStatusCode::MbusErrUnexpectedResponse => b"unexpected response\0",
MbusStatusCode::MbusErrInvalidTransport => b"invalid transport\0",
MbusStatusCode::MbusErrInvalidSlaveAddress => b"invalid slave address\0",
MbusStatusCode::MbusErrChecksumError => b"checksum error\0",
MbusStatusCode::MbusErrInvalidConfiguration => b"invalid configuration\0",
MbusStatusCode::MbusErrInvalidNumOfExpectedRsps => {
b"invalid number of expected responses\0"
}
MbusStatusCode::MbusErrInvalidDataLen => b"invalid data length\0",
MbusStatusCode::MbusErrInvalidQuantity => b"invalid quantity\0",
MbusStatusCode::MbusErrInvalidValue => b"invalid value\0",
MbusStatusCode::MbusErrInvalidAndMask => b"invalid AND mask\0",
MbusStatusCode::MbusErrInvalidOrMask => b"invalid OR mask\0",
MbusStatusCode::MbusErrInvalidByteCount => b"invalid byte count\0",
MbusStatusCode::MbusErrInvalidDeviceIdentification => b"invalid device identification\0",
MbusStatusCode::MbusErrInvalidDeviceIdCode => b"invalid device ID code\0",
MbusStatusCode::MbusErrInvalidMeiType => b"invalid MEI type\0",
MbusStatusCode::MbusErrInvalidBroadcastAddress => b"invalid broadcast address\0",
MbusStatusCode::MbusErrBroadcastNotAllowed => b"broadcast not allowed\0",
MbusStatusCode::MbusErrFramingError => b"framing error\0",
MbusStatusCode::MbusErrNullPointer => b"null pointer\0",
MbusStatusCode::MbusErrInvalidUtf8 => b"invalid UTF-8 in config string\0",
MbusStatusCode::MbusErrInvalidClientId => b"invalid client ID\0",
MbusStatusCode::MbusErrPoolFull => b"client pool full\0",
MbusStatusCode::MbusErrClientTypeMismatch => b"client type mismatch\0",
MbusStatusCode::MbusErrBusy => b"client is busy (re-entrant call)\0",
};
s.as_ptr() as *const c_char
}
#[cfg(test)]
mod tests {
use super::*;
use core::ffi::CStr;
use mbus_core::errors::MbusError;
#[test]
fn from_mbus_error_maps_every_variant() {
macro_rules! check {
($err:expr, $code:ident) => {
assert_eq!(
MbusStatusCode::from($err),
MbusStatusCode::$code,
"wrong mapping for {:?}",
$err
);
};
}
check!(MbusError::ParseError, MbusErrParseError);
check!(MbusError::BasicParseError, MbusErrBasicParseError);
check!(MbusError::Timeout, MbusErrTimeout);
check!(MbusError::ModbusException(1), MbusErrModbusException);
check!(MbusError::IoError, MbusErrIoError);
check!(MbusError::Unexpected, MbusErrUnexpected);
check!(MbusError::ConnectionLost, MbusErrConnectionLost);
check!(
MbusError::UnsupportedFunction(1),
MbusErrUnsupportedFunction
);
check!(
MbusError::ReservedSubFunction(1),
MbusErrReservedSubFunction
);
check!(MbusError::InvalidPduLength, MbusErrInvalidPduLength);
check!(MbusError::InvalidAduLength, MbusErrInvalidAduLength);
check!(MbusError::ConnectionFailed, MbusErrConnectionFailed);
check!(MbusError::ConnectionClosed, MbusErrConnectionClosed);
check!(MbusError::BufferTooSmall, MbusErrBufferTooSmall);
check!(MbusError::BufferLenMissmatch, MbusErrBufferLenMismatch);
check!(MbusError::SendFailed, MbusErrSendFailed);
check!(MbusError::InvalidAddress, MbusErrInvalidAddress);
check!(MbusError::InvalidOffset, MbusErrInvalidOffset);
check!(MbusError::TooManyRequests, MbusErrTooManyRequests);
check!(MbusError::InvalidFunctionCode, MbusErrInvalidFunctionCode);
check!(MbusError::NoRetriesLeft, MbusErrNoRetriesLeft);
check!(
MbusError::TooManyFileReadSubRequests,
MbusErrTooManyFileReadSubRequests
);
check!(MbusError::FileReadPduOverflow, MbusErrFileReadPduOverflow);
check!(MbusError::UnexpectedResponse, MbusErrUnexpectedResponse);
check!(MbusError::InvalidTransport, MbusErrInvalidTransport);
check!(MbusError::InvalidSlaveAddress, MbusErrInvalidSlaveAddress);
check!(MbusError::ChecksumError, MbusErrChecksumError);
check!(MbusError::InvalidConfiguration, MbusErrInvalidConfiguration);
check!(
MbusError::InvalidNumOfExpectedRsps,
MbusErrInvalidNumOfExpectedRsps
);
check!(MbusError::InvalidDataLen, MbusErrInvalidDataLen);
check!(MbusError::InvalidQuantity, MbusErrInvalidQuantity);
check!(MbusError::InvalidValue, MbusErrInvalidValue);
check!(MbusError::InvalidAndMask, MbusErrInvalidAndMask);
check!(MbusError::InvalidOrMask, MbusErrInvalidOrMask);
check!(MbusError::InvalidByteCount, MbusErrInvalidByteCount);
check!(
MbusError::InvalidDeviceIdentification,
MbusErrInvalidDeviceIdentification
);
check!(MbusError::InvalidDeviceIdCode, MbusErrInvalidDeviceIdCode);
check!(MbusError::InvalidMeiType, MbusErrInvalidMeiType);
check!(
MbusError::InvalidBroadcastAddress,
MbusErrInvalidBroadcastAddress
);
check!(MbusError::BroadcastNotAllowed, MbusErrBroadcastNotAllowed);
check!(MbusError::FramingError, MbusErrFramingError);
}
#[test]
fn ok_maps_to_zero_discriminant() {
assert_eq!(MbusStatusCode::MbusOk as u32, 0);
}
#[test]
fn status_str_is_nonnull_nonempty_and_nul_terminated_for_every_variant() {
let all: &[MbusStatusCode] = &[
MbusStatusCode::MbusOk,
MbusStatusCode::MbusErrParseError,
MbusStatusCode::MbusErrBasicParseError,
MbusStatusCode::MbusErrTimeout,
MbusStatusCode::MbusErrModbusException,
MbusStatusCode::MbusErrIoError,
MbusStatusCode::MbusErrUnexpected,
MbusStatusCode::MbusErrConnectionLost,
MbusStatusCode::MbusErrUnsupportedFunction,
MbusStatusCode::MbusErrReservedSubFunction,
MbusStatusCode::MbusErrInvalidPduLength,
MbusStatusCode::MbusErrInvalidAduLength,
MbusStatusCode::MbusErrConnectionFailed,
MbusStatusCode::MbusErrConnectionClosed,
MbusStatusCode::MbusErrBufferTooSmall,
MbusStatusCode::MbusErrBufferLenMismatch,
MbusStatusCode::MbusErrSendFailed,
MbusStatusCode::MbusErrInvalidAddress,
MbusStatusCode::MbusErrInvalidOffset,
MbusStatusCode::MbusErrTooManyRequests,
MbusStatusCode::MbusErrInvalidFunctionCode,
MbusStatusCode::MbusErrNoRetriesLeft,
MbusStatusCode::MbusErrTooManyFileReadSubRequests,
MbusStatusCode::MbusErrFileReadPduOverflow,
MbusStatusCode::MbusErrUnexpectedResponse,
MbusStatusCode::MbusErrInvalidTransport,
MbusStatusCode::MbusErrInvalidSlaveAddress,
MbusStatusCode::MbusErrChecksumError,
MbusStatusCode::MbusErrInvalidConfiguration,
MbusStatusCode::MbusErrInvalidNumOfExpectedRsps,
MbusStatusCode::MbusErrInvalidDataLen,
MbusStatusCode::MbusErrInvalidQuantity,
MbusStatusCode::MbusErrInvalidValue,
MbusStatusCode::MbusErrInvalidAndMask,
MbusStatusCode::MbusErrInvalidOrMask,
MbusStatusCode::MbusErrInvalidByteCount,
MbusStatusCode::MbusErrInvalidDeviceIdentification,
MbusStatusCode::MbusErrInvalidDeviceIdCode,
MbusStatusCode::MbusErrInvalidMeiType,
MbusStatusCode::MbusErrInvalidBroadcastAddress,
MbusStatusCode::MbusErrBroadcastNotAllowed,
MbusStatusCode::MbusErrFramingError,
MbusStatusCode::MbusErrNullPointer,
MbusStatusCode::MbusErrInvalidUtf8,
MbusStatusCode::MbusErrInvalidClientId,
MbusStatusCode::MbusErrPoolFull,
MbusStatusCode::MbusErrClientTypeMismatch,
MbusStatusCode::MbusErrBusy,
];
for &code in all {
let ptr = mbus_status_str(code);
assert!(
!ptr.is_null(),
"mbus_status_str returned null for {:?}",
code
);
let s = unsafe { CStr::from_ptr(ptr) }
.to_str()
.expect("status string is not valid UTF-8");
assert!(
!s.is_empty(),
"mbus_status_str returned empty string for {:?}",
code
);
}
}
#[test]
fn status_str_ok_is_literally_ok() {
let ptr = mbus_status_str(MbusStatusCode::MbusOk);
let s = unsafe { CStr::from_ptr(ptr) }.to_str().unwrap();
assert_eq!(s, "OK");
}
#[test]
fn status_str_returns_same_pointer_on_repeated_calls() {
let p1 = mbus_status_str(MbusStatusCode::MbusErrTimeout);
let p2 = mbus_status_str(MbusStatusCode::MbusErrTimeout);
assert_eq!(p1, p2);
}
}