use crate::{
auxiliary::{crc16, utils},
codec::{BasicCodec, Codec},
error::{ErpcResult, TransportError},
transport::Transport,
};
use async_trait::async_trait;
use std::time::Duration;
const HEADER_LEN: usize = 6;
#[async_trait]
pub trait FramedTransport: Send + Sync {
async fn base_send(&mut self, data: &[u8]) -> ErpcResult<()>;
async fn base_receive(&mut self, length: usize) -> ErpcResult<Vec<u8>>;
fn is_connected(&self) -> bool;
async fn close(&mut self) -> ErpcResult<()>;
fn set_timeout(&mut self, timeout: Duration);
}
#[async_trait]
impl<T: FramedTransport + 'static> Transport for T {
async fn send(&mut self, data: &[u8]) -> ErpcResult<()> {
let mut codec = BasicCodec::new();
let message_length = data.len() as u16;
let crc_body = crc16::calculate(data);
let length_bytes = utils::uint16_to_bytes(message_length);
let crc_body_bytes = utils::uint16_to_bytes(crc_body);
let crc_length = crc16::calculate(&length_bytes);
let crc_body_crc = crc16::calculate(&crc_body_bytes);
let crc_header = (crc_length.wrapping_add(crc_body_crc));
codec.write_uint16(crc_header as u16)?;
codec.write_uint16(message_length)?;
codec.write_uint16(crc_body)?;
let header = codec.as_bytes();
self.base_send(header).await?;
self.base_send(data).await?;
Ok(())
}
async fn receive(&mut self) -> ErpcResult<Vec<u8>> {
let header_data = self.base_receive(HEADER_LEN).await?;
let mut codec = BasicCodec::from_data(header_data);
let crc_header = codec.read_uint16()?;
let message_length = codec.read_uint16()?;
let crc_body = codec.read_uint16()?;
let length_bytes = utils::uint16_to_bytes(message_length);
let crc_body_bytes = utils::uint16_to_bytes(crc_body);
let computed_crc_length = crc16::calculate(&length_bytes);
let computed_crc_body_crc = crc16::calculate(&crc_body_bytes);
let computed_crc_header = (computed_crc_length.wrapping_add(computed_crc_body_crc));
if computed_crc_header != crc_header as u16 {
return Err(
TransportError::ReceiveFailed("Invalid message (header) CRC".to_string()).into(),
);
}
let data = self.base_receive(message_length as usize).await?;
let computed_body_crc = crc16::calculate(&data);
if computed_body_crc != crc_body {
return Err(
TransportError::ReceiveFailed("Invalid message (body) CRC".to_string()).into(),
);
}
Ok(data)
}
fn is_connected(&self) -> bool {
FramedTransport::is_connected(self)
}
async fn close(&mut self) -> ErpcResult<()> {
FramedTransport::close(self).await
}
fn set_timeout(&mut self, timeout: Duration) {
FramedTransport::set_timeout(self, timeout)
}
}