use std::fmt;
use libp2p_core::Multiaddr;
use smallvec::SmallVec;
#[derive(Clone)]
pub struct Addresses {
addrs: SmallVec<[Multiaddr; 6]>,
}
#[allow(clippy::len_without_is_empty)]
impl Addresses {
pub fn new(addr: Multiaddr) -> Addresses {
let mut addrs = SmallVec::new();
addrs.push(addr);
Addresses { addrs }
}
pub fn first(&self) -> &Multiaddr {
&self.addrs[0]
}
pub fn iter(&self) -> impl Iterator<Item = &Multiaddr> {
self.addrs.iter()
}
pub fn len(&self) -> usize {
self.addrs.len()
}
pub fn into_vec(self) -> Vec<Multiaddr> {
self.addrs.into_vec()
}
#[allow(clippy::result_unit_err)]
pub fn remove(&mut self, addr: &Multiaddr) -> Result<(), ()> {
if self.addrs.len() == 1 && self.addrs[0] == *addr {
return Err(());
}
if let Some(pos) = self.addrs.iter().position(|a| a == addr) {
self.addrs.remove(pos);
if self.addrs.len() <= self.addrs.inline_size() {
self.addrs.shrink_to_fit();
}
}
Ok(())
}
pub fn insert(&mut self, addr: Multiaddr) -> bool {
if self.addrs.iter().all(|a| *a != addr) {
self.addrs.push(addr);
true
} else {
false
}
}
pub fn replace(&mut self, old: &Multiaddr, new: &Multiaddr) -> bool {
if let Some(a) = self.addrs.iter_mut().find(|a| *a == old) {
*a = new.clone();
return true;
}
false
}
}
impl fmt::Debug for Addresses {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_list().entries(self.addrs.iter()).finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn given_one_address_when_removing_different_one_returns_ok() {
let mut addresses = make_addresses([tcp_addr(1234)]);
let result = addresses.remove(&tcp_addr(4321));
assert!(result.is_ok());
assert_eq!(
addresses.into_vec(),
vec![tcp_addr(1234)],
"`Addresses` to not change because we tried to remove a non-present address"
);
}
#[test]
fn given_one_address_when_removing_correct_one_returns_err() {
let mut addresses = make_addresses([tcp_addr(1234)]);
let result = addresses.remove(&tcp_addr(1234));
assert!(result.is_err());
assert_eq!(
addresses.into_vec(),
vec![tcp_addr(1234)],
"`Addresses` to not be empty because it would have been the last address to be removed"
);
}
#[test]
fn given_many_addresses_when_removing_different_one_does_not_remove_and_returns_ok() {
let mut addresses = make_addresses([tcp_addr(1234), tcp_addr(4321)]);
let result = addresses.remove(&tcp_addr(5678));
assert!(result.is_ok());
assert_eq!(
addresses.into_vec(),
vec![tcp_addr(1234), tcp_addr(4321)],
"`Addresses` to not change because we tried to remove a non-present address"
);
}
#[test]
fn given_many_addresses_when_removing_correct_one_removes_and_returns_ok() {
let mut addresses = make_addresses([tcp_addr(1234), tcp_addr(4321)]);
let result = addresses.remove(&tcp_addr(1234));
assert!(result.is_ok());
assert_eq!(
addresses.into_vec(),
vec![tcp_addr(4321)],
"`Addresses to no longer contain address was present and then removed`"
);
}
fn make_addresses(addresses: impl IntoIterator<Item = Multiaddr>) -> Addresses {
Addresses {
addrs: SmallVec::from_iter(addresses),
}
}
fn tcp_addr(port: u16) -> Multiaddr {
format!("/ip4/127.0.0.1/tcp/{port}").parse().unwrap()
}
}