use std::sync::{Arc, RwLock};
use sim_kernel::{Cx, Error, Expr, Object, ObjectCompat, Result, Value};
use crate::standard_mutate_capability;
#[sim_citizen_derive::non_citizen(
reason = "mutable vector handle; reconstruct from vector entries plus mutation policy",
kind = "handle",
descriptor = "core/Expr"
)]
pub struct MutableVector {
items: RwLock<Vec<Value>>,
}
impl MutableVector {
pub fn new(items: Vec<Value>) -> Self {
Self {
items: RwLock::new(items),
}
}
pub fn len(&self) -> Result<usize> {
Ok(self
.items
.read()
.map_err(|_| Error::PoisonedLock("mutation vector"))?
.len())
}
pub fn is_empty(&self) -> Result<bool> {
Ok(self.len()? == 0)
}
pub fn get(&self, index: usize) -> Result<Option<Value>> {
Ok(self
.items
.read()
.map_err(|_| Error::PoisonedLock("mutation vector"))?
.get(index)
.cloned())
}
pub fn set(&self, cx: &mut Cx, index: usize, value: Value) -> Result<()> {
cx.require(&standard_mutate_capability())?;
let mut items = self
.items
.write()
.map_err(|_| Error::PoisonedLock("mutation vector"))?;
let Some(slot) = items.get_mut(index) else {
return Err(Error::Eval(format!(
"mutable vector index {index} out of bounds"
)));
};
*slot = value;
Ok(())
}
pub fn push(&self, cx: &mut Cx, value: Value) -> Result<()> {
cx.require(&standard_mutate_capability())?;
self.items
.write()
.map_err(|_| Error::PoisonedLock("mutation vector"))?
.push(value);
Ok(())
}
pub fn to_vec(&self) -> Result<Vec<Value>> {
Ok(self
.items
.read()
.map_err(|_| Error::PoisonedLock("mutation vector"))?
.clone())
}
}
impl Object for MutableVector {
fn display(&self, _cx: &mut Cx) -> Result<String> {
Ok(format!("#<mutation-vector {}>", self.len()?))
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
impl ObjectCompat for MutableVector {
fn as_expr(&self, cx: &mut Cx) -> Result<Expr> {
Ok(Expr::Vector(
self.to_vec()?
.iter()
.map(|value| value.object().as_expr(cx))
.collect::<Result<Vec<_>>>()?,
))
}
}
pub fn mutable_vector(cx: &mut Cx, items: Vec<Value>) -> Result<Value> {
cx.factory().opaque(Arc::new(MutableVector::new(items)))
}
pub fn mutable_vector_from_value(cx: &mut Cx, value: &Value) -> Result<Value> {
let expr = value.object().as_expr(cx)?;
let Expr::Vector(items) = expr else {
return Err(Error::TypeMismatch {
expected: "vector expression",
found: "non-vector",
});
};
let mut values = Vec::with_capacity(items.len());
for item in items {
values.push(cx.factory().expr(item)?);
}
mutable_vector(cx, values)
}
pub fn mutable_vector_value(value: &Value) -> Result<&MutableVector> {
value
.object()
.downcast_ref::<MutableVector>()
.ok_or(Error::TypeMismatch {
expected: "mutable vector",
found: "non-vector",
})
}