use core::fmt;
use serde::de::{SeqAccess, Visitor};
use serde::ser::{SerializeSeq, Serializer};
use serde::{de, Deserialize, Deserializer, Serialize};
use std::{fmt::Debug, time::Duration};
use crate::ipnd::services::ServiceBlock;
use bp7::{bundle::Block, ByteBuffer, EndpointID};
pub const IPND_VERSION: u8 = 0x07;
pub type BeaconFlags = u8;
pub const SOURCE_EID_PRESENT: BeaconFlags = 0b0000_0001;
pub const SERVICE_BLOCK_PRESENT: BeaconFlags = 0b0000_0010;
pub const BEACON_PERIOD_PRESENT: BeaconFlags = 0b0000_0100;
pub const RESERVED_BITS: BeaconFlags = 0b1111_1000;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Beacon {
version: u8,
flags: BeaconFlags,
eid: EndpointID,
beacon_sequence_number: u32,
service_block: ServiceBlock,
beacon_period: Option<Duration>,
}
impl Beacon {
pub fn new(eid: EndpointID) -> Beacon {
Beacon {
version: IPND_VERSION,
flags: SOURCE_EID_PRESENT,
eid,
beacon_sequence_number: 0,
service_block: ServiceBlock::new(),
beacon_period: None,
}
}
pub fn with_config(
eid: EndpointID,
service_block: ServiceBlock,
beacon_period: Option<Duration>,
) -> Beacon {
let mut beacon = Beacon {
version: IPND_VERSION,
flags: SOURCE_EID_PRESENT,
eid,
beacon_sequence_number: 0,
service_block,
beacon_period,
};
if !beacon.service_block().is_empty() {
beacon.add_flags(SERVICE_BLOCK_PRESENT);
}
if let Some(_bp) = beacon.beacon_period() {
beacon.add_flags(BEACON_PERIOD_PRESENT);
}
beacon
}
pub fn version(&self) -> String {
format!("{:#x}", self.version)
}
pub fn flags(&self) -> String {
format!("{:#010b}", self.flags)
}
pub fn eid(&self) -> &EndpointID {
&self.eid
}
pub fn beacon_sequence_number(&self) -> u32 {
self.beacon_sequence_number
}
pub fn service_block(&self) -> &ServiceBlock {
&self.service_block
}
pub fn beacon_period(&self) -> Option<Duration> {
self.beacon_period
}
fn add_flags(&mut self, flags: BeaconFlags) {
self.flags |= flags;
}
pub fn set_beacon_sequence_number(&mut self, bsn: u32) {
self.beacon_sequence_number = bsn;
}
pub fn add_cla(&mut self, name: &str, port: &Option<u16>) {
self.service_block.add_cla(name, port);
self.add_flags(SERVICE_BLOCK_PRESENT);
}
pub fn add_custom_service(&mut self, tag: u8, service: String) {
let payload = ServiceBlock::build_custom_service(tag, service.as_str())
.expect("Error while parsing Service to byte format");
self.service_block.add_custom_service(tag, &payload.1);
self.add_flags(SERVICE_BLOCK_PRESENT);
}
}
impl std::fmt::Display for Beacon {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let temp = format!("{:010b}", self.flags);
let output = if self.beacon_period.is_some() {
format!("Version: {:#x}\tFlags: {}\tBeaconSequenceNumber: {}\nEID: {}\nServiceBlock:\n{}\nBeaconPeriod: {:#?}",
self.version, temp, self.beacon_sequence_number, self.eid, self.service_block, self.beacon_period.unwrap())
} else {
format!("Version: {:#x}\tFlags: {}\tBeaconSequenceNumber: {}\nEID: {}\nServiceBlock:\n{}\nBeaconPeriod: None",
self.version, temp, self.beacon_sequence_number, self.eid, self.service_block)
};
write!(f, "{}", output)
}
}
impl Serialize for Beacon {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let num_elems = if self.beacon_period.is_some() && !self.service_block.is_empty() {
6
} else if self.beacon_period.is_none() && self.service_block.is_empty() {
4
} else {
5
};
let mut seq = serializer.serialize_seq(Some(num_elems))?;
seq.serialize_element(&self.version)?;
seq.serialize_element(&self.flags)?;
seq.serialize_element(&self.eid)?;
seq.serialize_element(&self.beacon_sequence_number)?;
if !self.service_block.is_empty() {
seq.serialize_element(&self.service_block)?;
}
if self.beacon_period.is_some() {
let period_number = self.beacon_period.unwrap().as_secs();
seq.serialize_element(&period_number)?;
}
seq.end()
}
}
impl<'de> Deserialize<'de> for Beacon {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct BeaconVisitor;
impl<'de> Visitor<'de> for BeaconVisitor {
type Value = Beacon;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("beacon")
}
fn visit_seq<V>(self, mut seq: V) -> Result<Self::Value, V::Error>
where
V: SeqAccess<'de>,
{
let version = seq
.next_element()?
.ok_or_else(|| de::Error::invalid_length(0, &self))?;
let flags = seq
.next_element()?
.ok_or_else(|| de::Error::invalid_length(1, &self))?;
let eid = seq
.next_element()?
.ok_or_else(|| de::Error::invalid_length(2, &self))?;
let beacon_sequence_number = seq
.next_element()?
.ok_or_else(|| de::Error::invalid_length(3, &self))?;
if seq.size_hint().unwrap() == 0 {
Ok(Beacon {
version,
flags,
eid,
beacon_sequence_number,
service_block: ServiceBlock::new(),
beacon_period: None,
})
} else if seq.size_hint().unwrap() == 1 {
if (flags & SERVICE_BLOCK_PRESENT) == SERVICE_BLOCK_PRESENT {
Ok(Beacon {
version,
flags,
eid,
beacon_sequence_number,
service_block: seq.next_element()?.unwrap(),
beacon_period: None,
})
} else {
Ok(Beacon {
version,
flags,
eid,
beacon_sequence_number,
service_block: ServiceBlock::new(),
beacon_period: Some(Duration::from_secs(seq.next_element()?.unwrap())),
})
}
} else {
let service_block = seq.next_element()?.unwrap();
let beacon_period = Some(Duration::from_secs(seq.next_element()?.unwrap()));
Ok(Beacon {
version,
flags,
eid,
beacon_sequence_number,
service_block,
beacon_period,
})
}
}
}
deserializer.deserialize_any(BeaconVisitor)
}
}
impl Block for Beacon {
fn to_cbor(&self) -> ByteBuffer {
serde_cbor::to_vec(&self).expect("Error exporting Beacon to cbor")
}
}