use std::{
collections::BTreeMap,
sync::{Arc, RwLock},
};
use sim_kernel::{
Cx, Error, Expr, Object, ObjectCompat, Result, Symbol, Table, Value, object::ClassRef,
};
use crate::standard_mutate_capability;
#[sim_citizen_derive::non_citizen(
reason = "mutable table handle; reconstruct from table entries plus mutation policy",
kind = "handle",
descriptor = "core/Table"
)]
pub struct MutableTable {
entries: RwLock<BTreeMap<Symbol, Value>>,
}
impl MutableTable {
pub fn new(entries: Vec<(Symbol, Value)>) -> Self {
Self {
entries: RwLock::new(entries.into_iter().collect()),
}
}
}
impl Object for MutableTable {
fn display(&self, cx: &mut Cx) -> Result<String> {
Ok(format!("#<mutation-table {}>", self.len(cx)?))
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
impl ObjectCompat for MutableTable {
fn class(&self, cx: &mut Cx) -> Result<ClassRef> {
cx.factory().class_stub(
sim_kernel::CORE_TABLE_CLASS_ID,
Symbol::qualified("core", "Table"),
)
}
fn as_expr(&self, cx: &mut Cx) -> Result<Expr> {
self.as_table_expr(cx)
}
fn as_table_impl(&self) -> Option<&dyn Table> {
Some(self)
}
}
impl Table for MutableTable {
fn backend_symbol(&self) -> Symbol {
Symbol::qualified("mutation", "table")
}
fn get(&self, cx: &mut Cx, key: Symbol) -> Result<Value> {
self.entries
.read()
.map_err(|_| Error::PoisonedLock("mutation table"))?
.get(&key)
.cloned()
.map_or_else(|| cx.factory().nil(), Ok)
}
fn set(&self, cx: &mut Cx, key: Symbol, value: Value) -> Result<()> {
cx.require(&standard_mutate_capability())?;
self.entries
.write()
.map_err(|_| Error::PoisonedLock("mutation table"))?
.insert(key, value);
Ok(())
}
fn has(&self, _cx: &mut Cx, key: Symbol) -> Result<bool> {
Ok(self
.entries
.read()
.map_err(|_| Error::PoisonedLock("mutation table"))?
.contains_key(&key))
}
fn del(&self, cx: &mut Cx, key: Symbol) -> Result<Value> {
cx.require(&standard_mutate_capability())?;
self.entries
.write()
.map_err(|_| Error::PoisonedLock("mutation table"))?
.remove(&key)
.map_or_else(|| cx.factory().nil(), Ok)
}
fn keys(&self, _cx: &mut Cx) -> Result<Vec<Symbol>> {
Ok(self
.entries
.read()
.map_err(|_| Error::PoisonedLock("mutation table"))?
.keys()
.cloned()
.collect())
}
fn entries(&self, _cx: &mut Cx) -> Result<Vec<(Symbol, Value)>> {
Ok(self
.entries
.read()
.map_err(|_| Error::PoisonedLock("mutation table"))?
.iter()
.map(|(key, value)| (key.clone(), value.clone()))
.collect())
}
fn len(&self, _cx: &mut Cx) -> Result<usize> {
Ok(self
.entries
.read()
.map_err(|_| Error::PoisonedLock("mutation table"))?
.len())
}
fn clear(&self, cx: &mut Cx) -> Result<()> {
cx.require(&standard_mutate_capability())?;
self.entries
.write()
.map_err(|_| Error::PoisonedLock("mutation table"))?
.clear();
Ok(())
}
}
pub fn mutable_table(cx: &mut Cx, entries: Vec<(Symbol, Value)>) -> Result<Value> {
cx.factory().opaque(Arc::new(MutableTable::new(entries)))
}
pub fn mutable_table_value(value: &Value) -> Result<&MutableTable> {
value
.object()
.downcast_ref::<MutableTable>()
.ok_or(Error::TypeMismatch {
expected: "mutable table",
found: "non-table",
})
}