use std::any::Any;
use crate::{
LispContext, LispError, Value, ValueRef,
clos::{Class, ObjectId},
};
struct ObjectSlot {
generation: u64,
value: Option<Box<dyn Any>>,
}
#[derive(Default)]
pub struct ObjectRegistry {
objects: Vec<ObjectSlot>,
free: Vec<usize>,
}
impl ObjectRegistry {
pub fn insert<T: 'static>(&mut self, value: T) -> ObjectId {
if let Some(index) = self.free.pop() {
let slot = &mut self.objects[index];
slot.value = Some(Box::new(value));
return ObjectId {
index,
generation: slot.generation,
};
}
let index = self.objects.len();
self.objects.push(ObjectSlot {
generation: 0,
value: Some(Box::new(value)),
});
ObjectId {
index,
generation: 0,
}
}
pub fn remove(&mut self, id: ObjectId) -> bool {
let Some(slot) = self.objects.get_mut(id.index) else {
return false;
};
if slot.generation != id.generation || slot.value.is_none() {
return false;
}
slot.value = None;
slot.generation = slot
.generation
.checked_add(1)
.expect("Object generation exhausted");
self.free.push(id.index);
true
}
pub fn get<T: 'static>(&self, id: ObjectId) -> Option<&T> {
let slot = self.objects.get(id.index)?;
if slot.generation != id.generation {
return None;
}
slot.value.as_ref()?.downcast_ref::<T>()
}
pub fn get_mut<T: 'static>(&mut self, id: ObjectId) -> Option<&mut T> {
let slot = self.objects.get_mut(id.index)?;
if slot.generation != id.generation {
return None;
}
slot.value.as_mut()?.downcast_mut::<T>()
}
pub fn read<'rt, T: 'static>(
&self,
id: ObjectId,
class: &Class<T>,
property: &str,
ctx: &LispContext<'rt>,
) -> Result<Option<Value<'rt>>, LispError> {
let obj = self
.get::<T>(id)
.ok_or_else(|| LispError::RuntimeError("Invalid object ID or type".into()))?;
class.read(property, obj, ctx)
}
pub fn write<T: 'static>(
&mut self,
id: ObjectId,
class: &Class<T>,
property: &str,
value: ValueRef<'_>,
) -> Result<bool, LispError> {
let obj = self
.get_mut::<T>(id)
.ok_or_else(|| LispError::RuntimeError("Invalid object ID or type".into()))?;
class.write(property, obj, value)
}
}
#[cfg(test)]
mod tests {
use super::*;
struct Transform {
x: f32,
}
#[test]
fn object_registry() {
let mut registry = ObjectRegistry::default();
let id = registry.insert(Transform { x: 10.0 });
assert_eq!(registry.get::<Transform>(id).unwrap().x, 10.0);
registry.get_mut::<Transform>(id).unwrap().x = 42.0;
assert_eq!(registry.get::<Transform>(id).unwrap().x, 42.0);
assert!(registry.get::<String>(id).is_none());
}
#[test]
fn stale_handles_are_invalid() {
let mut registry = ObjectRegistry::default();
let first = registry.insert(42i32);
assert_eq!(registry.get::<i32>(first), Some(&42));
assert!(registry.remove(first));
assert!(registry.get::<i32>(first).is_none());
let second = registry.insert(100i32);
assert_ne!(first, second);
assert!(registry.get::<i32>(first).is_none());
assert_eq!(registry.get::<i32>(second), Some(&100));
assert!(!registry.remove(first));
}
}