sim_lib_mutation/
runtime_table.rs1use std::{
2 collections::BTreeMap,
3 sync::{Arc, RwLock},
4};
5
6use sim_kernel::{Cx, Error, Expr, Object, ObjectCompat, Result, Value};
7
8use crate::{RuntimeKey, RuntimeKeyPolicy, standard_mutate_capability};
9
10#[sim_citizen_derive::non_citizen(
18 reason = "mutable runtime-keyed table handle; reconstruct from entries plus key policy",
19 kind = "handle",
20 descriptor = "core/Expr"
21)]
22pub struct MutableRuntimeTable<P> {
23 policy: P,
24 entries: RwLock<BTreeMap<RuntimeKey, Value>>,
25}
26
27impl<P: RuntimeKeyPolicy> MutableRuntimeTable<P> {
28 pub fn new(policy: P) -> Self {
30 Self {
31 policy,
32 entries: RwLock::new(BTreeMap::new()),
33 }
34 }
35
36 pub fn with_entries(cx: &mut Cx, policy: P, entries: Vec<(Value, Value)>) -> Result<Self> {
41 let table = Self::new(policy);
42 let mut keyed = BTreeMap::new();
43 for (key, value) in entries {
44 keyed.insert(table.key_for_write(cx, &key)?, value);
45 }
46 *table.write_entries()? = keyed;
47 Ok(table)
48 }
49
50 pub fn policy(&self) -> &P {
52 &self.policy
53 }
54
55 pub fn get(&self, cx: &mut Cx, key: &Value) -> Result<Option<Value>> {
57 let Some(key) = self.policy.key_for(cx, key)? else {
58 return Ok(None);
59 };
60 self.get_runtime_key(&key)
61 }
62
63 pub fn get_runtime_key(&self, key: &RuntimeKey) -> Result<Option<Value>> {
65 Ok(self.read_entries()?.get(key).cloned())
66 }
67
68 pub fn set(&self, cx: &mut Cx, key: Value, value: Value) -> Result<()> {
70 let key = self.key_for_write(cx, &key)?;
71 self.set_runtime_key(cx, key, value)
72 }
73
74 pub fn set_runtime_key(&self, cx: &mut Cx, key: RuntimeKey, value: Value) -> Result<()> {
76 cx.require(&standard_mutate_capability())?;
77 self.write_entries()?.insert(key, value);
78 Ok(())
79 }
80
81 pub fn del(&self, cx: &mut Cx, key: &Value) -> Result<Option<Value>> {
83 let Some(key) = self.policy.key_for(cx, key)? else {
84 return Ok(None);
85 };
86 self.del_runtime_key(cx, &key)
87 }
88
89 pub fn del_runtime_key(&self, cx: &mut Cx, key: &RuntimeKey) -> Result<Option<Value>> {
91 cx.require(&standard_mutate_capability())?;
92 Ok(self.write_entries()?.remove(key))
93 }
94
95 pub fn entries_in_key_order(&self) -> Result<Vec<(RuntimeKey, Value)>> {
97 Ok(self
98 .read_entries()?
99 .iter()
100 .map(|(key, value)| (key.clone(), value.clone()))
101 .collect())
102 }
103
104 pub fn len(&self) -> Result<usize> {
106 Ok(self.read_entries()?.len())
107 }
108
109 pub fn is_empty(&self) -> Result<bool> {
111 Ok(self.len()? == 0)
112 }
113
114 pub fn clear(&self, cx: &mut Cx) -> Result<()> {
116 cx.require(&standard_mutate_capability())?;
117 self.write_entries()?.clear();
118 Ok(())
119 }
120
121 fn key_for_write(&self, cx: &mut Cx, key: &Value) -> Result<RuntimeKey> {
122 self.policy
123 .key_for(cx, key)?
124 .ok_or_else(|| Error::Eval("runtime table key is not allowed by policy".to_owned()))
125 }
126
127 fn read_entries(&self) -> Result<std::sync::RwLockReadGuard<'_, BTreeMap<RuntimeKey, Value>>> {
128 self.entries
129 .read()
130 .map_err(|_| Error::PoisonedLock("runtime-keyed mutation table"))
131 }
132
133 fn write_entries(
134 &self,
135 ) -> Result<std::sync::RwLockWriteGuard<'_, BTreeMap<RuntimeKey, Value>>> {
136 self.entries
137 .write()
138 .map_err(|_| Error::PoisonedLock("runtime-keyed mutation table"))
139 }
140}
141
142impl<P: RuntimeKeyPolicy + 'static> Object for MutableRuntimeTable<P> {
143 fn display(&self, _cx: &mut Cx) -> Result<String> {
144 Ok(format!("#<runtime-mutation-table {}>", self.len()?))
145 }
146
147 fn as_any(&self) -> &dyn std::any::Any {
148 self
149 }
150}
151
152impl<P: RuntimeKeyPolicy + 'static> ObjectCompat for MutableRuntimeTable<P> {
153 fn as_expr(&self, cx: &mut Cx) -> Result<Expr> {
154 Ok(Expr::Map(
155 self.entries_in_key_order()?
156 .into_iter()
157 .map(|(key, value)| Ok((key.as_expr(), value.object().as_expr(cx)?)))
158 .collect::<Result<Vec<_>>>()?,
159 ))
160 }
161
162 fn truth(&self, _cx: &mut Cx) -> Result<bool> {
163 Ok(!self.is_empty()?)
164 }
165}
166
167pub fn mutable_runtime_table<P>(
169 cx: &mut Cx,
170 policy: P,
171 entries: Vec<(Value, Value)>,
172) -> Result<Value>
173where
174 P: RuntimeKeyPolicy + 'static,
175{
176 let table = MutableRuntimeTable::with_entries(cx, policy, entries)?;
177 cx.factory().opaque(Arc::new(table))
178}
179
180pub fn mutable_runtime_table_value<P>(value: &Value) -> Result<&MutableRuntimeTable<P>>
182where
183 P: RuntimeKeyPolicy + 'static,
184{
185 value
186 .object()
187 .downcast_ref::<MutableRuntimeTable<P>>()
188 .ok_or(Error::TypeMismatch {
189 expected: "runtime-keyed mutation table",
190 found: "non-table",
191 })
192}