use std::{fmt::Display, net::IpAddr, str::FromStr};
use serde_with::{DeserializeFromStr, SerializeDisplay};
use utoipa::{PartialSchema, ToSchema, openapi::Type};
use crate::{
address::{
addr::{ScionAddr, ScionAddrV4, ScionAddrV6},
ip_socket_addr::ScionSocketIpAddr,
},
core::macros::impl_from,
scion::{
address::{AddressParseError, host_addr::ScionHostAddr},
identifier::isd_asn::IsdAsn,
},
};
#[derive(
Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, SerializeDisplay, DeserializeFromStr,
)]
#[cfg_attr(feature = "proptest", derive(proptest_derive::Arbitrary))]
pub enum ScionIpAddr {
V4(ScionAddrV4),
V6(ScionAddrV6),
}
impl ScionIpAddr {
#[inline]
pub const fn new(ia: IsdAsn, host: IpAddr) -> Self {
match host {
IpAddr::V4(host) => Self::V4(ScionAddrV4::new(ia, host)),
IpAddr::V6(host) => Self::V6(ScionAddrV6::new(ia, host)),
}
}
#[inline]
pub const fn isd_asn(&self) -> IsdAsn {
match self {
ScionIpAddr::V4(addr) => addr.isd_asn,
ScionIpAddr::V6(addr) => addr.isd_asn,
}
}
#[inline]
pub fn set_isd_asn(&mut self, ia: IsdAsn) {
match self {
ScionIpAddr::V4(addr) => addr.isd_asn = ia,
ScionIpAddr::V6(addr) => addr.isd_asn = ia,
}
}
#[inline]
pub const fn host(&self) -> ScionHostAddr {
match self {
ScionIpAddr::V4(addr) => ScionHostAddr::V4(addr.host),
ScionIpAddr::V6(addr) => ScionHostAddr::V6(addr.host),
}
}
#[inline]
pub fn set_host(&mut self, host: IpAddr) {
*self = Self::new(self.isd_asn(), host);
}
#[inline]
pub const fn ip(&self) -> std::net::IpAddr {
match self {
ScionIpAddr::V4(addr) => std::net::IpAddr::V4(addr.host),
ScionIpAddr::V6(addr) => std::net::IpAddr::V6(addr.host),
}
}
#[inline]
pub const fn is_ipv4(&self) -> bool {
matches!(self, ScionIpAddr::V4(_))
}
#[inline]
pub const fn is_ipv6(&self) -> bool {
matches!(self, ScionIpAddr::V6(_))
}
#[inline]
pub const fn into_scion_addr(self) -> ScionAddr {
match self {
ScionIpAddr::V4(addr) => ScionAddr::V4(addr),
ScionIpAddr::V6(addr) => ScionAddr::V6(addr),
}
}
}
impl FromStr for ScionIpAddr {
type Err = AddressParseError;
#[inline]
fn from_str(s: &str) -> Result<Self, Self::Err> {
if let Ok(addr) = ScionAddrV4::from_str(s) {
Ok(ScionIpAddr::V4(addr))
} else if let Ok(addr) = ScionAddrV6::from_str(s) {
Ok(ScionIpAddr::V6(addr))
} else {
Err(AddressParseError::Scion)
}
}
}
impl Display for ScionIpAddr {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ScionIpAddr::V4(addr) => addr.fmt(f),
ScionIpAddr::V6(addr) => addr.fmt(f),
}
}
}
impl_from!(ScionAddrV4, ScionIpAddr, |v| ScionIpAddr::V4(v));
impl_from!(ScionAddrV6, ScionIpAddr, |v| ScionIpAddr::V6(v));
impl_from!(ScionSocketIpAddr, ScionIpAddr, |v| {
ScionIpAddr::new(v.isd_asn(), v.ip())
});
impl TryFrom<ScionAddr> for ScionIpAddr {
type Error = ScionAddr;
#[inline]
fn try_from(value: ScionAddr) -> Result<Self, Self::Error> {
value.try_into_scion_ip_addr()
}
}
impl PartialSchema for ScionIpAddr {
fn schema() -> utoipa::openapi::RefOr<utoipa::openapi::schema::Schema> {
utoipa::openapi::ObjectBuilder::new()
.examples(["1-ff00:0:110,192.0.2.1", "1-ff00:0:110,2001:db8::1"])
.schema_type(Type::String)
.into()
}
}
impl ToSchema for ScionIpAddr {}