sim_lib_mutation/
table.rs1use std::{
2 collections::BTreeMap,
3 sync::{Arc, RwLock},
4};
5
6use sim_kernel::{
7 Cx, Error, Expr, Object, ObjectCompat, Result, Symbol, Table, Value, object::ClassRef,
8};
9
10use crate::standard_mutate_capability;
11
12#[sim_citizen_derive::non_citizen(
18 reason = "mutable table handle; reconstruct from table entries plus mutation policy",
19 kind = "handle",
20 descriptor = "core/Table"
21)]
22pub struct MutableTable {
23 entries: RwLock<BTreeMap<Symbol, Value>>,
24}
25
26impl MutableTable {
27 pub fn new(entries: Vec<(Symbol, Value)>) -> Self {
29 Self {
30 entries: RwLock::new(entries.into_iter().collect()),
31 }
32 }
33}
34
35impl Object for MutableTable {
36 fn display(&self, cx: &mut Cx) -> Result<String> {
37 Ok(format!("#<mutation-table {}>", self.len(cx)?))
38 }
39
40 fn as_any(&self) -> &dyn std::any::Any {
41 self
42 }
43}
44
45impl ObjectCompat for MutableTable {
46 fn class(&self, cx: &mut Cx) -> Result<ClassRef> {
47 cx.factory().class_stub(
48 sim_kernel::CORE_TABLE_CLASS_ID,
49 Symbol::qualified("core", "Table"),
50 )
51 }
52
53 fn as_expr(&self, cx: &mut Cx) -> Result<Expr> {
54 self.as_table_expr(cx)
55 }
56
57 fn as_table_impl(&self) -> Option<&dyn Table> {
58 Some(self)
59 }
60}
61
62impl Table for MutableTable {
63 fn backend_symbol(&self) -> Symbol {
64 Symbol::qualified("mutation", "table")
65 }
66
67 fn get(&self, cx: &mut Cx, key: Symbol) -> Result<Value> {
68 self.entries
69 .read()
70 .map_err(|_| Error::PoisonedLock("mutation table"))?
71 .get(&key)
72 .cloned()
73 .map_or_else(|| cx.factory().nil(), Ok)
74 }
75
76 fn set(&self, cx: &mut Cx, key: Symbol, value: Value) -> Result<()> {
77 cx.require(&standard_mutate_capability())?;
78 self.entries
79 .write()
80 .map_err(|_| Error::PoisonedLock("mutation table"))?
81 .insert(key, value);
82 Ok(())
83 }
84
85 fn has(&self, _cx: &mut Cx, key: Symbol) -> Result<bool> {
86 Ok(self
87 .entries
88 .read()
89 .map_err(|_| Error::PoisonedLock("mutation table"))?
90 .contains_key(&key))
91 }
92
93 fn del(&self, cx: &mut Cx, key: Symbol) -> Result<Value> {
94 cx.require(&standard_mutate_capability())?;
95 self.entries
96 .write()
97 .map_err(|_| Error::PoisonedLock("mutation table"))?
98 .remove(&key)
99 .map_or_else(|| cx.factory().nil(), Ok)
100 }
101
102 fn keys(&self, _cx: &mut Cx) -> Result<Vec<Symbol>> {
103 Ok(self
104 .entries
105 .read()
106 .map_err(|_| Error::PoisonedLock("mutation table"))?
107 .keys()
108 .cloned()
109 .collect())
110 }
111
112 fn entries(&self, _cx: &mut Cx) -> Result<Vec<(Symbol, Value)>> {
113 Ok(self
114 .entries
115 .read()
116 .map_err(|_| Error::PoisonedLock("mutation table"))?
117 .iter()
118 .map(|(key, value)| (key.clone(), value.clone()))
119 .collect())
120 }
121
122 fn len(&self, _cx: &mut Cx) -> Result<usize> {
123 Ok(self
124 .entries
125 .read()
126 .map_err(|_| Error::PoisonedLock("mutation table"))?
127 .len())
128 }
129
130 fn clear(&self, cx: &mut Cx) -> Result<()> {
131 cx.require(&standard_mutate_capability())?;
132 self.entries
133 .write()
134 .map_err(|_| Error::PoisonedLock("mutation table"))?
135 .clear();
136 Ok(())
137 }
138}
139
140pub fn mutable_table(cx: &mut Cx, entries: Vec<(Symbol, Value)>) -> Result<Value> {
142 cx.factory().opaque(Arc::new(MutableTable::new(entries)))
143}
144
145pub fn mutable_table_value(value: &Value) -> Result<&MutableTable> {
147 value
148 .object()
149 .downcast_ref::<MutableTable>()
150 .ok_or(Error::TypeMismatch {
151 expected: "mutable table",
152 found: "non-table",
153 })
154}