1use std::fmt;
22
23use libp2p_core::Multiaddr;
24use smallvec::SmallVec;
25
26#[derive(Clone)]
29pub struct Addresses {
30 addrs: SmallVec<[Multiaddr; 6]>,
31}
32
33#[allow(clippy::len_without_is_empty)]
34impl Addresses {
35 pub fn new(addr: Multiaddr) -> Addresses {
37 let mut addrs = SmallVec::new();
38 addrs.push(addr);
39 Addresses { addrs }
40 }
41
42 pub fn first(&self) -> &Multiaddr {
44 &self.addrs[0]
45 }
46
47 pub fn iter(&self) -> impl Iterator<Item = &Multiaddr> {
49 self.addrs.iter()
50 }
51
52 pub fn len(&self) -> usize {
54 self.addrs.len()
55 }
56
57 pub fn into_vec(self) -> Vec<Multiaddr> {
59 self.addrs.into_vec()
60 }
61
62 #[allow(clippy::result_unit_err)]
71 pub fn remove(&mut self, addr: &Multiaddr) -> Result<(), ()> {
72 if self.addrs.len() == 1 && self.addrs[0] == *addr {
73 return Err(());
74 }
75
76 if let Some(pos) = self.addrs.iter().position(|a| a == addr) {
77 self.addrs.remove(pos);
78 if self.addrs.len() <= self.addrs.inline_size() {
79 self.addrs.shrink_to_fit();
80 }
81 }
82
83 Ok(())
84 }
85
86 pub fn insert(&mut self, addr: Multiaddr) -> bool {
91 if self.addrs.iter().all(|a| *a != addr) {
92 self.addrs.push(addr);
93 true
94 } else {
95 false
96 }
97 }
98
99 pub fn replace(&mut self, old: &Multiaddr, new: &Multiaddr) -> bool {
104 if let Some(a) = self.addrs.iter_mut().find(|a| *a == old) {
105 *a = new.clone();
106 return true;
107 }
108
109 false
110 }
111}
112
113impl fmt::Debug for Addresses {
114 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
115 f.debug_list().entries(self.addrs.iter()).finish()
116 }
117}
118
119#[cfg(test)]
120mod tests {
121 use super::*;
122
123 #[test]
124 fn given_one_address_when_removing_different_one_returns_ok() {
125 let mut addresses = make_addresses([tcp_addr(1234)]);
126
127 let result = addresses.remove(&tcp_addr(4321));
128
129 assert!(result.is_ok());
130 assert_eq!(
131 addresses.into_vec(),
132 vec![tcp_addr(1234)],
133 "`Addresses` to not change because we tried to remove a non-present address"
134 );
135 }
136
137 #[test]
138 fn given_one_address_when_removing_correct_one_returns_err() {
139 let mut addresses = make_addresses([tcp_addr(1234)]);
140
141 let result = addresses.remove(&tcp_addr(1234));
142
143 assert!(result.is_err());
144 assert_eq!(
145 addresses.into_vec(),
146 vec![tcp_addr(1234)],
147 "`Addresses` to not be empty because it would have been the last address to be removed"
148 );
149 }
150
151 #[test]
152 fn given_many_addresses_when_removing_different_one_does_not_remove_and_returns_ok() {
153 let mut addresses = make_addresses([tcp_addr(1234), tcp_addr(4321)]);
154
155 let result = addresses.remove(&tcp_addr(5678));
156
157 assert!(result.is_ok());
158 assert_eq!(
159 addresses.into_vec(),
160 vec![tcp_addr(1234), tcp_addr(4321)],
161 "`Addresses` to not change because we tried to remove a non-present address"
162 );
163 }
164
165 #[test]
166 fn given_many_addresses_when_removing_correct_one_removes_and_returns_ok() {
167 let mut addresses = make_addresses([tcp_addr(1234), tcp_addr(4321)]);
168
169 let result = addresses.remove(&tcp_addr(1234));
170
171 assert!(result.is_ok());
172 assert_eq!(
173 addresses.into_vec(),
174 vec![tcp_addr(4321)],
175 "`Addresses to no longer contain address was present and then removed`"
176 );
177 }
178
179 fn make_addresses(addresses: impl IntoIterator<Item = Multiaddr>) -> Addresses {
181 Addresses {
182 addrs: SmallVec::from_iter(addresses),
183 }
184 }
185
186 fn tcp_addr(port: u16) -> Multiaddr {
188 format!("/ip4/127.0.0.1/tcp/{port}").parse().unwrap()
189 }
190}