use super::RESPONSE_HDR_SIZE;
use crate::requests::{BodyType, Opcode, ProviderID, ResponseStatus, Result, MAGIC_NUMBER};
use num::FromPrimitive;
use serde::{Deserialize, Serialize};
use std::convert::TryFrom;
use std::io::{Read, Write};
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
pub struct RawResponseHeader {
pub version_maj: u8,
pub version_min: u8,
pub provider: u8,
pub session: u64,
pub content_type: u8,
pub body_len: u32,
pub opcode: u16,
pub status: u16,
}
impl RawResponseHeader {
#[cfg(feature = "testing")]
#[allow(clippy::new_without_default)]
pub fn new() -> RawResponseHeader {
RawResponseHeader {
version_maj: 0,
version_min: 0,
provider: 0,
session: 0,
content_type: 0,
body_len: 0,
opcode: 0,
status: 0,
}
}
pub fn write_to_stream(&self, stream: &mut impl Write) -> Result<()> {
stream.write_all(&bincode::serialize(&MAGIC_NUMBER)?)?;
stream.write_all(&bincode::serialize(&RESPONSE_HDR_SIZE)?)?;
stream.write_all(&bincode::serialize(&self)?)?;
Ok(())
}
pub fn read_from_stream(mut stream: &mut impl Read) -> Result<RawResponseHeader> {
let magic_number = get_from_stream!(stream, u32);
let hdr_size = get_from_stream!(stream, u16);
if magic_number != MAGIC_NUMBER || hdr_size != RESPONSE_HDR_SIZE {
return Err(ResponseStatus::InvalidHeader);
}
let mut bytes = vec![0_u8; usize::try_from(hdr_size)?];
stream.read_exact(&mut bytes)?;
Ok(bincode::deserialize(&bytes)?)
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct ResponseHeader {
pub version_maj: u8,
pub version_min: u8,
pub provider: ProviderID,
pub session: u64,
pub content_type: BodyType,
pub opcode: Opcode,
pub status: ResponseStatus,
}
impl ResponseHeader {
pub(crate) fn new() -> ResponseHeader {
ResponseHeader {
version_maj: 0,
version_min: 0,
provider: ProviderID::CoreProvider,
session: 0,
content_type: BodyType::Protobuf,
opcode: Opcode::Ping,
status: ResponseStatus::Success,
}
}
}
impl TryFrom<RawResponseHeader> for ResponseHeader {
type Error = ResponseStatus;
fn try_from(header: RawResponseHeader) -> Result<ResponseHeader> {
let provider: ProviderID = match FromPrimitive::from_u8(header.provider) {
Some(provider_id) => provider_id,
None => return Err(ResponseStatus::ProviderDoesNotExist),
};
let content_type: BodyType = match FromPrimitive::from_u8(header.content_type) {
Some(content_type) => content_type,
None => return Err(ResponseStatus::ContentTypeNotSupported),
};
let opcode: Opcode = match FromPrimitive::from_u16(header.opcode) {
Some(opcode) => opcode,
None => return Err(ResponseStatus::OpcodeDoesNotExist),
};
let status: ResponseStatus = match FromPrimitive::from_u16(header.status) {
Some(status) => status,
None => return Err(ResponseStatus::InvalidEncoding),
};
Ok(ResponseHeader {
version_maj: header.version_maj,
version_min: header.version_min,
provider,
session: header.session,
content_type,
opcode,
status,
})
}
}
impl From<ResponseHeader> for RawResponseHeader {
fn from(header: ResponseHeader) -> Self {
RawResponseHeader {
version_maj: header.version_maj,
version_min: header.version_min,
provider: header.provider as u8,
session: header.session,
content_type: header.content_type as u8,
body_len: 0,
opcode: header.opcode as u16,
status: header.status as u16,
}
}
}