use std::{collections::HashMap, net::SocketAddr};
use dht_rpc::IdBytes;
pub const MAX_RECORDS_PER_TOPIC: usize = 20;
#[derive(Debug, Clone)]
pub struct PeerRecord {
pub public_key: [u8; 32],
pub encoded: Vec<u8>,
}
#[derive(Debug, Default)]
pub struct PeerRecordCache {
records: HashMap<IdBytes, Vec<PeerRecord>>,
}
impl PeerRecordCache {
pub fn new() -> Self {
Self::default()
}
pub fn add(&mut self, topic: IdBytes, public_key: [u8; 32], encoded: Vec<u8>) {
let records = self.records.entry(topic).or_default();
if let Some(r) = records.iter_mut().find(|r| r.public_key == public_key) {
r.encoded = encoded;
return;
}
if records.len() >= MAX_RECORDS_PER_TOPIC {
records.remove(0);
}
records.push(PeerRecord {
public_key,
encoded,
});
}
pub fn get(&self, topic: &IdBytes, limit: usize) -> Vec<&PeerRecord> {
self.records
.get(topic)
.map(|records| records.iter().take(limit).collect())
.unwrap_or_default()
}
pub fn remove(&mut self, topic: &IdBytes, public_key: &[u8; 32]) {
if let Some(records) = self.records.get_mut(topic) {
records.retain(|r| &r.public_key != public_key);
if records.is_empty() {
self.records.remove(topic);
}
}
}
}
#[derive(Debug, Clone)]
pub struct RouterEntry {
pub relay: SocketAddr,
pub record: Vec<u8>,
}
impl RouterEntry {
pub fn new(relay: SocketAddr, record: Vec<u8>) -> Self {
Self { relay, record }
}
}
#[derive(Debug, Default)]
pub struct PeerRouter {
entries: HashMap<IdBytes, RouterEntry>,
}
impl PeerRouter {
pub fn new() -> Self {
Self::default()
}
pub fn set(&mut self, target: IdBytes, entry: RouterEntry) {
self.entries.insert(target, entry);
}
pub fn get(&self, target: &IdBytes) -> Option<&RouterEntry> {
self.entries.get(target)
}
pub fn delete(&mut self, target: &IdBytes) {
self.entries.remove(target);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_record_cache_add_and_get() {
let mut cache = PeerRecordCache::new();
let topic = IdBytes::random();
let pubkey = [1u8; 32];
let encoded = b"test record".to_vec();
cache.add(topic, pubkey, encoded.clone());
let records = cache.get(&topic, 20);
assert_eq!(records.len(), 1);
assert_eq!(records[0].public_key, pubkey);
assert_eq!(records[0].encoded, encoded);
}
#[test]
fn test_record_cache_update_existing() {
let mut cache = PeerRecordCache::new();
let topic = IdBytes::random();
let pubkey = [1u8; 32];
cache.add(topic, pubkey, b"first".to_vec());
cache.add(topic, pubkey, b"second".to_vec());
let records = cache.get(&topic, 20);
assert_eq!(records.len(), 1);
assert_eq!(records[0].encoded, b"second");
}
#[test]
fn test_record_cache_limit() {
let mut cache = PeerRecordCache::new();
let topic = IdBytes::random();
for i in 0..=MAX_RECORDS_PER_TOPIC {
let mut pubkey = [0u8; 32];
pubkey[0] = i as u8;
cache.add(topic, pubkey, vec![i as u8]);
}
let records = cache.get(&topic, 100);
assert_eq!(records.len(), MAX_RECORDS_PER_TOPIC);
assert!(records.iter().all(|r| r.public_key[0] != 0));
}
#[test]
fn test_record_cache_remove() {
let mut cache = PeerRecordCache::new();
let topic = IdBytes::random();
let pubkey = [1u8; 32];
cache.add(topic, pubkey, b"test".to_vec());
assert_eq!(cache.get(&topic, 20).len(), 1);
cache.remove(&topic, &pubkey);
assert_eq!(cache.get(&topic, 20).len(), 0);
}
#[test]
fn test_router_basic_operations() {
let mut router = PeerRouter::new();
let target = IdBytes::random();
let relay = "127.0.0.1:8080".parse().unwrap();
let record = b"peer record".to_vec();
router.set(target, RouterEntry::new(relay, record.clone()));
assert!(router.get(&target).is_some());
let entry = router.get(&target).unwrap();
assert_eq!(entry.record, record);
router.delete(&target);
assert!(router.get(&target).is_none());
}
}