1use std::{
4 collections::BTreeMap,
5 fmt,
6 sync::{Arc, RwLock},
7};
8
9use iroh_base::RelayUrl;
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 urls<T>(&self) -> T
58 where
59 T: FromIterator<RelayUrl>,
60 {
61 self.relays
62 .read()
63 .expect("poisoned")
64 .keys()
65 .cloned()
66 .collect::<T>()
67 }
68
69 pub fn relays<T>(&self) -> T
74 where
75 T: FromIterator<Arc<RelayConfig>>,
76 {
77 self.relays
78 .read()
79 .expect("poisoned")
80 .values()
81 .cloned()
82 .collect::<T>()
83 }
84
85 pub fn contains(&self, url: &RelayUrl) -> bool {
87 self.relays.read().expect("poisoned").contains_key(url)
88 }
89
90 pub fn get(&self, url: &RelayUrl) -> Option<Arc<RelayConfig>> {
92 self.relays.read().expect("poisoned").get(url).cloned()
93 }
94
95 pub fn len(&self) -> usize {
97 self.relays.read().expect("poisoned").len()
98 }
99
100 pub fn is_empty(&self) -> bool {
102 self.relays.read().expect("poisoned").is_empty()
103 }
104
105 pub fn insert(&self, url: RelayUrl, endpoint: Arc<RelayConfig>) -> Option<Arc<RelayConfig>> {
107 self.relays.write().expect("poisoned").insert(url, endpoint)
108 }
109
110 pub fn remove(&self, url: &RelayUrl) -> Option<Arc<RelayConfig>> {
112 self.relays.write().expect("poisoned").remove(url)
113 }
114
115 pub fn extend(&self, other: &RelayMap) {
117 let mut a = self.relays.write().expect("poisoned");
118 let b = other.relays.read().expect("poisoned");
119 a.extend(b.iter().map(|(a, b)| (a.clone(), b.clone())));
120 }
121}
122
123impl FromIterator<RelayConfig> for RelayMap {
124 fn from_iter<T: IntoIterator<Item = RelayConfig>>(iter: T) -> Self {
125 Self::from_iter(iter.into_iter().map(Arc::new))
126 }
127}
128
129impl FromIterator<Arc<RelayConfig>> for RelayMap {
130 fn from_iter<T: IntoIterator<Item = Arc<RelayConfig>>>(iter: T) -> Self {
131 Self {
132 relays: Arc::new(RwLock::new(
133 iter.into_iter()
134 .map(|config| (config.url.clone(), config))
135 .collect(),
136 )),
137 }
138 }
139}
140
141impl From<RelayUrl> for RelayMap {
142 fn from(value: RelayUrl) -> Self {
147 Self {
148 relays: Arc::new(RwLock::new(
149 [(value.clone(), Arc::new(value.into()))].into(),
150 )),
151 }
152 }
153}
154
155impl From<RelayConfig> for RelayMap {
156 fn from(value: RelayConfig) -> Self {
157 Self {
158 relays: Arc::new(RwLock::new([(value.url.clone(), Arc::new(value))].into())),
159 }
160 }
161}
162
163impl FromIterator<RelayUrl> for RelayMap {
164 fn from_iter<T: IntoIterator<Item = RelayUrl>>(iter: T) -> Self {
169 Self {
170 relays: Arc::new(RwLock::new(
171 iter.into_iter()
172 .map(|url| (url.clone(), Arc::new(url.into())))
173 .collect(),
174 )),
175 }
176 }
177}
178
179impl fmt::Display for RelayMap {
180 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
181 fmt::Debug::fmt(&self, f)
182 }
183}
184
185#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, PartialOrd, Ord)]
191pub struct RelayConfig {
192 pub url: RelayUrl,
194 #[serde(default = "quic_config")]
199 pub quic: Option<RelayQuicConfig>,
200}
201
202impl From<RelayUrl> for RelayConfig {
203 fn from(value: RelayUrl) -> Self {
204 Self {
205 url: value,
206 quic: quic_config(),
207 }
208 }
209}
210
211fn quic_config() -> Option<RelayQuicConfig> {
212 Some(RelayQuicConfig::default())
213}
214
215#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq, PartialOrd, Ord)]
218pub struct RelayQuicConfig {
219 pub port: u16,
221}
222
223impl Default for RelayQuicConfig {
224 fn default() -> Self {
225 Self {
226 port: DEFAULT_RELAY_QUIC_PORT,
227 }
228 }
229}
230
231impl fmt::Display for RelayConfig {
232 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
233 write!(f, "{}", self.url)
234 }
235}
236
237#[cfg(test)]
238mod tests {
239 use std::str::FromStr;
240
241 use super::*;
242
243 #[test]
244 fn relay_map_extend() {
245 let urls1 = vec![
246 RelayUrl::from_str("https://hello-a-01.com").unwrap(),
247 RelayUrl::from_str("https://hello-b-01.com").unwrap(),
248 RelayUrl::from_str("https://hello-c-01-.com").unwrap(),
249 ];
250
251 let urls2 = vec![
252 RelayUrl::from_str("https://hello-a-02.com").unwrap(),
253 RelayUrl::from_str("https://hello-b-02.com").unwrap(),
254 RelayUrl::from_str("https://hello-c-02-.com").unwrap(),
255 ];
256
257 let map1 = RelayMap::from_iter(urls1.clone().into_iter().map(RelayConfig::from));
258 let map2 = RelayMap::from_iter(urls2.clone().into_iter().map(RelayConfig::from));
259
260 assert_ne!(map1, map2);
261
262 let map3 = RelayMap::from_iter(
265 map1.relays::<Vec<_>>()
266 .into_iter()
267 .chain(map2.relays::<Vec<_>>()),
268 );
269
270 assert_eq!(map3.len(), 6);
271
272 map1.extend(&map2);
273 assert_eq!(map3, map1);
274 }
275}