#![deny(missing_docs)]
use std::collections::HashMap;
use std::ops::{Deref, DerefMut};
use std::sync::{Arc, Mutex, MutexGuard};
type Id = u32;
struct InnerTracker<C> {
controllers: HashMap<Id, C>,
next_id: Id,
}
pub struct Tracker<C>(Arc<Mutex<InnerTracker<C>>>);
pub struct Tracked<T, C> {
tracker: Tracker<C>,
id: Id,
object: T,
}
pub struct TrackerGuard<'a, C>(MutexGuard<'a, InnerTracker<C>>);
pub struct Iter<'a, C>(std::collections::hash_map::Values<'a, Id, C>);
impl<C> InnerTracker<C> {
fn register(&mut self, controller: C) -> Id {
let id = self.next_id;
self.next_id += 1;
self.controllers.insert(id, controller);
id
}
fn unregister(&mut self, id: Id) {
self.controllers.remove(&id);
}
}
impl<C> Tracker<C> {
pub fn new() -> Tracker<C> {
Tracker(Arc::new(Mutex::new(InnerTracker {
controllers: HashMap::new(),
next_id: 0,
})))
}
pub fn track<T>(&self, object: T, controller: C) -> Tracked<T, C> {
let mut tracker = self.0.lock().unwrap();
let id = tracker.register(controller);
Tracked {
tracker: Tracker(self.0.clone()),
id,
object,
}
}
pub fn track_pair<T>(&self, pair: (T, C)) -> Tracked<T, C> {
self.track(pair.0, pair.1)
}
pub fn lock(&mut self) -> TrackerGuard<C> {
TrackerGuard(self.0.lock().unwrap())
}
pub fn for_each<F: FnMut(&C)>(&mut self, f: F) {
self.lock().iter().for_each(f);
}
}
impl<C> Clone for Tracker<C> {
fn clone(&self) -> Tracker<C> {
Tracker(Arc::clone(&self.0))
}
}
impl<C> Default for Tracker<C> {
fn default() -> Self {
Self::new()
}
}
impl<T, C> Drop for Tracked<T, C> {
fn drop(&mut self) {
let mut tracker = self.tracker.0.lock().unwrap();
tracker.unregister(self.id);
}
}
impl<T, C> Deref for Tracked<T, C> {
type Target = T;
fn deref(&self) -> &T {
&self.object
}
}
impl<T, C> DerefMut for Tracked<T, C> {
fn deref_mut(&mut self) -> &mut T {
&mut self.object
}
}
impl<'a, C> TrackerGuard<'a, C> {
pub fn iter(&'a self) -> Iter<'a, C> {
Iter(self.0.controllers.values())
}
}
impl<'a, C> Iterator for Iter<'a, C> {
type Item = &'a C;
fn next(&mut self) -> Option<Self::Item> {
self.0.next()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test1() {
let mut tracker = Tracker::new();
let obj = tracker.track((), 5);
tracker.lock().iter().for_each(|c| {
assert_eq!(c, &5);
});
assert_eq!(tracker.lock().iter().count(), 1);
drop(obj);
assert_eq!(tracker.lock().iter().count(), 0);
}
#[test]
fn test2() {
let mut tracker = Tracker::new();
let obj1 = tracker.track((), 1);
let obj2 = tracker.track((), 2);
let obj3 = tracker.track((), 3);
assert_eq!(tracker.lock().iter().count(), 3);
let mut sum = 0;
tracker.for_each(|x| {
sum += x;
});
assert_eq!(sum, 6);
drop(obj2);
sum = 0;
tracker.for_each(|x| {
sum += x;
});
assert_eq!(sum, 4);
assert_eq!(tracker.lock().iter().count(), 2);
drop(obj3);
drop(obj1);
sum = 0;
tracker.for_each(|x| {
sum += x;
});
assert_eq!(sum, 0);
}
}