use std::borrow::Cow;
use bytes::{Buf, BufMut};
use crate::protocol::{BinaryMessage, BinaryPayload, ParseError};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ServiceCallResponse<'a> {
pub service_id: u32,
pub call_id: u32,
pub encoding: Cow<'a, str>,
pub payload: Cow<'a, [u8]>,
}
impl ServiceCallResponse<'_> {
pub fn into_owned(self) -> ServiceCallResponse<'static> {
ServiceCallResponse {
service_id: self.service_id,
call_id: self.call_id,
encoding: Cow::Owned(self.encoding.into_owned()),
payload: Cow::Owned(self.payload.into_owned()),
}
}
}
impl<'a> BinaryPayload<'a> for ServiceCallResponse<'a> {
fn parse_payload(mut data: &'a [u8]) -> Result<Self, ParseError> {
if data.len() < 4 + 4 + 4 {
return Err(ParseError::BufferTooShort);
}
let service_id = data.get_u32_le();
let call_id = data.get_u32_le();
let encoding_len = data.get_u32_le() as usize;
if data.len() < encoding_len {
return Err(ParseError::BufferTooShort);
}
let encoding = Cow::Borrowed(std::str::from_utf8(&data[..encoding_len])?);
data.advance(encoding_len);
Ok(Self {
service_id,
call_id,
encoding,
payload: Cow::Borrowed(data),
})
}
fn payload_size(&self) -> usize {
4 + 4 + 4 + self.encoding.len() + self.payload.len()
}
fn write_payload(&self, buf: &mut impl BufMut) {
buf.put_u32_le(self.service_id);
buf.put_u32_le(self.call_id);
buf.put_u32_le(self.encoding.len() as u32);
buf.put_slice(self.encoding.as_bytes());
buf.put_slice(&self.payload);
}
}
impl<'a> BinaryMessage<'a> for ServiceCallResponse<'a> {
const OPCODE: u8 = 3;
}
#[cfg(test)]
mod tests {
use assert_matches::assert_matches;
use super::*;
fn message() -> ServiceCallResponse<'static> {
ServiceCallResponse {
service_id: 10,
call_id: 12,
encoding: "json".into(),
payload: br#"{"key": "value"}"#.into(),
}
}
#[test]
fn test_parse() {
assert_matches!(
ServiceCallResponse::parse_payload(b""),
Err(ParseError::BufferTooShort)
);
assert_matches!(
ServiceCallResponse::parse_payload(&[0; 11]),
Err(ParseError::BufferTooShort)
);
let mut buf = Vec::new();
buf.put_u32_le(10);
buf.put_u32_le(12);
buf.put_u32_le(1);
assert_matches!(
ServiceCallResponse::parse_payload(&buf),
Err(ParseError::BufferTooShort)
);
}
#[test]
fn test_roundtrip() {
let orig = message();
let mut buf = Vec::new();
BinaryPayload::write_payload(&orig, &mut buf);
let parsed = ServiceCallResponse::parse_payload(&buf).unwrap();
assert_eq!(parsed, orig);
}
}