jam_std_common/net/
mod.rs1mod peer_id;
2
3use codec::{ConstEncodedLen, Decode, Encode, MaxEncodedLen};
4pub use peer_id::{P256PeerId, ParseErr as PeerIdParseErr, PeerId};
5use std::{net::SocketAddr, str::FromStr};
6
7#[derive(Clone, Copy, Encode, Decode, MaxEncodedLen, PartialEq, Eq)]
11pub struct PeerAddr {
12 pub ip: [u8; 16],
15 pub port: u16,
17}
18
19impl ConstEncodedLen for PeerAddr {}
20
21impl core::fmt::Debug for PeerAddr {
22 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
23 SocketAddr::from(*self).fmt(f)
24 }
25}
26
27impl core::fmt::Display for PeerAddr {
28 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
29 SocketAddr::from(*self).fmt(f)
30 }
31}
32
33impl TryFrom<&str> for PeerAddr {
34 type Error = std::net::AddrParseError;
35
36 fn try_from(s: &str) -> Result<Self, Self::Error> {
37 Ok(SocketAddr::from_str(s)?.into())
38 }
39}
40
41impl FromStr for PeerAddr {
42 type Err = std::net::AddrParseError;
43
44 fn from_str(s: &str) -> Result<Self, Self::Err> {
45 s.try_into()
46 }
47}
48
49impl From<SocketAddr> for PeerAddr {
50 fn from(addr: SocketAddr) -> Self {
51 let ip = match addr.ip() {
52 std::net::IpAddr::V4(ip) => ip.to_ipv6_mapped(),
53 std::net::IpAddr::V6(ip) => ip,
54 };
55 Self { ip: ip.octets(), port: addr.port() }
56 }
57}
58
59impl From<PeerAddr> for SocketAddr {
60 fn from(addr: PeerAddr) -> Self {
61 Self::new(std::net::Ipv6Addr::from(addr.ip).to_canonical(), addr.port)
62 }
63}
64
65#[derive(Clone, Encode, Decode, MaxEncodedLen, PartialEq, Eq)]
67pub struct PeerDetails {
68 pub id: PeerId,
69 pub addr: PeerAddr,
70}
71
72impl core::fmt::Debug for PeerDetails {
73 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
74 write!(f, "{:?}@{:?}", self.id, self.addr)
75 }
76}
77
78impl core::fmt::Display for PeerDetails {
79 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
80 write!(f, "{}@{}", self.id, self.addr)
81 }
82}
83
84#[derive(Debug, thiserror::Error)]
86pub enum PeerDetailsParseErr {
87 #[error("No @ character; expected between peer ID and address")]
88 NoSeparator,
89 #[error("Bad peer ID: {0}")]
90 Id(#[from] PeerIdParseErr),
91 #[error("Bad address: {0}")]
92 Addr(#[from] std::net::AddrParseError),
93}
94
95impl TryFrom<&str> for PeerDetails {
96 type Error = PeerDetailsParseErr;
97
98 fn try_from(s: &str) -> Result<Self, Self::Error> {
99 let Some((id, addr)) = s.split_once('@') else {
100 return Err(PeerDetailsParseErr::NoSeparator)
101 };
102 Ok(Self { id: id.try_into()?, addr: addr.try_into()? })
103 }
104}
105
106impl FromStr for PeerDetails {
107 type Err = PeerDetailsParseErr;
108
109 fn from_str(s: &str) -> Result<Self, Self::Err> {
110 s.try_into()
111 }
112}
113
114impl serde::Serialize for PeerDetails {
115 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
116 serializer.collect_str(self)
117 }
118}
119
120impl<'de> serde::Deserialize<'de> for PeerDetails {
121 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
122 let text = <&str>::deserialize(deserializer)?;
123 text.parse().map_err(serde::de::Error::custom)
124 }
125}
126
127#[cfg(any(test, feature = "rand"))]
128impl rand::distr::Distribution<PeerAddr> for rand::distr::StandardUniform {
129 fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> PeerAddr {
130 let ip = if rng.random() {
131 (rng.random::<u32>() as u128) | (0xffff << 32)
133 } else {
134 (rng.random::<u128>() & !(0x7 << 125)) | (1 << 125)
136 };
137 PeerAddr { ip: ip.to_be_bytes(), port: rng.random() }
138 }
139}
140
141#[cfg(any(test, feature = "rand"))]
142impl rand::distr::Distribution<PeerDetails> for rand::distr::StandardUniform {
143 fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> PeerDetails {
144 PeerDetails { id: rng.random(), addr: rng.random() }
145 }
146}
147
148#[derive(Clone, Debug)]
150pub struct UnresolvedAddr {
151 pub host: String,
152 pub port: u16,
153}
154
155impl UnresolvedAddr {
156 pub fn as_ref(&self) -> (&str, u16) {
158 (&self.host, self.port)
159 }
160}
161
162impl std::fmt::Display for UnresolvedAddr {
163 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
164 write!(f, "{}:{}", self.host, self.port)
165 }
166}
167
168impl std::str::FromStr for UnresolvedAddr {
169 type Err = &'static str;
170
171 fn from_str(s: &str) -> Result<Self, Self::Err> {
172 let (host, port) = s.rsplit_once(':').ok_or("Expected ':' between host and port")?;
173 let port = port.parse().map_err(|_| "Bad port")?;
174 Ok(Self { host: host.to_string(), port })
175 }
176}
177
178#[cfg(test)]
179mod test {
180 #![allow(clippy::unwrap_used)]
181 use super::*;
182 use crate::SecretKeyset;
183
184 #[test]
185 fn peer_details_round_trip_as_a_json_string() {
186 let details = PeerDetails {
187 id: PeerId(SecretKeyset::trivial(0).ed25519.public()),
188 addr: "127.0.0.1:40000".parse::<SocketAddr>().unwrap().into(),
189 };
190
191 let json = serde_json::to_string(&details).unwrap();
192 assert_eq!(json, format!("\"{details}\""));
193 assert_eq!(serde_json::from_str::<PeerDetails>(&json).unwrap(), details);
194 }
195}