taxy_api/
multiaddr.rs

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}