1use std::{
4 collections::BTreeMap,
5 fmt,
6 sync::{Arc, RwLock},
7};
8
9use iroh_base::{RelayUrl, RelayUrlParseError};
10use serde::{Deserialize, Serialize};
11
12use crate::defaults::DEFAULT_RELAY_QUIC_PORT;
13
14#[derive(Debug, Clone)]
30pub struct RelayMap {
31 relays: Arc<RwLock<BTreeMap<RelayUrl, Arc<RelayConfig>>>>,
33}
34
35impl PartialEq for RelayMap {
36 fn eq(&self, other: &Self) -> bool {
37 let this = self.relays.read().expect("poisoned");
38 let that = other.relays.read().expect("poisoned");
39 this.eq(&*that)
40 }
41}
42
43impl Eq for RelayMap {}
44
45impl RelayMap {
46 pub fn empty() -> Self {
48 Self {
49 relays: Default::default(),
50 }
51 }
52
53 pub fn try_from_iter<'a, T: IntoIterator<Item = &'a str>>(
65 urls: T,
66 ) -> Result<Self, RelayUrlParseError> {
67 let relays: BTreeMap<RelayUrl, Arc<RelayConfig>> = urls
68 .into_iter()
69 .map(|t| {
70 t.parse()
71 .map(|url: RelayUrl| (url.clone(), Arc::new(RelayConfig::from(url))))
72 })
73 .collect::<Result<_, _>>()?;
74 Ok(Self {
75 relays: Arc::new(RwLock::new(relays)),
76 })
77 }
78
79 pub fn urls<T>(&self) -> T
84 where
85 T: FromIterator<RelayUrl>,
86 {
87 self.relays
88 .read()
89 .expect("poisoned")
90 .keys()
91 .cloned()
92 .collect::<T>()
93 }
94
95 pub fn relays<T>(&self) -> T
100 where
101 T: FromIterator<Arc<RelayConfig>>,
102 {
103 self.relays
104 .read()
105 .expect("poisoned")
106 .values()
107 .cloned()
108 .collect::<T>()
109 }
110
111 pub fn contains(&self, url: &RelayUrl) -> bool {
113 self.relays.read().expect("poisoned").contains_key(url)
114 }
115
116 pub fn get(&self, url: &RelayUrl) -> Option<Arc<RelayConfig>> {
118 self.relays.read().expect("poisoned").get(url).cloned()
119 }
120
121 pub fn len(&self) -> usize {
123 self.relays.read().expect("poisoned").len()
124 }
125
126 pub fn is_empty(&self) -> bool {
128 self.relays.read().expect("poisoned").is_empty()
129 }
130
131 pub fn insert(&self, url: RelayUrl, endpoint: Arc<RelayConfig>) -> Option<Arc<RelayConfig>> {
133 self.relays.write().expect("poisoned").insert(url, endpoint)
134 }
135
136 pub fn remove(&self, url: &RelayUrl) -> Option<Arc<RelayConfig>> {
138 self.relays.write().expect("poisoned").remove(url)
139 }
140
141 pub fn extend(&self, other: &RelayMap) {
143 let mut a = self.relays.write().expect("poisoned");
144 let b = other.relays.read().expect("poisoned");
145 a.extend(b.iter().map(|(a, b)| (a.clone(), b.clone())));
146 }
147
148 pub fn with_auth_token(self, auth_token: impl Into<String>) -> Self {
155 let auth_token = auth_token.into();
156 for config in self.relays.write().expect("poisoned").values_mut() {
157 *config = Arc::new(config.as_ref().clone().with_auth_token(auth_token.clone()));
158 }
159 self
160 }
161}
162
163impl FromIterator<RelayConfig> for RelayMap {
164 fn from_iter<T: IntoIterator<Item = RelayConfig>>(iter: T) -> Self {
165 Self::from_iter(iter.into_iter().map(Arc::new))
166 }
167}
168
169impl FromIterator<Arc<RelayConfig>> for RelayMap {
170 fn from_iter<T: IntoIterator<Item = Arc<RelayConfig>>>(iter: T) -> Self {
171 Self {
172 relays: Arc::new(RwLock::new(
173 iter.into_iter()
174 .map(|config| (config.url.clone(), config))
175 .collect(),
176 )),
177 }
178 }
179}
180
181impl From<RelayUrl> for RelayMap {
182 fn from(value: RelayUrl) -> Self {
187 Self {
188 relays: Arc::new(RwLock::new(
189 [(value.clone(), Arc::new(value.into()))].into(),
190 )),
191 }
192 }
193}
194
195impl From<RelayConfig> for RelayMap {
196 fn from(value: RelayConfig) -> Self {
197 Self {
198 relays: Arc::new(RwLock::new([(value.url.clone(), Arc::new(value))].into())),
199 }
200 }
201}
202
203impl FromIterator<RelayUrl> for RelayMap {
204 fn from_iter<T: IntoIterator<Item = RelayUrl>>(iter: T) -> Self {
209 Self {
210 relays: Arc::new(RwLock::new(
211 iter.into_iter()
212 .map(|url| (url.clone(), Arc::new(url.into())))
213 .collect(),
214 )),
215 }
216 }
217}
218
219impl fmt::Display for RelayMap {
220 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
221 fmt::Debug::fmt(&self, f)
222 }
223}
224
225#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, PartialOrd, Ord)]
231#[non_exhaustive]
232pub struct RelayConfig {
233 pub url: RelayUrl,
235 #[serde(default = "quic_config")]
240 pub quic: Option<RelayQuicConfig>,
241 #[serde(default, skip_serializing_if = "Option::is_none")]
245 pub auth_token: Option<String>,
246}
247
248impl RelayConfig {
249 pub fn new(url: RelayUrl, quic: Option<RelayQuicConfig>) -> Self {
251 Self {
252 url,
253 quic,
254 auth_token: None,
255 }
256 }
257
258 pub fn with_auth_token(mut self, token: impl Into<String>) -> Self {
267 self.auth_token = Some(token.into());
268 self
269 }
270}
271
272impl From<RelayUrl> for RelayConfig {
273 fn from(value: RelayUrl) -> Self {
274 Self {
275 url: value,
276 quic: quic_config(),
277 auth_token: None,
278 }
279 }
280}
281
282fn quic_config() -> Option<RelayQuicConfig> {
283 Some(RelayQuicConfig::default())
284}
285
286#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq, PartialOrd, Ord)]
291#[non_exhaustive]
292pub struct RelayQuicConfig {
293 pub port: u16,
295}
296
297impl RelayQuicConfig {
298 pub fn new(port: u16) -> Self {
300 Self { port }
301 }
302}
303
304impl Default for RelayQuicConfig {
305 fn default() -> Self {
306 Self {
307 port: DEFAULT_RELAY_QUIC_PORT,
308 }
309 }
310}
311
312impl fmt::Display for RelayConfig {
313 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
314 write!(f, "{}", self.url)
315 }
316}
317
318#[cfg(test)]
319mod tests {
320 use std::str::FromStr;
321
322 use super::*;
323
324 #[test]
325 fn relay_map_extend() {
326 let urls1 = vec![
327 RelayUrl::from_str("https://hello-a-01.com").unwrap(),
328 RelayUrl::from_str("https://hello-b-01.com").unwrap(),
329 RelayUrl::from_str("https://hello-c-01-.com").unwrap(),
330 ];
331
332 let urls2 = vec![
333 RelayUrl::from_str("https://hello-a-02.com").unwrap(),
334 RelayUrl::from_str("https://hello-b-02.com").unwrap(),
335 RelayUrl::from_str("https://hello-c-02-.com").unwrap(),
336 ];
337
338 let map1 = RelayMap::from_iter(urls1.clone().into_iter().map(RelayConfig::from));
339 let map2 = RelayMap::from_iter(urls2.clone().into_iter().map(RelayConfig::from));
340
341 assert_ne!(map1, map2);
342
343 let map3 = RelayMap::from_iter(
346 map1.relays::<Vec<_>>()
347 .into_iter()
348 .chain(map2.relays::<Vec<_>>()),
349 );
350
351 assert_eq!(map3.len(), 6);
352
353 map1.extend(&map2);
354 assert_eq!(map3, map1);
355 }
356}