1use crate::error::Error;
2use std::{
3 fmt,
4 net::{IpAddr, SocketAddr},
5 str::FromStr,
6};
7
8#[derive(Debug, Default, Clone, PartialEq, Eq)]
9pub struct Multiaddr {
10 protocols: Vec<Protocol>,
11}
12
13impl Multiaddr {
14 pub fn is_tls(&self) -> bool {
15 self.protocols.iter().any(|p| matches!(p, Protocol::Tls))
16 }
17
18 pub fn is_http(&self) -> bool {
19 self.protocols
20 .iter()
21 .any(|p| matches!(p, Protocol::Http(_)))
22 }
23
24 pub fn is_udp(&self) -> bool {
25 self.protocols.iter().any(|p| matches!(p, Protocol::Udp(_)))
26 }
27
28 pub fn is_quic(&self) -> bool {
29 self.protocols.iter().any(|p| matches!(p, Protocol::Quic))
30 }
31
32 pub fn socket_addr(&self) -> Result<SocketAddr, Error> {
33 self.ip_addr()
34 .and_then(|ip| self.port().map(|port| SocketAddr::new(ip, port)))
35 }
36
37 pub fn ip_addr(&self) -> Result<IpAddr, Error> {
38 self.protocols
39 .iter()
40 .find_map(|p| match p {
41 Protocol::Ip(addr) => Some(*addr),
42 _ => None,
43 })
44 .ok_or_else(|| Error::InvalidMultiaddr {
45 addr: self.to_string(),
46 })
47 }
48
49 pub fn port(&self) -> Result<u16, Error> {
50 self.protocols
51 .iter()
52 .find_map(|p| match p {
53 Protocol::Tcp(port) => Some(*port),
54 Protocol::Udp(port) => Some(*port),
55 _ => None,
56 })
57 .ok_or_else(|| Error::InvalidMultiaddr {
58 addr: self.to_string(),
59 })
60 }
61
62 pub fn host(&self) -> Result<String, Error> {
63 self.protocols
64 .iter()
65 .find_map(|p| match p {
66 Protocol::Dns(host) => Some(host.clone()),
67 Protocol::Ip(addr) => Some(addr.to_string()),
68 _ => None,
69 })
70 .ok_or_else(|| Error::InvalidMultiaddr {
71 addr: self.to_string(),
72 })
73 }
74
75 pub fn protocol_name(&self) -> &'static str {
76 match (self.is_udp(), self.is_http(), self.is_tls(), self.is_quic()) {
77 (_, false, _, true) => "QUIC",
78 (_, true, _, true) => "HTTP over QUIC",
79 (true, _, _, _) => "UDP",
80 (false, true, true, _) => "HTTPS",
81 (false, true, false, _) => "HTTP",
82 (false, false, true, _) => "TCP over TLS",
83 (false, false, false, _) => "TCP",
84 }
85 }
86}
87
88#[derive(Debug, Clone, PartialEq, Eq)]
89pub enum Protocol {
90 Dns(String),
91 Ip(IpAddr),
92 Tcp(u16),
93 Udp(u16),
94 Tls,
95 Http(String),
96 Quic,
97}
98
99impl FromStr for Multiaddr {
100 type Err = Error;
101
102 fn from_str(s: &str) -> Result<Self, Self::Err> {
103 let mut protocols = Vec::new();
104 let mut rest = s.trim_start_matches('/');
105 while !rest.is_empty() {
106 let (protocol, next) = rest.split_once('/').unwrap_or((rest, ""));
107 match protocol {
108 "dns" => {
109 let (host, next) = next.split_once('/').unwrap_or((next, ""));
110 protocols.push(Protocol::Dns(host.to_string()));
111 rest = next;
112 }
113 "ip4" | "ip6" => {
114 let (addr, next) = next.split_once('/').unwrap_or((next, ""));
115 let addr = addr
116 .parse::<IpAddr>()
117 .map_err(|_| Error::InvalidMultiaddr {
118 addr: s.to_string(),
119 })?;
120 protocols.push(Protocol::Ip(addr));
121 rest = next;
122 }
123 "tcp" => {
124 let (port, next) = next.split_once('/').unwrap_or((next, ""));
125 let port = port.parse::<u16>().map_err(|_| Error::InvalidMultiaddr {
126 addr: s.to_string(),
127 })?;
128 protocols.push(Protocol::Tcp(port));
129 rest = next;
130 }
131 "tls" => {
132 protocols.push(Protocol::Tls);
133 rest = next;
134 }
135 "quic" => {
136 protocols.push(Protocol::Quic);
137 rest = next;
138 }
139 "http" => {
140 protocols.push(Protocol::Http(format!("/{next}")));
141 rest = "";
142 }
143 "https" => {
144 protocols.push(Protocol::Tls);
145 protocols.push(Protocol::Http(format!("/{next}")));
146 rest = "";
147 }
148 "udp" => {
149 let (port, next) = next.split_once('/').unwrap_or((next, ""));
150 let port = port.parse::<u16>().map_err(|_| Error::InvalidMultiaddr {
151 addr: s.to_string(),
152 })?;
153 protocols.push(Protocol::Udp(port));
154 rest = next;
155 }
156 _ => rest = next,
157 }
158 }
159 Ok(Self { protocols })
160 }
161}
162
163impl fmt::Display for Multiaddr {
164 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
165 for protocol in &self.protocols {
166 match protocol {
167 Protocol::Dns(host) => write!(f, "/dns/{}", host)?,
168 Protocol::Ip(addr) => {
169 if addr.is_ipv4() {
170 write!(f, "/ip4/{}", addr)?;
171 } else {
172 write!(f, "/ip6/{}", addr)?;
173 }
174 }
175 Protocol::Tcp(port) => write!(f, "/tcp/{}", port)?,
176 Protocol::Udp(port) => write!(f, "/udp/{}", port)?,
177 Protocol::Tls => {
178 if !self.is_http() {
179 write!(f, "/tls")?
180 }
181 }
182 Protocol::Http(path) => {
183 let path = if path == "/" || path.is_empty() {
184 ""
185 } else {
186 path.as_str()
187 };
188 if self.is_tls() {
189 write!(f, "/https{}", path)?;
190 } else {
191 write!(f, "/http{}", path)?;
192 }
193 }
194 Protocol::Quic => write!(f, "/quic")?,
195 }
196 }
197 Ok(())
198 }
199}
200
201impl serde::Serialize for Multiaddr {
202 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
203 serializer.serialize_str(&self.to_string())
204 }
205}
206
207impl<'de> serde::Deserialize<'de> for Multiaddr {
208 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
209 let s = String::deserialize(deserializer)?;
210 Multiaddr::from_str(&s).map_err(serde::de::Error::custom)
211 }
212}
213
214#[cfg(test)]
215mod test {
216 use super::*;
217
218 #[test]
219 fn test_multiaddr() {
220 let addr = Multiaddr::from_str("/dns/example.com/tcp/8080").unwrap();
221 assert_eq!(addr.to_string(), "/dns/example.com/tcp/8080");
222 assert!(!addr.is_http());
223 assert!(!addr.is_tls());
224
225 let addr = Multiaddr::from_str("/ip4/127.0.0.1/tcp/8080").unwrap();
226 assert_eq!(addr.to_string(), "/ip4/127.0.0.1/tcp/8080");
227 assert!(!addr.is_http());
228 assert!(!addr.is_tls());
229
230 let addr = Multiaddr::from_str("/ip4/127.0.0.1/tcp/8080/tls").unwrap();
231 assert_eq!(addr.to_string(), "/ip4/127.0.0.1/tcp/8080/tls");
232 assert!(!addr.is_http());
233 assert!(addr.is_tls());
234
235 let addr = Multiaddr::from_str("/ip4/127.0.0.1/tcp/8080/http").unwrap();
236 assert_eq!(addr.to_string(), "/ip4/127.0.0.1/tcp/8080/http");
237 assert!(addr.is_http());
238 assert!(!addr.is_tls());
239
240 let addr = Multiaddr::from_str("/ip6/::/tcp/8080/https/example.com/index.html").unwrap();
241 assert_eq!(
242 addr.to_string(),
243 "/ip6/::/tcp/8080/https/example.com/index.html"
244 );
245 assert!(addr.is_http());
246 assert!(addr.is_tls());
247
248 let addr = Multiaddr::from_str("/ip4/127.0.0.1/udp/8080").unwrap();
249 assert_eq!(addr.to_string(), "/ip4/127.0.0.1/udp/8080");
250 assert!(!addr.is_http());
251 assert!(!addr.is_tls());
252 assert!(addr.is_udp());
253
254 let addr = Multiaddr::from_str("/ip4/127.0.0.1/udp/8080/quic/http").unwrap();
255 assert_eq!(addr.to_string(), "/ip4/127.0.0.1/udp/8080/quic/http");
256 assert!(addr.is_http());
257 assert!(!addr.is_tls());
258 assert!(addr.is_udp());
259 assert!(addr.is_quic());
260 }
261}