use super::bundle::*;
use core::convert::From;
use core::fmt;
use serde::de::{SeqAccess, Visitor};
use crate::helpers::Url;
use serde::{de, Deserialize, Deserializer, Serialize};
pub const ENDPOINT_URI_SCHEME_DTN: u8 = 1;
pub const ENDPOINT_URI_SCHEME_IPN: u8 = 2;
pub const DTN_NONE: EndpointID = EndpointID::DtnNone(ENDPOINT_URI_SCHEME_DTN, 0);
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub struct IpnAddress(pub u64, pub u64);
#[derive(Debug, Clone, Serialize, PartialEq, Eq, Hash)]
#[serde(untagged)]
pub enum EndpointID {
Dtn(u8, String), DtnNone(u8, u8),
Ipn(u8, IpnAddress),
}
impl<'de> Deserialize<'de> for EndpointID {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct EndpointIDVisitor;
impl<'de> Visitor<'de> for EndpointIDVisitor {
type Value = EndpointID;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("EndpointID")
}
fn visit_seq<V>(self, mut seq: V) -> Result<Self::Value, V::Error>
where
V: SeqAccess<'de>,
{
let eid_type: u8 = seq
.next_element()?
.ok_or_else(|| de::Error::invalid_length(0, &self))?;
if eid_type == ENDPOINT_URI_SCHEME_DTN {
let name: String = seq.next_element().unwrap_or_default().unwrap_or_default();
if name == "" {
Ok(EndpointID::with_dtn_none())
} else {
Ok(EndpointID::Dtn(eid_type, name))
}
} else if eid_type == ENDPOINT_URI_SCHEME_IPN {
let ipnaddr: IpnAddress = seq
.next_element()?
.ok_or_else(|| de::Error::invalid_length(1, &self))?;
Ok(EndpointID::with_ipn(ipnaddr))
} else {
Err(de::Error::invalid_value(
de::Unexpected::Unsigned(eid_type.into()),
&self,
))
}
}
}
deserializer.deserialize_any(EndpointIDVisitor)
}
}
impl Default for EndpointID {
fn default() -> Self {
EndpointID::DtnNone(ENDPOINT_URI_SCHEME_DTN, 0)
}
}
impl EndpointID {
pub fn new() -> EndpointID {
Default::default()
}
pub fn with_dtn(addr: &str) -> EndpointID {
EndpointID::Dtn(ENDPOINT_URI_SCHEME_DTN, addr.into())
}
pub fn with_dtn_none() -> EndpointID {
EndpointID::DtnNone(ENDPOINT_URI_SCHEME_DTN, 0)
}
pub fn with_ipn(addr: IpnAddress) -> EndpointID {
EndpointID::Ipn(ENDPOINT_URI_SCHEME_IPN, addr)
}
pub fn scheme(&self) -> String {
match self {
EndpointID::DtnNone(_, _) => "dtn".to_string(),
EndpointID::Dtn(_, _) => "dtn".to_string(),
EndpointID::Ipn(_, _) => "ipn".to_string(),
}
}
pub fn scheme_specific_part_dtn(&self) -> Option<String> {
match self {
EndpointID::Dtn(_, ssp) => Some(ssp.to_string()),
_ => None,
}
}
}
impl fmt::Display for EndpointID {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}://{}",
self.scheme(),
self.scheme_specific_part_dtn()
.unwrap_or_else(|| "none".to_string())
)
}
}
impl EndpointID {
pub fn node_part(&self) -> Option<String> {
match self {
EndpointID::DtnNone(_, _) => None,
EndpointID::Dtn(_, eid) => {
let nodeid: Vec<&str> = eid.split('/').collect();
Some(nodeid[0].to_string())
}
EndpointID::Ipn(_, addr) => Some(addr.0.to_string()),
}
}
pub fn is_node_id(&self) -> bool {
match self {
EndpointID::DtnNone(_, _) => false,
EndpointID::Dtn(_, eid) => self.node_part() == Some(eid.to_string()),
EndpointID::Ipn(_, addr) => addr.1 == 0,
}
}
pub fn validation_error(&self) -> Option<Bp7Error> {
match self {
EndpointID::Dtn(_, _) => None, EndpointID::Ipn(code, addr) => {
if *code != ENDPOINT_URI_SCHEME_IPN {
Some(Bp7Error::EIDError(
"Wrong URI scheme code for IPN".to_string(),
))
} else if addr.0 < 1 || addr.1 < 1 {
Some(Bp7Error::EIDError(
"IPN's node and service number must be >= 1".to_string(),
))
} else {
None
}
}
EndpointID::DtnNone(code, addr) => {
if *code != ENDPOINT_URI_SCHEME_DTN {
Some(Bp7Error::EIDError(
"Wrong URI scheme code for DTN".to_string(),
))
} else if *addr != 0 {
Some(Bp7Error::EIDError(
"dtn none must have uint(0) set as address".to_string(),
))
} else {
None
}
}
}
}
}
impl From<String> for EndpointID {
fn from(item: String) -> Self {
let item = if item.contains("://") {
item
} else {
item.replace(":", "://")
};
let u = Url::parse(&item).expect("EndpointID url parsing error");
let host = u.host();
match u.scheme() {
"dtn" => {
if host == "none" {
return <EndpointID>::with_dtn_none();
}
let mut host = format!("{}{}", host, u.path());
if host.ends_with('/') {
host.truncate(host.len() - 1);
}
EndpointID::with_dtn(&host)
}
"ipn" => {
let fields: Vec<&str> = host.split('.').collect();
if fields.len() != 2 {
panic!("wrong number of fields in IPN address");
}
let p1: u64 = fields[0].parse().unwrap();
let p2: u64 = fields[1].parse().unwrap();
EndpointID::with_ipn(IpnAddress(p1, p2))
}
_ => <EndpointID>::with_dtn_none(),
}
}
}
impl From<&str> for EndpointID {
fn from(item: &str) -> Self {
EndpointID::from(String::from(item))
}
}