1use core::{
4 fmt::{self, Display},
5 str::{self, FromStr},
6};
7
8use serde::{de::Error as _, Deserialize, Deserializer, Serialize, Serializer};
9use tendermint::node::{self, info::ListenAddress};
10use url::Url;
11
12use crate::{error::Error, prelude::*};
13
14pub const TCP_PREFIX: &str = "tcp://";
16
17pub const UNIX_PREFIX: &str = "unix://";
19
20#[derive(Clone, Debug, Eq, Hash, PartialEq)]
28pub enum Address {
29 Tcp {
31 peer_id: Option<node::Id>,
33
34 host: String,
36
37 port: u16,
39 },
40
41 Unix {
43 path: String,
45 },
46}
47
48impl Address {
49 pub fn from_listen_address(address: &ListenAddress) -> Option<Self> {
51 let raw_address = address.as_str();
52 if raw_address.starts_with("tcp://") {
54 raw_address.parse().ok()
55 } else {
56 format!("tcp://{raw_address}").parse().ok()
57 }
58 }
59}
60
61impl<'de> Deserialize<'de> for Address {
62 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
63 Self::from_str(&String::deserialize(deserializer)?)
64 .map_err(|e| D::Error::custom(format!("{e}")))
65 }
66}
67
68impl Display for Address {
69 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
70 match self {
71 Address::Tcp {
72 peer_id: None,
73 host,
74 port,
75 } => write!(f, "{TCP_PREFIX}{host}:{port}"),
76 Address::Tcp {
77 peer_id: Some(peer_id),
78 host,
79 port,
80 } => write!(f, "{TCP_PREFIX}{peer_id}@{host}:{port}"),
81 Address::Unix { path } => write!(f, "{UNIX_PREFIX}{path}"),
82 }
83 }
84}
85
86impl FromStr for Address {
87 type Err = Error;
88
89 fn from_str(addr: &str) -> Result<Self, Error> {
90 if addr.starts_with("unix://@") {
92 return Ok(Self::Unix {
93 path: addr.strip_prefix("unix://").unwrap().to_owned(),
94 });
95 }
96
97 let prefixed_addr = if addr.contains("://") {
98 addr.to_owned()
99 } else {
100 format!("{TCP_PREFIX}{addr}")
102 };
103 let url = Url::parse(&prefixed_addr).map_err(Error::parse_url)?;
104 match url.scheme() {
105 "tcp" => Ok(Self::Tcp {
106 peer_id: if !url.username().is_empty() {
107 let username = url.username().parse().map_err(Error::tendermint)?;
108 Some(username)
109 } else {
110 None
111 },
112 host: url
113 .host_str()
114 .ok_or_else(|| {
115 Error::parse(format!("invalid TCP address (missing host): {addr}"))
116 })?
117 .to_owned(),
118 port: url.port().ok_or_else(|| {
119 Error::parse(format!("invalid TCP address (missing port): {addr}"))
120 })?,
121 }),
122 "unix" => Ok(Self::Unix {
123 path: url.path().to_string(),
124 }),
125 _ => Err(Error::parse(format!("invalid address scheme: {addr:?}"))),
126 }
127 }
128}
129
130impl Serialize for Address {
131 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
132 self.to_string().serialize(serializer)
133 }
134}
135
136#[cfg(test)]
137mod tests {
138 use super::*;
139
140 const EXAMPLE_TCP_ADDR: &str =
141 "tcp://abd636b766dcefb5322d8ca40011ec2cb35efbc2@35.192.61.41:26656";
142 const EXAMPLE_TCP_ADDR_WITHOUT_ID: &str = "tcp://35.192.61.41:26656";
143 const EXAMPLE_UNIX_ADDR: &str = "unix:///tmp/node.sock";
144 const EXAMPLE_TCP_IPV6_ADDR: &str =
145 "tcp://abd636b766dcefb5322d8ca40011ec2cb35efbc2@[2001:0000:3238:DFE1:0063:0000:0000:FEFB]:26656";
146
147 #[test]
148 fn parse_tcp_addr() {
149 let tcp_addr_without_prefix = &EXAMPLE_TCP_ADDR[TCP_PREFIX.len()..];
150
151 for tcp_addr in &[EXAMPLE_TCP_ADDR, tcp_addr_without_prefix] {
152 match tcp_addr.parse::<Address>().unwrap() {
153 Address::Tcp {
154 peer_id,
155 host,
156 port,
157 } => {
158 assert_eq!(
159 peer_id.unwrap(),
160 "abd636b766dcefb5322d8ca40011ec2cb35efbc2"
161 .parse::<node::Id>()
162 .unwrap()
163 );
164 assert_eq!(host, "35.192.61.41");
165 assert_eq!(port, 26656);
166 },
167 other => panic!("unexpected address type: {other:?}"),
168 }
169 }
170 }
171
172 #[test]
173 fn parse_tcp_addr_without_id() {
174 let addr = EXAMPLE_TCP_ADDR_WITHOUT_ID.parse::<Address>().unwrap();
175 let addr_without_prefix = EXAMPLE_TCP_ADDR_WITHOUT_ID[TCP_PREFIX.len()..]
176 .parse::<Address>()
177 .unwrap();
178 for addr in &[addr, addr_without_prefix] {
179 match addr {
180 Address::Tcp {
181 peer_id,
182 host,
183 port,
184 } => {
185 assert!(peer_id.is_none());
186 assert_eq!(host, "35.192.61.41");
187 assert_eq!(*port, 26656);
188 },
189 other => panic!("unexpected address type: {other:?}"),
190 }
191 }
192 }
193
194 #[test]
195 fn parse_unix_addr() {
196 let addr = EXAMPLE_UNIX_ADDR.parse::<Address>().unwrap();
197 match addr {
198 Address::Unix { path } => {
199 assert_eq!(path, "/tmp/node.sock");
200 },
201 other => panic!("unexpected address type: {other:?}"),
202 }
203 }
204
205 #[test]
206 fn parse_tcp_ipv6_addr() {
207 let addr = EXAMPLE_TCP_IPV6_ADDR.parse::<Address>().unwrap();
208 let addr_without_prefix = EXAMPLE_TCP_IPV6_ADDR[TCP_PREFIX.len()..]
209 .parse::<Address>()
210 .unwrap();
211 for addr in &[addr, addr_without_prefix] {
212 match addr {
213 Address::Tcp {
214 peer_id,
215 host,
216 port,
217 } => {
218 assert_eq!(
219 peer_id.unwrap(),
220 "abd636b766dcefb5322d8ca40011ec2cb35efbc2"
221 .parse::<node::Id>()
222 .unwrap()
223 );
224 assert_eq!(host, "[2001:0:3238:dfe1:63::fefb]");
226 assert_eq!(*port, 26656);
227 },
228 other => panic!("unexpected address type: {other:?}"),
229 }
230 }
231 }
232}