use binrw::{BinWrite, binread};
use crate::protocol::{Error, Exception, Function, r#struct::Writable};
#[derive(Copy, Clone, BinWrite)]
#[bw(big)]
pub struct Request<T: Writable> {
pub function_code: u8,
pub args: T,
}
impl<T: Writable> Request<T> {
pub const fn from_args<F: Function<Args = T>>(args: T) -> Self {
Self { function_code: F::CODE, args }
}
}
#[binread]
#[br(big)]
#[derive(Copy, Clone)]
pub enum Response<F: Function> {
Ok {
#[br(temp, assert(function_code == F::CODE))]
function_code: u8,
output: F::Output,
},
Exception {
#[br(temp, assert(function_code == F::CODE | 0x80))]
function_code: u8,
exception: Exception,
},
}
impl<F: Function> Response<F> {
pub fn into_result(self) -> Result<F::Output, Error> {
match self {
Self::Ok { output, .. } => Ok(output),
Self::Exception { exception, .. } => Err(Error::Exception(exception)),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::protocol::{ServerError, function::ReadHoldingRegisters, r#struct::Readable};
#[test]
fn parse_exception_ok() {
const RESPONSE: &[u8] = &[
0x83, 0x04, ];
let response = Response::<ReadHoldingRegisters>::from_bytes(RESPONSE).unwrap();
assert!(matches!(
response,
Response::Exception { exception: Exception::Server(ServerError::ServerDeviceFailure) }
));
}
#[test]
fn unknown_error_code_ok() {
const RESPONSE: &[u8] = &[
0x83, 0xFF, ];
let response = Response::<ReadHoldingRegisters>::from_bytes(RESPONSE).unwrap();
assert!(matches!(response, Response::Exception { exception: Exception::Unknown(0xFF) }));
}
}