use super::MessageError;
use crate::conn::{frame::Frame, MessageKind};
pub(super) type ExtendedMessageSubtype = u16;
const MSG_SUBTYPE_SIZE: usize = std::mem::size_of::<ExtendedMessageSubtype>();
macro_rules! extended_message {
( $subtype:expr,
$req_vis:vis struct $req_name:ident { $($req_fields:tt)* }
$resp_vis:vis struct $resp_name:ident { $($resp_fields:tt)* }
{ $($req_impl:tt)* }
{ $($resp_impl:tt)* }) => {
use crate::conn::frame::Frame;
use super::MessageError;
#[derive(Clone, Debug, PartialEq)]
$req_vis struct $req_name {
message_id: u8,
$($req_fields)*
}
#[derive(Clone, Debug, PartialEq)]
$resp_vis struct $resp_name {
message_id: u8,
$($resp_fields)*
}
impl ExtendedMessage for $req_name {
const MSG_SUBTYPE: u16 = ($subtype);
fn message_id(&self) -> u8 { self.message_id }
$($req_impl)*
}
impl ExtendedMessage for $resp_name {
const MSG_SUBTYPE: u16 = ($subtype);
fn message_id(&self) -> u8 { self.message_id }
$($resp_impl)*
}
impl TryFrom<Frame> for $req_name {
type Error = MessageError;
fn try_from(value: Frame) -> Result<Self, Self::Error> { Self::from_frame(value) }
}
impl TryFrom<Frame> for $resp_name {
type Error = MessageError;
fn try_from(value: Frame) -> Result<Self, Self::Error> { Self::from_frame(value) }
}
impl TryFrom<$req_name> for Frame {
type Error = MessageError;
fn try_from(value: $req_name) -> Result<Self, Self::Error> { value.into_request_frame() }
}
impl TryFrom<$resp_name> for Frame {
type Error = MessageError;
fn try_from(value: $resp_name) -> Result<Self, Self::Error> { value.into_response_frame() }
}
};
}
pub(super) use extended_message;
const MSG_TYPE_EXTENDED: u8 = 0x1f;
pub(super) trait ExtendedMessage: Sized {
const MSG_SUBTYPE: ExtendedMessageSubtype;
fn impl_frame_data_len(&self) -> usize;
fn impl_frame_data<W: std::io::Write>(&self, dst: &mut W) -> Result<(), MessageError>;
fn from_frame_data(message_id: u8, data: Vec<u8>) -> Result<Self, MessageError>;
fn message_id(&self) -> u8;
fn from_frame(mut frame: Frame) -> Result<Self, MessageError> {
if peek_subtype(&frame)? != Self::MSG_SUBTYPE {
return Err(MessageError::IncorrectSubtype(super::Frame {
inner: frame,
}));
}
Self::from_frame_data(frame.msg_id, frame.data.drain(MSG_SUBTYPE_SIZE..).collect())
}
fn into_frame(self, kind: MessageKind) -> Result<Frame, MessageError> {
use std::io::Write;
let (msg_type, address) = kind.into();
let mut data = std::io::Cursor::new(Vec::with_capacity(
MSG_SUBTYPE_SIZE + self.impl_frame_data_len(),
));
data.write_all(&Self::MSG_SUBTYPE.to_be_bytes())?;
self.impl_frame_data(&mut data)?;
Ok(Frame {
address,
msg_id: self.message_id(),
msg_type,
kind,
data: data.into_inner(),
})
}
fn into_request_frame(self) -> Result<Frame, MessageError> {
self.into_frame(MessageKind::ExtendedRequest)
}
fn into_response_frame(self) -> Result<Frame, MessageError> {
self.into_frame(MessageKind::ExtendedResponse)
}
}
pub(super) fn peek_subtype(frame: &Frame) -> Result<ExtendedMessageSubtype, MessageError> {
if frame.msg_type != MSG_TYPE_EXTENDED || frame.data.len() < MSG_SUBTYPE_SIZE {
return Err(MessageError::InvalidData);
}
Ok(ExtendedMessageSubtype::from_be_bytes(
frame.data[..MSG_SUBTYPE_SIZE].try_into().unwrap(),
))
}