quincy_server/server/
address_pool.rs

1use dashmap::DashSet;
2use ipnet::{IpAddrRange, IpNet, Ipv4AddrRange, Ipv6AddrRange};
3use std::net::IpAddr;
4
5/// Represents a pool of addresses from which addresses can be requested and released.
6pub struct AddressPool {
7    network: IpNet,
8    used_addresses: DashSet<IpAddr>,
9}
10
11impl AddressPool {
12    /// Creates a new instance of an `AddressPool`.
13    ///
14    /// ### Arguments
15    /// - `network` - the network address and mask
16    pub fn new(network: IpNet) -> Self {
17        let pool = Self {
18            network,
19            used_addresses: DashSet::new(),
20        };
21
22        pool.reset();
23
24        pool
25    }
26
27    /// Returns the next available address if such an address exists.
28    pub fn next_available_address(&self) -> Option<IpNet> {
29        let mut range = match self.network {
30            IpNet::V4(network) => {
31                IpAddrRange::V4(Ipv4AddrRange::new(network.network(), network.broadcast()))
32            }
33            IpNet::V6(network) => {
34                IpAddrRange::V6(Ipv6AddrRange::new(network.network(), network.broadcast()))
35            }
36        };
37
38        range
39            .find(|address| !self.used_addresses.contains(address))
40            .map(|address| {
41                self.used_addresses.insert(address);
42                IpNet::with_netmask(address, self.network.netmask())
43                    .expect("Netmask will always be valid")
44            })
45    }
46
47    /// Releases the specified address so it can be used in further requests.
48    ///
49    /// ### Arguments
50    /// - `address` - the address to release
51    pub fn release_address(&self, address: &IpAddr) {
52        self.used_addresses.remove(address);
53    }
54
55    /// Resets the address pool by releasing all addresses.
56    pub fn reset(&self) {
57        self.used_addresses.clear();
58        self.used_addresses.insert(self.network.network());
59        self.used_addresses.insert(self.network.addr());
60        self.used_addresses.insert(self.network.broadcast());
61    }
62}
63
64#[cfg(test)]
65mod tests {
66    use crate::server::address_pool::AddressPool;
67    use ipnet::{IpNet, Ipv4Net};
68    use std::net::{IpAddr, Ipv4Addr};
69
70    #[test]
71    fn test_address_pool() {
72        let pool = AddressPool::new(IpNet::V4(
73            Ipv4Net::with_netmask(
74                Ipv4Addr::new(10, 0, 0, 1),
75                Ipv4Addr::new(255, 255, 255, 252),
76            )
77            .unwrap(),
78        ));
79
80        assert_eq!(
81            pool.next_available_address().unwrap(),
82            IpNet::V4(
83                Ipv4Net::with_netmask(
84                    Ipv4Addr::new(10, 0, 0, 2),
85                    Ipv4Addr::new(255, 255, 255, 252),
86                )
87                .unwrap()
88            )
89        );
90
91        assert_eq!(pool.next_available_address(), None);
92        pool.release_address(&IpAddr::V4(Ipv4Addr::new(10, 0, 0, 2)));
93
94        assert_eq!(
95            pool.next_available_address().unwrap(),
96            IpNet::V4(
97                Ipv4Net::with_netmask(
98                    Ipv4Addr::new(10, 0, 0, 2),
99                    Ipv4Addr::new(255, 255, 255, 252),
100                )
101                .unwrap()
102            )
103        );
104    }
105}