pub mod address;
pub mod codec;
pub mod function;
use bytes::{Buf, BufMut};
use crate::{
Error,
protocol::{
codec::{Decode, Encode},
function::IntoValue,
},
};
#[derive(Copy, Clone)]
pub struct Request<A> {
pub function_code: u8,
pub args: A,
}
impl<A> Request<A> {
pub const fn wrap<F: Function<Args = A>>(args: A) -> Self {
Self { function_code: F::CODE, args }
}
}
impl<A: Encode> Encode for Request<A> {
fn encode(&self, to: &mut impl BufMut) {
to.put_u8(self.function_code);
self.args.encode(to);
}
}
#[derive(Copy, Clone)]
pub enum Response<F: Function> {
Ok(F::Output),
Exception(Exception),
}
impl<F: Function> Decode for Response<F> {
fn decode(from: &mut impl Buf) -> Result<Self, Error> {
match from.try_get_u8()? {
function_code if function_code == F::CODE => Ok(Self::Ok(F::Output::decode(from)?)),
function_code if function_code == (F::CODE | 0x80) => {
Ok(Self::Exception(Exception::decode(from)?))
}
function_code => Err(Error::UnexpectedFunctionCode(function_code)),
}
}
}
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)),
}
}
}
#[must_use]
#[derive(Copy, Clone, Debug, thiserror::Error)]
pub enum Exception {
#[error("illegal function")]
IllegalFunction,
#[error("illegal data address")]
IllegalDataAddress,
#[error("illegal data value")]
IllegalDataValue,
#[error("server device failure")]
ServerDeviceFailure,
#[error("acknowledge")]
Acknowledge,
#[error("server device busy")]
ServerDeviceBusy,
#[error("memory parity error")]
MemoryParityError,
#[error("gateway path unavailable")]
GatewayPathUnavailable,
#[error("gateway target device failed to respond")]
GatewayTargetDeviceFailedToRespond,
#[error("custom error ({0})")]
Custom(u8),
}
impl Decode for Exception {
fn decode(from: &mut impl Buf) -> Result<Self, Error> {
match from.try_get_u8()? {
0x01 => Ok(Self::IllegalFunction),
0x02 => Ok(Self::IllegalDataAddress),
0x03 => Ok(Self::IllegalDataValue),
0x04 => Ok(Self::ServerDeviceFailure),
0x05 => Ok(Self::Acknowledge),
0x06 => Ok(Self::ServerDeviceBusy),
0x08 => Ok(Self::MemoryParityError),
0x0A => Ok(Self::GatewayPathUnavailable),
0x0B => Ok(Self::GatewayTargetDeviceFailedToRespond),
exception_code => Ok(Self::Custom(exception_code)),
}
}
}
pub trait Function: function::Code {
type Args: Encode;
type Output: Decode + IntoValue;
}
pub trait Address: Encode {}
impl Address for u16 {}