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
149impl FromIterator<RelayConfig> for RelayMap {
150 fn from_iter<T: IntoIterator<Item = RelayConfig>>(iter: T) -> Self {
151 Self::from_iter(iter.into_iter().map(Arc::new))
152 }
153}
154
155impl FromIterator<Arc<RelayConfig>> for RelayMap {
156 fn from_iter<T: IntoIterator<Item = Arc<RelayConfig>>>(iter: T) -> Self {
157 Self {
158 relays: Arc::new(RwLock::new(
159 iter.into_iter()
160 .map(|config| (config.url.clone(), config))
161 .collect(),
162 )),
163 }
164 }
165}
166
167impl From<RelayUrl> for RelayMap {
168 fn from(value: RelayUrl) -> Self {
173 Self {
174 relays: Arc::new(RwLock::new(
175 [(value.clone(), Arc::new(value.into()))].into(),
176 )),
177 }
178 }
179}
180
181impl From<RelayConfig> for RelayMap {
182 fn from(value: RelayConfig) -> Self {
183 Self {
184 relays: Arc::new(RwLock::new([(value.url.clone(), Arc::new(value))].into())),
185 }
186 }
187}
188
189impl FromIterator<RelayUrl> for RelayMap {
190 fn from_iter<T: IntoIterator<Item = RelayUrl>>(iter: T) -> Self {
195 Self {
196 relays: Arc::new(RwLock::new(
197 iter.into_iter()
198 .map(|url| (url.clone(), Arc::new(url.into())))
199 .collect(),
200 )),
201 }
202 }
203}
204
205impl fmt::Display for RelayMap {
206 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
207 fmt::Debug::fmt(&self, f)
208 }
209}
210
211#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, PartialOrd, Ord)]
217pub struct RelayConfig {
218 pub url: RelayUrl,
220 #[serde(default = "quic_config")]
225 pub quic: Option<RelayQuicConfig>,
226}
227
228impl From<RelayUrl> for RelayConfig {
229 fn from(value: RelayUrl) -> Self {
230 Self {
231 url: value,
232 quic: quic_config(),
233 }
234 }
235}
236
237fn quic_config() -> Option<RelayQuicConfig> {
238 Some(RelayQuicConfig::default())
239}
240
241#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq, PartialOrd, Ord)]
246pub struct RelayQuicConfig {
247 pub port: u16,
249}
250
251impl Default for RelayQuicConfig {
252 fn default() -> Self {
253 Self {
254 port: DEFAULT_RELAY_QUIC_PORT,
255 }
256 }
257}
258
259impl fmt::Display for RelayConfig {
260 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
261 write!(f, "{}", self.url)
262 }
263}
264
265#[cfg(test)]
266mod tests {
267 use std::str::FromStr;
268
269 use super::*;
270
271 #[test]
272 fn relay_map_extend() {
273 let urls1 = vec![
274 RelayUrl::from_str("https://hello-a-01.com").unwrap(),
275 RelayUrl::from_str("https://hello-b-01.com").unwrap(),
276 RelayUrl::from_str("https://hello-c-01-.com").unwrap(),
277 ];
278
279 let urls2 = vec![
280 RelayUrl::from_str("https://hello-a-02.com").unwrap(),
281 RelayUrl::from_str("https://hello-b-02.com").unwrap(),
282 RelayUrl::from_str("https://hello-c-02-.com").unwrap(),
283 ];
284
285 let map1 = RelayMap::from_iter(urls1.clone().into_iter().map(RelayConfig::from));
286 let map2 = RelayMap::from_iter(urls2.clone().into_iter().map(RelayConfig::from));
287
288 assert_ne!(map1, map2);
289
290 let map3 = RelayMap::from_iter(
293 map1.relays::<Vec<_>>()
294 .into_iter()
295 .chain(map2.relays::<Vec<_>>()),
296 );
297
298 assert_eq!(map3.len(), 6);
299
300 map1.extend(&map2);
301 assert_eq!(map3, map1);
302 }
303}