use desync::*;
use std::fmt;
use std::ops::*;
use std::sync::*;
use std::fmt::Debug;
use std::collections::*;
struct ResourceManagerCore<T: Send+Sync> {
resources: Vec<WeakResource<T>>,
name_for_id: Vec<Arc<Mutex<Option<String>>>>,
free_slots: Vec<usize>,
clean_size: usize,
named_resources: HashMap<String, Resource<T>>
}
pub struct ResourceManager<T: Send+Sync> {
core: Desync<ResourceManagerCore<T>>,
}
struct WeakResource<T> {
resource: Weak<T>
}
pub struct Resource<T> {
id: u32,
name: Arc<Mutex<Option<String>>>,
resource: Arc<T>
}
impl<T> Clone for Resource<T> {
fn clone(&self) -> Resource<T> {
Resource {
id: self.id,
resource: self.resource.clone(),
name: self.name.clone()
}
}
}
impl<T> Resource<T> {
pub fn id(&self) -> u32 {
self.id
}
pub fn name(&self) -> Option<String> {
let name = self.name.lock().unwrap();
if let Some(ref name) = *name {
Some(name.clone())
} else {
None
}
}
pub fn get_arc(&self) -> Arc<T> {
self.resource.clone()
}
}
impl<T> Deref for Resource<T> {
type Target = T;
fn deref(&self) -> &T {
&*self.resource
}
}
impl<T> PartialEq for Resource<T> {
fn eq(&self, other: &Resource<T>) -> bool {
other.id == self.id
}
}
impl<T> Debug for Resource<T> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
let name = self.name.lock().unwrap().clone();
if let Some(name) = name {
fmt.write_fmt(format_args!("{} \"{}\"", self.id, name))
} else {
fmt.write_fmt(format_args!("{}", self.id))
}
}
}
impl<T: Send+Sync> ResourceManagerCore<T> {
fn register(&mut self, resource: T) -> Resource<T> {
let id = self.free_slots.pop().unwrap_or(self.resources.len());
let resource = Resource {
id: id as u32,
resource: Arc::new(resource),
name: self.set_name_for_id(id as u32, None)
};
let weak = WeakResource { resource: Arc::downgrade(&resource.resource) };
if id >= self.resources.len() {
self.resources.push(weak);
} else {
self.resources[id] = weak;
}
resource
}
fn set_name_for_id(&mut self, id: u32, name: Option<String>) -> Arc<Mutex<Option<String>>> {
while self.name_for_id.len() <= id as usize {
self.name_for_id.push(Arc::new(Mutex::new(None)))
}
let name_slot = &self.name_for_id[id as usize];
*name_slot.lock().unwrap() = name;
name_slot.clone()
}
fn get_name_for_id(&mut self, id: u32) -> Arc<Mutex<Option<String>>> {
if (id as usize) < self.name_for_id.len() as usize {
self.name_for_id[id as usize].clone()
} else {
self.set_name_for_id(id, None)
}
}
fn clean_resources(&mut self) {
let mut new_free_slots = vec![];
for resource_id in 0..self.resources.len() {
if self.resources[resource_id].resource.upgrade().is_none() {
new_free_slots.push(resource_id);
}
}
self.free_slots = new_free_slots;
self.clean_size = self.resources.len() - self.free_slots.len() + 16;
}
}
impl<T: 'static+Send+Sync> ResourceManager<T> {
pub fn new() -> ResourceManager<T> {
ResourceManager {
core: Desync::new(ResourceManagerCore {
resources: vec![],
named_resources: HashMap::new(),
name_for_id: vec![],
free_slots: vec![],
clean_size: 1
})
}
}
pub fn register(&self, data: T) -> Resource<T> {
self.core.sync(move |core| {
let resource = core.register(data);
if core.resources.len() >= core.clean_size {
self.core.async(|core| core.clean_resources());
}
resource
})
}
pub fn get_resource_with_id(&self, id: u32) -> Option<Resource<T>> {
self.core.sync(move |core| {
if id as usize >= core.resources.len() {
None
} else {
let name = core.get_name_for_id(id);
let weak = &core.resources[id as usize];
if let Some(data_ref) = weak.resource.upgrade() {
Some(Resource {
id: id,
resource: data_ref,
name: name
})
} else {
None
}
}
})
}
pub fn assign_name(&self, resource: &Resource<T>, name: &str) {
let to_name = resource.clone();
let name_string = String::from(name);
self.core.async(move |core| {
{
let previous_owner = core.named_resources.get(&name_string);
if let Some(previous_owner) = previous_owner {
if previous_owner.id != to_name.id {
let mut previous_name = previous_owner.name.lock().unwrap();
if *previous_name == Some(name_string.clone()) {
*previous_name = None;
}
}
}
}
core.named_resources.insert(name_string, to_name);
});
*resource.name.lock().unwrap() = Some(String::from(name));
}
pub fn get_name(&self, resource: &Resource<T>) -> Option<String> {
self.core.sync(move |_core| {
resource.name()
})
}
pub fn get_named_resource(&self, name: &str) -> Option<Resource<T>> {
self.core.sync(move |core| {
core.named_resources
.get(&String::from(name))
.map(|res| res.clone())
})
}
}
#[cfg(test)]
mod test {
use super::*;
use std::mem;
#[test]
fn can_create_resource() {
let resource_manager = ResourceManager::new();
let resource = resource_manager.register(2);
assert!(*resource == 2);
}
#[test]
fn resources_start_with_no_name() {
let resource_manager = ResourceManager::new();
let resource = resource_manager.register(2);
assert!(resource.name() == None);
}
#[test]
fn can_retrieve_resource_by_id() {
let resource_manager = ResourceManager::new();
let resource = resource_manager.register(2);
let id = resource.id();
assert!(resource_manager.get_resource_with_id(id).map(|x| *x) == Some(2));
}
#[test]
fn bad_ids_dont_exist() {
let resource_manager = ResourceManager::new();
let resource = resource_manager.register(2);
let id = resource.id()+1;
assert!(resource_manager.get_resource_with_id(id).map(|x| *x) == None);
}
#[test]
fn resources_expire_when_released() {
let resource_manager = ResourceManager::new();
let resource = resource_manager.register(2);
let id = resource.id();
mem::drop(resource);
assert!(resource_manager.get_resource_with_id(id).map(|x| *x) == None);
}
#[test]
fn can_retrieve_resource_by_name() {
let resource_manager = ResourceManager::new();
let resource = resource_manager.register(2);
resource_manager.assign_name(&resource, "Mr Resource");
assert!(resource_manager.get_named_resource("Mr Resource").map(|x| *x) == Some(2));
}
#[test]
fn can_replace_named_resources() {
let resource_manager = ResourceManager::new();
let resource = resource_manager.register(2);
let resource2 = resource_manager.register(3);
resource_manager.assign_name(&resource, "Mr Resource");
resource_manager.assign_name(&resource2, "Mr Resource");
assert!(resource_manager.get_named_resource("Mr Resource").map(|x| *x) == Some(3));
}
#[test]
fn can_get_name_from_resource() {
let resource_manager = ResourceManager::new();
let resource = resource_manager.register(2);
resource_manager.assign_name(&resource, "Mr Resource");
assert!(resource.name() == Some(String::from("Mr Resource")));
}
#[test]
fn can_get_name_after_retrieving_by_id() {
let resource_manager = ResourceManager::new();
let resource = resource_manager.register(2);
resource_manager.assign_name(&resource, "Mr Resource");
let id = resource.id();
assert!(resource_manager.get_resource_with_id(id).map(|x| x.name()).unwrap() == Some(String::from("Mr Resource")));
}
#[test]
fn name_expires_when_replaced() {
let resource_manager = ResourceManager::new();
let resource = resource_manager.register(2);
let resource2 = resource_manager.register(3);
resource_manager.assign_name(&resource, "Mr Resource");
resource_manager.assign_name(&resource2, "Mr Resource");
assert!(resource_manager.get_name(&resource) == None);
assert!(resource_manager.get_name(&resource2) == Some(String::from("Mr Resource")));
}
#[test]
fn named_resources_do_not_expire() {
let resource_manager = ResourceManager::new();
let resource = resource_manager.register(2);
resource_manager.assign_name(&resource, "Mr Resource");
mem::drop(resource);
assert!(resource_manager.get_named_resource("Mr Resource").map(|x| *x) == Some(2));
}
#[test]
fn can_manage_many_resources() {
let resource_manager = ResourceManager::new();
let mut resource = vec![];
for i in 0..100 {
resource.push(resource_manager.register(i));
}
for i in 0..100 {
assert!(resource_manager.get_resource_with_id(resource[i].id()).map(|x| *x) == Some(i));
}
}
}