#![macro_use]
use std::fmt;
use serde::de::{Deserialize, Deserializer, SeqAccess, Visitor};
use serde::ser::SerializeTuple;
use serde::ser::{Serialize, Serializer};
#[derive(PartialEq, Debug)]
pub enum HeaderVersion {
PyIPV8Header,
}
#[derive(PartialEq, Debug)]
pub struct Header {
pub size: usize,
pub version: HeaderVersion,
pub mid_hash: Option<Vec<u8>>,
pub message_type: Option<u64>,
}
impl Header {
pub fn py_ipv8_header(mid_hash: [u8; 20], message_type: u8) -> Self {
Header {
size: PY_IPV8_HEADER_SIZE,
version: HeaderVersion::PyIPV8Header,
mid_hash: Some(mid_hash.to_vec()),
message_type: Some(u64::from(message_type)),
}
}
}
#[derive(Debug, PartialEq, serde::Deserialize)]
struct PyIPV8HeaderPattern(
u8,
u8,
u8,
u8,
u8,
u8,
u8,
u8,
u8,
u8,
u8,
u8,
u8,
u8,
u8,
u8,
u8,
u8,
u8,
u8,
u8,
);
const PY_IPV8_HEADER_SIZE: usize = 23;
impl Serialize for Header {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match self.version {
HeaderVersion::PyIPV8Header => {
let mut state = serializer.serialize_tuple(PY_IPV8_HEADER_SIZE)?;
match self.version {
HeaderVersion::PyIPV8Header => state.serialize_element(&(2 as u16))?,
}
let hash = match &self.mid_hash {
Some(m) => m,
None => {
return Err(serde::ser::Error::custom(
"mid_hash was empty and this wasn't expected",
))
}
};
for i in hash {
state.serialize_element(&i)?;
}
let message_type: u8 = self.message_type.ok_or_else(|| {
serde::ser::Error::custom("Message type was empty and this wasn't expected")
})? as u8;
state.serialize_element(&message_type)?;
state.end()
}
}
}
}
impl<'de> Deserialize<'de> for Header {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
#[doc(hidden)]
struct HeaderVisitor;
impl<'de> Visitor<'de> for HeaderVisitor {
type Value = Header;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("Header")
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: SeqAccess<'de>,
{
let version;
let mut version_bytes: Vec<u8> = vec![];
#[allow(clippy::never_loop)]
loop {
version_bytes.push(seq.next_element()?.ok_or_else(|| {
serde::de::Error::custom("No valid header type could be determined")
})?);
version_bytes.push(seq.next_element()?.ok_or_else(|| {
serde::de::Error::custom("No valid header type could be determined")
})?);
if version_bytes.as_slice() == [0, 2] {
version = Some(HeaderVersion::PyIPV8Header);
break;
}
return Err(serde::de::Error::custom(
"No valid header type could be determined",
));
}
match version {
Some(i) => match i {
HeaderVersion::PyIPV8Header => {
let mut mid_hash: [u8; 20] = [0; 20];
for i in mid_hash.iter_mut() {
*i = seq.next_element()?.ok_or_else(|| {
serde::de::Error::custom(
"No valid header type could be determined",
)
})?;
}
let message_type: u8 = seq.next_element()?.ok_or_else(|| {
serde::de::Error::custom("No valid header type could be determined")
})?;
Ok(Header::py_ipv8_header(mid_hash, message_type))
}
},
None => Err(serde::de::Error::custom(
"Somehow the header type was valid but the version None",
)),
}
}
}
Ok(deserializer.deserialize_tuple(std::usize::MAX, HeaderVisitor)?)
}
}
#[cfg(test)]
macro_rules! create_test_header {
() => {
crate::serialization::header::Header {
size: 23,
version: crate::serialization::header::HeaderVersion::PyIPV8Header,
mid_hash: Some(vec![
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
]),
message_type: Some(42),
};
};
}
#[cfg(test)]
mod tests {
use bincode;
use super::*;
#[test]
fn integration_test_creation() {
let h = Header::py_ipv8_header(
[
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
],
42,
);
assert_eq!(
h,
bincode::config()
.big_endian()
.deserialize(&bincode::config().big_endian().serialize(&h).unwrap())
.unwrap()
);
}
}