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