use crate::node::Address;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
#[derive(Clone)]
pub struct Record {
pub address: Address,
pub subscribers: Vec<Address>,
}
#[derive(Clone)]
pub struct RecordBucket(Arc<Mutex<HashMap<Address, Record>>>);
impl Record {
pub fn new(address: Address) -> Self {
Self {
address,
subscribers: Vec::new(),
}
}
pub fn subscribe(&mut self, subscriber: Address) {
if !self.contains(&subscriber) {
self.subscribers.push(subscriber)
}
}
pub fn unsubscribe(&mut self, subscriber: &Address) {
let index = self.subscribers.iter().position(|e| e == subscriber);
match index {
Some(i) => {
self.subscribers.remove(i);
}
None => {}
}
}
pub fn contains(&self, query: &Address) -> bool {
self.subscribers.contains(query)
}
}
impl RecordBucket {
pub fn new() -> Self {
Self(Arc::new(Mutex::new(HashMap::new())))
}
pub fn add(&self, record: Record) {
match self.0.lock() {
Ok(mut records) => {
records.insert(record.address.clone(), record);
}
Err(e) => {
log::warn!(
"unable to lock thread, another thread has encountered an error: {}",
e
);
}
}
}
pub fn remove(&self, address: &Address) {
match self.0.lock() {
Ok(mut records) => {
records.remove(address);
}
Err(e) => {
log::warn!(
"unable to lock thread, another thread has encountered an error: {}",
e
);
}
}
}
pub fn contains(&self, address: &Address) -> bool {
match self.0.lock() {
Ok(records) => records.contains_key(address),
Err(e) => {
log::warn!(
"unable to lock thread, another thread
has encountered an error: {}",
e
);
false
}
}
}
pub fn get(&self, address: &Address) -> Option<Record> {
match self.0.lock() {
Ok(records) => match records.get(address) {
Some(record) => Some(record.clone()),
None => None,
},
Err(e) => {
log::warn!(
"unable to lock thread, another thread has encountered an error: {}",
e
);
None
}
}
}
pub fn subscribe(&self, record: &Address, subscriber: Address) {
match self.0.lock() {
Ok(mut records) => match (*records).get_mut(&record) {
Some(record) => {
(*record).subscribe(subscriber);
}
None => {}
},
Err(e) => {
log::warn!(
"unable to lock thread, another thread has encountered an error: {}",
e
);
}
}
}
pub fn unsubscribe(&self, record: &Address, subscriber: &Address) {
match self.0.lock() {
Ok(mut records) => match (*records).get_mut(&record) {
Some(record) => {
(*record).unsubscribe(subscriber);
}
None => {}
},
Err(e) => {
log::warn!(
"unable to lock thread, another thread has encountered an error: {}",
e
);
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_bucket_empty() {
let bucket = RecordBucket::new();
let query = Address::random();
assert_eq!(bucket.get(&query).is_none(), true);
}
#[test]
fn test_bucket_add() {
let bucket = RecordBucket::new();
let addr = Address::random();
let record = Record::new(addr.clone());
bucket.add(record);
assert_eq!(bucket.contains(&addr), true);
assert_eq!(bucket.contains(&Address::random()), false);
}
#[test]
fn test_bucket_subscribe() {
let bucket = RecordBucket::new();
let record_addr = Address::random();
let record = Record::new(record_addr.clone());
bucket.add(record);
let subscriber = Address::random();
bucket.subscribe(&record_addr, subscriber.clone());
let record = bucket.get(&record_addr);
assert_eq!(record.is_none(), false);
assert_eq!(record.unwrap().contains(&subscriber), true);
}
#[test]
fn test_bucket_unsubscribe() {
let bucket = RecordBucket::new();
let record_addr = Address::random();
let record = Record::new(record_addr.clone());
bucket.add(record);
let subscriber = Address::random();
bucket.subscribe(&record_addr, subscriber.clone());
bucket.unsubscribe(&record_addr, &subscriber);
let record = bucket.get(&record_addr);
assert_eq!(record.unwrap().contains(&subscriber), false);
}
}