use crate::protocol::dds::api::dds_type::DdsType;
use crate::protocol::dds::api::error::DdsApiError;
use crate::protocol::dds::byte_cursor::{ByteCursor, ByteWriter, Endianness};
use super::sample_identity::SampleIdentity;
pub trait ServiceField: Sized {
fn serialize_inner(&self, w: &mut ByteWriter<'_>) -> Result<(), DdsApiError>;
fn deserialize_inner(r: &mut ByteCursor<'_>) -> Result<Self, DdsApiError>;
}
pub trait Service: Sized {
type Request: ServiceField + Clone;
type Response: ServiceField;
const REQUEST_TYPE_NAME: &'static str;
const RESPONSE_TYPE_NAME: &'static str;
}
pub struct RequestWrapper<S: Service> {
pub header: SampleIdentity,
pub body: S::Request,
}
impl<S: Service> DdsType for RequestWrapper<S> {
const TYPE_NAME: &'static str = S::REQUEST_TYPE_NAME;
fn serialize(&self, buf: &mut [u8]) -> Result<usize, DdsApiError> {
let mut w = make_writer(buf)?;
self.header.serialize_inner(&mut w)?;
self.body.serialize_inner(&mut w)?;
Ok(4 + w.position())
}
fn deserialize(payload: &[u8]) -> Result<Self, DdsApiError> {
let mut r = make_cursor(payload)?;
let header = SampleIdentity::deserialize_inner(&mut r)?;
let body = S::Request::deserialize_inner(&mut r)?;
Ok(Self { header, body })
}
}
pub struct ReplyWrapper<S: Service> {
pub header: SampleIdentity,
pub body: S::Response,
}
impl<S: Service> DdsType for ReplyWrapper<S> {
const TYPE_NAME: &'static str = S::RESPONSE_TYPE_NAME;
fn serialize(&self, buf: &mut [u8]) -> Result<usize, DdsApiError> {
let mut w = make_writer(buf)?;
self.header.serialize_inner(&mut w)?;
self.body.serialize_inner(&mut w)?;
Ok(4 + w.position())
}
fn deserialize(payload: &[u8]) -> Result<Self, DdsApiError> {
let mut r = make_cursor(payload)?;
let header = SampleIdentity::deserialize_inner(&mut r)?;
let body = S::Response::deserialize_inner(&mut r)?;
Ok(Self { header, body })
}
}
const CDR_LE_HEADER: [u8; 4] = [0x00, 0x01, 0x00, 0x00];
fn make_writer(buf: &mut [u8]) -> Result<ByteWriter<'_>, DdsApiError> {
if buf.len() < 4 {
return Err(DdsApiError::PayloadBufferTooSmall);
}
buf[..4].copy_from_slice(&CDR_LE_HEADER);
Ok(ByteWriter::new(&mut buf[4..], Endianness::Little))
}
fn make_cursor(payload: &[u8]) -> Result<ByteCursor<'_>, DdsApiError> {
if payload.len() < 4 {
return Err(DdsApiError::Serialization(
"payload shorter than CDR header",
));
}
let endianness = if payload[1] & 0x01 != 0 {
Endianness::Little
} else {
Endianness::Big
};
Ok(ByteCursor::new(&payload[4..], endianness))
}
#[cfg(test)]
mod tests {
use super::*;
struct TestService;
#[derive(Debug, Clone, PartialEq)]
struct TestRequest {
value: i32,
}
#[derive(Debug, PartialEq)]
struct TestResponse {
doubled: i32,
}
impl ServiceField for TestRequest {
fn serialize_inner(&self, w: &mut ByteWriter<'_>) -> Result<(), DdsApiError> {
w.write_i32(self.value)?;
Ok(())
}
fn deserialize_inner(r: &mut ByteCursor<'_>) -> Result<Self, DdsApiError> {
Ok(Self {
value: r.read_i32()?,
})
}
}
impl ServiceField for TestResponse {
fn serialize_inner(&self, w: &mut ByteWriter<'_>) -> Result<(), DdsApiError> {
w.write_i32(self.doubled)?;
Ok(())
}
fn deserialize_inner(r: &mut ByteCursor<'_>) -> Result<Self, DdsApiError> {
Ok(Self {
doubled: r.read_i32()?,
})
}
}
impl Service for TestService {
type Request = TestRequest;
type Response = TestResponse;
const REQUEST_TYPE_NAME: &'static str = "test::srv::dds_::TestService_Request_";
const RESPONSE_TYPE_NAME: &'static str = "test::srv::dds_::TestService_Response_";
}
#[test]
fn request_wrapper_round_trip() {
let orig = RequestWrapper::<TestService> {
header: SampleIdentity::new([0xAA; 16], 7),
body: TestRequest { value: 42 },
};
let mut buf = [0u8; 64];
let len = orig.serialize(&mut buf).unwrap();
let decoded = RequestWrapper::<TestService>::deserialize(&buf[..len]).unwrap();
assert_eq!(decoded.header, orig.header);
assert_eq!(decoded.body, orig.body);
}
#[test]
fn reply_wrapper_round_trip() {
let orig = ReplyWrapper::<TestService> {
header: SampleIdentity::new([0xBB; 16], 3),
body: TestResponse { doubled: 84 },
};
let mut buf = [0u8; 64];
let len = orig.serialize(&mut buf).unwrap();
let decoded = ReplyWrapper::<TestService>::deserialize(&buf[..len]).unwrap();
assert_eq!(decoded.header, orig.header);
assert_eq!(decoded.body, orig.body);
}
#[test]
fn request_wrapper_type_name() {
assert_eq!(
RequestWrapper::<TestService>::TYPE_NAME,
"test::srv::dds_::TestService_Request_"
);
}
#[test]
fn reply_filter_rejects_foreign_guid() {
let my_guid = [0xAA; 16];
let foreign_guid = [0xBB; 16];
let my_reply_header = SampleIdentity::new(my_guid, 1);
let foreign_reply_header = SampleIdentity::new(foreign_guid, 1);
assert_eq!(my_reply_header.writer_guid, my_guid);
assert_ne!(foreign_reply_header.writer_guid, my_guid);
}
}