use std::net::SocketAddr;
const MAX_DIRECT_ADDRESSES: usize = 16;
#[derive(Debug, Default)]
pub(crate) struct ExternalAddresses {
direct: Vec<SocketAddr>,
relay: Option<SocketAddr>,
}
impl ExternalAddresses {
pub(crate) fn new() -> Self {
Self::default()
}
pub(crate) fn pin_direct(&mut self, addr: SocketAddr) {
if self.direct.contains(&addr) {
return;
}
if self.direct.len() >= MAX_DIRECT_ADDRESSES {
self.direct.remove(0);
}
self.direct.push(addr);
}
pub(crate) fn set_relay(&mut self, addr: SocketAddr) {
self.relay = Some(addr);
}
pub(crate) fn clear_relay(&mut self) {
self.relay = None;
}
pub(crate) fn all_addresses(&self) -> Vec<SocketAddr> {
let mut out = Vec::with_capacity(self.direct.len() + 1);
if let Some(relay) = self.relay {
out.push(relay);
}
for &addr in &self.direct {
if Some(addr) != self.relay {
out.push(addr);
}
}
out
}
pub(crate) fn direct_addresses(&self) -> Vec<SocketAddr> {
self.direct.clone()
}
#[cfg(test)]
pub(crate) fn relay_address(&self) -> Option<SocketAddr> {
self.relay
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::net::{IpAddr, Ipv4Addr};
fn addr(last_octet: u8, port: u16) -> SocketAddr {
SocketAddr::new(IpAddr::V4(Ipv4Addr::new(192, 0, 2, last_octet)), port)
}
#[test]
fn empty_returns_nothing() {
let ext = ExternalAddresses::new();
assert!(ext.all_addresses().is_empty());
assert!(ext.direct_addresses().is_empty());
assert!(ext.relay_address().is_none());
}
#[test]
fn pin_direct_dedup() {
let mut ext = ExternalAddresses::new();
let a = addr(1, 9000);
ext.pin_direct(a);
ext.pin_direct(a);
ext.pin_direct(a);
assert_eq!(ext.direct_addresses(), vec![a]);
assert_eq!(ext.all_addresses(), vec![a]);
}
#[test]
fn all_addresses_relay_first() {
let mut ext = ExternalAddresses::new();
let direct = addr(1, 9000);
let relay = addr(2, 9000);
ext.pin_direct(direct);
ext.set_relay(relay);
assert_eq!(ext.all_addresses(), vec![relay, direct]);
assert_eq!(ext.relay_address(), Some(relay));
}
#[test]
fn clear_relay_removes_from_all() {
let mut ext = ExternalAddresses::new();
let direct = addr(1, 9000);
let relay = addr(2, 9000);
ext.pin_direct(direct);
ext.set_relay(relay);
ext.clear_relay();
assert_eq!(ext.all_addresses(), vec![direct]);
assert!(ext.relay_address().is_none());
}
#[test]
fn relay_not_duplicated_when_also_pinned() {
let mut ext = ExternalAddresses::new();
let same = addr(1, 9000);
ext.pin_direct(same);
ext.set_relay(same);
assert_eq!(ext.all_addresses(), vec![same]);
}
#[test]
fn multiple_direct_addresses_preserved() {
let mut ext = ExternalAddresses::new();
let a = addr(1, 9000);
let b = addr(2, 9000);
ext.pin_direct(a);
ext.pin_direct(b);
assert_eq!(ext.direct_addresses(), vec![a, b]);
assert_eq!(ext.all_addresses(), vec![a, b]);
}
#[test]
fn direct_addresses_excludes_relay() {
let mut ext = ExternalAddresses::new();
let direct = addr(1, 9000);
let relay = addr(2, 9000);
ext.pin_direct(direct);
ext.set_relay(relay);
assert_eq!(ext.direct_addresses(), vec![direct]);
}
#[test]
fn pin_direct_evicts_oldest_at_capacity() {
let mut ext = ExternalAddresses::new();
for i in 0..MAX_DIRECT_ADDRESSES as u8 {
ext.pin_direct(addr(i, 9000));
}
assert_eq!(ext.direct_addresses().len(), MAX_DIRECT_ADDRESSES);
let new = addr(MAX_DIRECT_ADDRESSES as u8, 9000);
ext.pin_direct(new);
assert_eq!(ext.direct_addresses().len(), MAX_DIRECT_ADDRESSES);
assert!(!ext.direct_addresses().contains(&addr(0, 9000)));
assert_eq!(*ext.direct_addresses().last().unwrap(), new);
}
}