use std::{collections::HashMap, time::Instant};
use dht_rpc::IdBytes;
#[derive(Debug)]
pub struct ConnectionInfo {
pub client: bool,
pub bytes_read: u64,
pub bytes_written: u64,
pub established: Instant,
}
impl ConnectionInfo {
pub fn new(client: bool) -> Self {
Self {
client,
bytes_read: 0,
bytes_written: 0,
established: Instant::now(),
}
}
pub fn bytes_transferred(&self) -> u64 {
self.bytes_read.saturating_add(self.bytes_written)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AddResult {
Added,
KeptExisting,
Replaced,
}
#[derive(Debug, Default)]
pub struct ConnectionSet {
connections: HashMap<IdBytes, ConnectionInfo>,
}
impl ConnectionSet {
pub fn new() -> Self {
Self {
connections: HashMap::new(),
}
}
pub fn has(&self, public_key: &IdBytes) -> bool {
self.connections.contains_key(public_key)
}
pub fn get(&self, public_key: &IdBytes) -> Option<&ConnectionInfo> {
self.connections.get(public_key)
}
pub fn get_mut(&mut self, public_key: &IdBytes) -> Option<&mut ConnectionInfo> {
self.connections.get_mut(public_key)
}
pub fn add(&mut self, public_key: IdBytes, client: bool) -> AddResult {
if let Some(existing) = self.connections.get(&public_key) {
let existing_active = existing.bytes_transferred() > 0;
let new_active = false;
let keep_existing = if existing_active || new_active {
existing_active
} else {
!existing.client
};
if keep_existing {
return AddResult::KeptExisting;
}
self.connections
.insert(public_key, ConnectionInfo::new(client));
AddResult::Replaced
} else {
self.connections
.insert(public_key, ConnectionInfo::new(client));
AddResult::Added
}
}
pub fn remove(&mut self, public_key: &IdBytes) -> Option<ConnectionInfo> {
self.connections.remove(public_key)
}
pub fn len(&self) -> usize {
self.connections.len()
}
pub fn is_empty(&self) -> bool {
self.connections.is_empty()
}
pub fn client_count(&self) -> usize {
self.connections.values().filter(|c| c.client).count()
}
pub fn server_count(&self) -> usize {
self.connections.values().filter(|c| !c.client).count()
}
pub fn keys(&self) -> impl Iterator<Item = &IdBytes> {
self.connections.keys()
}
pub fn iter(&self) -> impl Iterator<Item = (&IdBytes, &ConnectionInfo)> {
self.connections.iter()
}
pub fn iter_mut(&mut self) -> impl Iterator<Item = (&IdBytes, &mut ConnectionInfo)> {
self.connections.iter_mut()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_add_new_connection() {
let mut set = ConnectionSet::new();
let pk = IdBytes::random();
let result = set.add(pk, true);
assert_eq!(result, AddResult::Added);
assert!(set.has(&pk));
assert_eq!(set.len(), 1);
}
#[test]
fn test_duplicate_no_bytes_client_loses() {
let mut set = ConnectionSet::new();
let pk = IdBytes::random();
set.add(pk, false);
assert!(!set.get(&pk).unwrap().client);
let result = set.add(pk, true);
assert_eq!(result, AddResult::KeptExisting);
assert!(!set.get(&pk).unwrap().client); }
#[test]
fn test_duplicate_with_bytes_wins() {
let mut set = ConnectionSet::new();
let pk = IdBytes::random();
set.add(pk, true);
set.get_mut(&pk).unwrap().bytes_read = 100;
let result = set.add(pk, false);
assert_eq!(result, AddResult::KeptExisting);
}
#[test]
fn test_remove() {
let mut set = ConnectionSet::new();
let pk = IdBytes::random();
set.add(pk, true);
assert!(set.has(&pk));
let removed = set.remove(&pk);
assert!(removed.is_some());
assert!(!set.has(&pk));
}
#[test]
fn test_counts() {
let mut set = ConnectionSet::new();
set.add(IdBytes::random(), true); set.add(IdBytes::random(), true); set.add(IdBytes::random(), false);
assert_eq!(set.len(), 3);
assert_eq!(set.client_count(), 2);
assert_eq!(set.server_count(), 1);
}
}