use std::collections::HashMap;
use std::collections::HashSet;
use std::fmt;
pub struct UsedPort {
port_number: u16,
port_service: String,
port_user: String,
}
impl UsedPort {
pub fn new(n: u16, service: &str, user: &str) -> UsedPort {
UsedPort {
port_number: n,
port_service: String::from(service),
port_user: String::from(user),
}
}
pub fn port(&self) -> u16 {
self.port_number
}
pub fn service(&self) -> String {
String::from(self.port_service.as_str())
}
pub fn user(&self) -> String {
String::from(self.port_user.as_str())
}
}
impl fmt::Display for UsedPort {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} {} {}", self.port(), self.service(), self.user())
}
}
type UnusedPort = u16;
pub struct PortPool {
used: HashMap<u16, UsedPort>,
unused: HashSet<UnusedPort>,
}
impl PortPool {
pub fn new(start: u16, n: u16) -> PortPool {
let mut unused = HashSet::new();
for p in start..(start + n) {
unused.insert(p);
}
PortPool {
used: HashMap::new(),
unused: unused,
}
}
fn mark_used(&mut self, port: u16) {
self.unused.remove(&port);
}
fn get_unused(&self) -> u16 {
let pport = self
.unused
.iter()
.next()
.expect("Bug non-empty free port pool iterator failed");
*pport
}
fn in_use(&self, service: &str, user: &str) -> bool {
for (_, value) in self.used.iter() {
if value.port_service == service && value.port_user == user {
return true;
}
}
return false;
}
pub fn allocate(&mut self, service: &str, user: &str) -> Result<UsedPort, String> {
if self.unused.len() == 0 {
return Err(String::from("No free ports available"));
} else {
if self.in_use(service, user) {
return Err(String::from("Duplicate port allocation attempted"));
}
let port = self.get_unused();
self.mark_used(port);
self.used.insert(port, UsedPort::new(port, service, user));
Ok(UsedPort::new(port, service, user))
}
}
pub fn usage(&self) -> Vec<UsedPort> {
let mut result: Vec<UsedPort> = Vec::new();
for (_, value) in &self.used {
let u = UsedPort::new(
value.port(),
value.service().as_str(),
value.user().as_str(),
);
result.push(u);
}
result.sort_by_key(|k| k.port_number);
result
}
pub fn free(&mut self, port: u16) -> Result<u16, String> {
match self.used.remove(&port) {
Some(_) => {
self.unused.insert(port);
Ok(port)
}
None => Err(String::from("Port is not allocated")),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn uport_construct() {
let u = UsedPort::new(100, "Mytest", "Fox");
assert_eq!(u.port_number, 100);
assert_eq!(u.port_service, String::from("Mytest"));
assert_eq!(u.port_user, String::from("Fox"));
}
#[test]
fn uport_port() {
let u = UsedPort::new(100, "Mytest", "Fox");
assert_eq!(100, u.port());
}
#[test]
fn uport_service() {
let u = UsedPort::new(100, "Mytest", "Fox");
assert_eq!(String::from("Mytest"), u.service());
}
#[test]
fn uport_user() {
let u = UsedPort::new(100, "Mytest", "Fox");
assert_eq!(String::from("Fox"), u.user());
}
#[test]
fn portpool_construct() {
let pool = PortPool::new(1000, 10);
assert_eq!(0, pool.used.len());
assert_eq!(10, pool.unused.len());
}
#[test]
fn portpool_allocate_1() {
let mut pool = PortPool::new(1000, 1); let result = pool.allocate("Service", "fox");
assert!(result.is_ok());
let info = result.unwrap();
assert_eq!(1000, info.port_number);
assert_eq!(String::from("Service"), info.port_service);
assert_eq!(String::from("fox"), info.port_user);
}
#[test]
fn portpool_allocate_2() {
let mut pool = PortPool::new(1000, 1);
pool.allocate("Ok", "fox").unwrap();
let result = pool.allocate("Fails", "fox");
assert!(result.is_err());
}
#[test]
fn portpool_allocate_3() {
let mut pool = PortPool::new(1000, 2); pool.allocate("SomeService", "fox").unwrap(); let result = pool.allocate("SomeService", "fox"); assert!(result.is_err());
}
#[test]
fn portpool_allocate_4() {
let mut pool = PortPool::new(1000, 2); pool.allocate("SomeService", "fox").unwrap(); let result = pool.allocate("SomeService", "cerizza"); assert!(result.is_ok());
}
#[test]
fn portpool_allocate_5() {
let mut pool = PortPool::new(1000, 2);
let port1 = pool.allocate("Service_1", "fox").unwrap();
let port2 = pool.allocate("Service_2", "fox").unwrap();
assert_ne!(port1.port_number, port2.port_number);
}
#[test]
fn usage_1() {
let pool = PortPool::new(1000, 2);
assert_eq!(0, pool.usage().len());
}
#[test]
fn usage_2() {
let mut pool = PortPool::new(1000, 2);
let port1 = pool.allocate("Serice_1", "fox").unwrap();
let usage = pool.usage();
assert_eq!(1, usage.len());
assert_eq!(port1.port_number, usage[0].port_number);
assert_eq!(port1.port_service, usage[0].port_service);
assert_eq!(port1.port_user, usage[0].port_user);
}
#[test]
fn usage_3() {
let mut pool = PortPool::new(1000, 2);
let port1 = pool.allocate("service 1", "fox").unwrap();
let port2 = pool.allocate("service_1", "cerizza").unwrap();
let mut allocated = vec![port1, port2];
allocated.sort_by_key(|v| v.port_number);
let used = pool.usage();
assert_eq!(allocated.len(), used.len());
for i in 0..used.len() {
assert_eq!(allocated[i].port_number, used[i].port_number);
assert_eq!(allocated[i].port_service, used[i].port_service);
assert_eq!(allocated[i].port_user, used[i].port_user);
}
}
#[test]
fn free_1() {
let mut pool = PortPool::new(1000, 2);
let result = pool.free(1000);
assert!(result.is_err());
}
#[test]
fn free_2() {
let mut pool = PortPool::new(1000, 1);
let port = pool.allocate("service", "fox").unwrap();
let result = pool.free(port.port_number);
assert!(result.is_ok());
}
#[test]
fn free_3() {
let mut pool = PortPool::new(1000, 1);
let port = pool.allocate("service", "fox").unwrap();
pool.free(port.port_number).unwrap();
assert_eq!(1, pool.unused.len());
assert!(pool.unused.contains(&port.port_number));
}
}