use std::{
collections::BTreeMap,
fmt,
sync::{Arc, RwLock},
};
use iroh_base::{RelayUrl, RelayUrlParseError};
use serde::{Deserialize, Serialize};
use crate::defaults::DEFAULT_RELAY_QUIC_PORT;
#[derive(Debug, Clone)]
pub struct RelayMap {
relays: Arc<RwLock<BTreeMap<RelayUrl, Arc<RelayConfig>>>>,
}
impl PartialEq for RelayMap {
fn eq(&self, other: &Self) -> bool {
let this = self.relays.read().expect("poisoned");
let that = other.relays.read().expect("poisoned");
this.eq(&*that)
}
}
impl Eq for RelayMap {}
impl RelayMap {
pub fn empty() -> Self {
Self {
relays: Default::default(),
}
}
pub fn try_from_iter<'a, T: IntoIterator<Item = &'a str>>(
urls: T,
) -> Result<Self, RelayUrlParseError> {
let relays: BTreeMap<RelayUrl, Arc<RelayConfig>> = urls
.into_iter()
.map(|t| {
t.parse()
.map(|url: RelayUrl| (url.clone(), Arc::new(RelayConfig::from(url))))
})
.collect::<Result<_, _>>()?;
Ok(Self {
relays: Arc::new(RwLock::new(relays)),
})
}
pub fn urls<T>(&self) -> T
where
T: FromIterator<RelayUrl>,
{
self.relays
.read()
.expect("poisoned")
.keys()
.cloned()
.collect::<T>()
}
pub fn relays<T>(&self) -> T
where
T: FromIterator<Arc<RelayConfig>>,
{
self.relays
.read()
.expect("poisoned")
.values()
.cloned()
.collect::<T>()
}
pub fn contains(&self, url: &RelayUrl) -> bool {
self.relays.read().expect("poisoned").contains_key(url)
}
pub fn get(&self, url: &RelayUrl) -> Option<Arc<RelayConfig>> {
self.relays.read().expect("poisoned").get(url).cloned()
}
pub fn len(&self) -> usize {
self.relays.read().expect("poisoned").len()
}
pub fn is_empty(&self) -> bool {
self.relays.read().expect("poisoned").is_empty()
}
pub fn insert(&self, url: RelayUrl, endpoint: Arc<RelayConfig>) -> Option<Arc<RelayConfig>> {
self.relays.write().expect("poisoned").insert(url, endpoint)
}
pub fn remove(&self, url: &RelayUrl) -> Option<Arc<RelayConfig>> {
self.relays.write().expect("poisoned").remove(url)
}
pub fn extend(&self, other: &RelayMap) {
let mut a = self.relays.write().expect("poisoned");
let b = other.relays.read().expect("poisoned");
a.extend(b.iter().map(|(a, b)| (a.clone(), b.clone())));
}
}
impl FromIterator<RelayConfig> for RelayMap {
fn from_iter<T: IntoIterator<Item = RelayConfig>>(iter: T) -> Self {
Self::from_iter(iter.into_iter().map(Arc::new))
}
}
impl FromIterator<Arc<RelayConfig>> for RelayMap {
fn from_iter<T: IntoIterator<Item = Arc<RelayConfig>>>(iter: T) -> Self {
Self {
relays: Arc::new(RwLock::new(
iter.into_iter()
.map(|config| (config.url.clone(), config))
.collect(),
)),
}
}
}
impl From<RelayUrl> for RelayMap {
fn from(value: RelayUrl) -> Self {
Self {
relays: Arc::new(RwLock::new(
[(value.clone(), Arc::new(value.into()))].into(),
)),
}
}
}
impl From<RelayConfig> for RelayMap {
fn from(value: RelayConfig) -> Self {
Self {
relays: Arc::new(RwLock::new([(value.url.clone(), Arc::new(value))].into())),
}
}
}
impl FromIterator<RelayUrl> for RelayMap {
fn from_iter<T: IntoIterator<Item = RelayUrl>>(iter: T) -> Self {
Self {
relays: Arc::new(RwLock::new(
iter.into_iter()
.map(|url| (url.clone(), Arc::new(url.into())))
.collect(),
)),
}
}
}
impl fmt::Display for RelayMap {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self, f)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, PartialOrd, Ord)]
#[non_exhaustive]
pub struct RelayConfig {
pub url: RelayUrl,
#[serde(default = "quic_config")]
pub quic: Option<RelayQuicConfig>,
}
impl RelayConfig {
pub fn new(url: RelayUrl, quic: Option<RelayQuicConfig>) -> Self {
Self { url, quic }
}
}
impl From<RelayUrl> for RelayConfig {
fn from(value: RelayUrl) -> Self {
Self {
url: value,
quic: quic_config(),
}
}
}
fn quic_config() -> Option<RelayQuicConfig> {
Some(RelayQuicConfig::default())
}
#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq, PartialOrd, Ord)]
#[non_exhaustive]
pub struct RelayQuicConfig {
pub port: u16,
}
impl RelayQuicConfig {
pub fn new(port: u16) -> Self {
Self { port }
}
}
impl Default for RelayQuicConfig {
fn default() -> Self {
Self {
port: DEFAULT_RELAY_QUIC_PORT,
}
}
}
impl fmt::Display for RelayConfig {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.url)
}
}
#[cfg(test)]
mod tests {
use std::str::FromStr;
use super::*;
#[test]
fn relay_map_extend() {
let urls1 = vec![
RelayUrl::from_str("https://hello-a-01.com").unwrap(),
RelayUrl::from_str("https://hello-b-01.com").unwrap(),
RelayUrl::from_str("https://hello-c-01-.com").unwrap(),
];
let urls2 = vec![
RelayUrl::from_str("https://hello-a-02.com").unwrap(),
RelayUrl::from_str("https://hello-b-02.com").unwrap(),
RelayUrl::from_str("https://hello-c-02-.com").unwrap(),
];
let map1 = RelayMap::from_iter(urls1.clone().into_iter().map(RelayConfig::from));
let map2 = RelayMap::from_iter(urls2.clone().into_iter().map(RelayConfig::from));
assert_ne!(map1, map2);
let map3 = RelayMap::from_iter(
map1.relays::<Vec<_>>()
.into_iter()
.chain(map2.relays::<Vec<_>>()),
);
assert_eq!(map3.len(), 6);
map1.extend(&map2);
assert_eq!(map3, map1);
}
}