mod peer_id;
pub use peer_id::{ParseErr as PeerIdParseErr, PeerId};
use scale::{Decode, Encode, MaxEncodedLen};
use std::{net::SocketAddr, str::FromStr};
#[derive(Clone, Copy, Encode, Decode, MaxEncodedLen, PartialEq, Eq)]
pub struct PeerAddr {
pub ip: [u8; 16],
pub port: u16,
}
impl core::fmt::Debug for PeerAddr {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
SocketAddr::from(*self).fmt(f)
}
}
impl core::fmt::Display for PeerAddr {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
SocketAddr::from(*self).fmt(f)
}
}
impl TryFrom<&str> for PeerAddr {
type Error = std::net::AddrParseError;
fn try_from(s: &str) -> Result<Self, Self::Error> {
Ok(SocketAddr::from_str(s)?.into())
}
}
impl FromStr for PeerAddr {
type Err = std::net::AddrParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
s.try_into()
}
}
impl From<SocketAddr> for PeerAddr {
fn from(addr: SocketAddr) -> Self {
let ip = match addr.ip() {
std::net::IpAddr::V4(ip) => ip.to_ipv6_mapped(),
std::net::IpAddr::V6(ip) => ip,
};
Self { ip: ip.octets(), port: addr.port() }
}
}
impl From<PeerAddr> for SocketAddr {
fn from(addr: PeerAddr) -> Self {
Self::new(std::net::Ipv6Addr::from(addr.ip).to_canonical(), addr.port)
}
}
#[derive(Clone, Encode, Decode, MaxEncodedLen, PartialEq, Eq)]
pub struct PeerDetails {
pub id: PeerId,
pub addr: PeerAddr,
}
impl core::fmt::Debug for PeerDetails {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
write!(f, "{:?}@{:?}", self.id, self.addr)
}
}
impl core::fmt::Display for PeerDetails {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
write!(f, "{}@{}", self.id, self.addr)
}
}
#[derive(Debug, thiserror::Error)]
pub enum PeerDetailsParseErr {
#[error("No @ character; expected between peer ID and address")]
NoSeparator,
#[error("Bad peer ID: {0}")]
Id(#[from] PeerIdParseErr),
#[error("Bad address: {0}")]
Addr(#[from] std::net::AddrParseError),
}
impl TryFrom<&str> for PeerDetails {
type Error = PeerDetailsParseErr;
fn try_from(s: &str) -> Result<Self, Self::Error> {
let Some((id, addr)) = s.split_once('@') else {
return Err(PeerDetailsParseErr::NoSeparator)
};
Ok(Self { id: id.try_into()?, addr: addr.try_into()? })
}
}
impl FromStr for PeerDetails {
type Err = PeerDetailsParseErr;
fn from_str(s: &str) -> Result<Self, Self::Err> {
s.try_into()
}
}
#[cfg(any(test, feature = "rand"))]
impl rand::distr::Distribution<PeerAddr> for rand::distr::StandardUniform {
fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> PeerAddr {
let ip = if rng.random() {
(rng.random::<u32>() as u128) | (0xffff << 32)
} else {
(rng.random::<u128>() & !(0x7 << 125)) | (1 << 125)
};
PeerAddr { ip: ip.to_be_bytes(), port: rng.random() }
}
}
#[cfg(any(test, feature = "rand"))]
impl rand::distr::Distribution<PeerDetails> for rand::distr::StandardUniform {
fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> PeerDetails {
PeerDetails { id: rng.random(), addr: rng.random() }
}
}