1use std::{
6 collections::HashMap,
7 sync::{Arc, OnceLock, PoisonError, RwLock, RwLockReadGuard, RwLockWriteGuard},
8};
9
10use sim_kernel::{
11 Cx, Error, Expr, Object, ObjectEncode, ObjectEncoding, Result, Symbol, Table, Value,
12 id::CORE_TABLE_CLASS_ID, object::ClassRef,
13};
14
15use crate::citizen::lazy_table_class_symbol;
16
17pub type ValueLoader = Arc<dyn Fn(&mut Cx) -> Result<Value> + Send + Sync>;
19
20struct LazyEntry {
21 loader: ValueLoader,
22 cache: OnceLock<Result<Value>>,
23}
24
25impl LazyEntry {
26 fn eager(value: Value) -> Self {
27 let cache = OnceLock::new();
28 let _ = cache.set(Ok(value.clone()));
29 Self {
30 loader: Arc::new(move |_| Ok(value.clone())),
31 cache,
32 }
33 }
34
35 fn force(&self, cx: &mut Cx) -> Result<Value> {
36 if let Some(cached) = self.cache.get() {
37 return cached.clone();
38 }
39 let result = (self.loader)(cx);
40 let _ = self.cache.set(result.clone());
41 result
42 }
43}
44
45pub struct LazyTable {
57 entries: RwLock<HashMap<Symbol, Arc<LazyEntry>>>,
58}
59
60impl Clone for LazyTable {
61 fn clone(&self) -> Self {
62 Self {
63 entries: RwLock::new(
64 self.entries
65 .read()
66 .unwrap_or_else(PoisonError::into_inner)
67 .clone(),
68 ),
69 }
70 }
71}
72
73impl LazyTable {
74 fn read(&self) -> Result<RwLockReadGuard<'_, HashMap<Symbol, Arc<LazyEntry>>>> {
75 self.entries
76 .read()
77 .map_err(|_| Error::Eval("table/lazy lock poisoned".into()))
78 }
79
80 fn write(&self) -> Result<RwLockWriteGuard<'_, HashMap<Symbol, Arc<LazyEntry>>>> {
81 self.entries
82 .write()
83 .map_err(|_| Error::Eval("table/lazy lock poisoned".into()))
84 }
85
86 pub fn new() -> Self {
88 Self {
89 entries: RwLock::new(HashMap::new()),
90 }
91 }
92
93 pub fn with_loaders(pairs: Vec<(Symbol, ValueLoader)>) -> Self {
117 let entries = pairs
118 .into_iter()
119 .map(|(key, loader)| {
120 (
121 key,
122 Arc::new(LazyEntry {
123 loader,
124 cache: OnceLock::new(),
125 }),
126 )
127 })
128 .collect();
129 Self {
130 entries: RwLock::new(entries),
131 }
132 }
133
134 pub fn with_entries(entries: Vec<(Symbol, Value)>) -> Self {
140 let entries = entries
141 .into_iter()
142 .map(|(key, value)| (key, Arc::new(LazyEntry::eager(value))))
143 .collect();
144 Self {
145 entries: RwLock::new(entries),
146 }
147 }
148
149 pub fn put_lazy(&self, key: Symbol, loader: ValueLoader) {
154 self.entries
155 .write()
156 .unwrap_or_else(PoisonError::into_inner)
157 .insert(
158 key,
159 Arc::new(LazyEntry {
160 loader,
161 cache: OnceLock::new(),
162 }),
163 );
164 }
165
166 fn descriptor_entries(&self, cx: &mut Cx) -> Result<Vec<(Symbol, Expr)>> {
167 let mut entries = self
168 .entries(cx)?
169 .into_iter()
170 .map(|(key, value)| Ok((key, value.object().as_expr(cx)?)))
171 .collect::<Result<Vec<_>>>()?;
172 entries.sort_by(|left, right| left.0.cmp(&right.0));
173 Ok(entries)
174 }
175}
176
177impl Default for LazyTable {
178 fn default() -> Self {
179 Self::new()
180 }
181}
182
183impl Object for LazyTable {
184 fn display(&self, _cx: &mut Cx) -> Result<String> {
185 Ok(format!("table/lazy[{}]", self.read()?.len()))
186 }
187
188 fn as_any(&self) -> &dyn std::any::Any {
189 self
190 }
191}
192
193impl sim_kernel::ObjectCompat for LazyTable {
194 fn class(&self, cx: &mut Cx) -> Result<ClassRef> {
195 let symbol = lazy_table_class_symbol();
196 if let Some(value) = cx.registry().class_by_symbol(&symbol) {
197 return Ok(value.clone());
198 }
199 let symbol = Symbol::qualified("core", "Table");
200 if let Some(value) = cx.registry().class_by_symbol(&symbol) {
201 return Ok(value.clone());
202 }
203 cx.factory().class_stub(CORE_TABLE_CLASS_ID, symbol)
204 }
205 fn as_expr(&self, cx: &mut Cx) -> Result<Expr> {
206 self.as_table_expr(cx)
207 }
208 fn truth(&self, _cx: &mut Cx) -> Result<bool> {
209 Ok(!self.read()?.is_empty())
210 }
211 fn as_table_impl(&self) -> Option<&dyn Table> {
212 Some(self)
213 }
214 fn as_object_encoder(&self) -> Option<&dyn ObjectEncode> {
215 Some(self)
216 }
217}
218
219impl ObjectEncode for LazyTable {
220 fn object_encoding(&self, cx: &mut Cx) -> Result<ObjectEncoding> {
221 Ok(ObjectEncoding::Constructor {
222 class: lazy_table_class_symbol(),
223 args: vec![
224 Expr::Symbol(Symbol::new("v0")),
225 sim_table_core::citizen_fields::entries::encode(&self.descriptor_entries(cx)?),
226 ],
227 })
228 }
229}
230
231impl sim_citizen::Citizen for LazyTable {
232 fn citizen_symbol() -> Symbol {
233 lazy_table_class_symbol()
234 }
235
236 fn citizen_version() -> u32 {
237 0
238 }
239
240 fn citizen_arity() -> usize {
241 1
242 }
243
244 fn citizen_fields() -> &'static [&'static str] {
245 &["entries"]
246 }
247}
248
249impl Table for LazyTable {
250 fn backend_symbol(&self) -> Symbol {
251 Symbol::qualified("table", "lazy")
252 }
253
254 fn get(&self, cx: &mut Cx, key: Symbol) -> Result<Value> {
255 match self.read()?.get(&key).cloned() {
256 Some(entry) => entry.force(cx),
257 None => cx.factory().nil(),
258 }
259 }
260
261 fn set(&self, _cx: &mut Cx, key: Symbol, value: Value) -> Result<()> {
262 self.write()?.insert(key, Arc::new(LazyEntry::eager(value)));
263 Ok(())
264 }
265
266 fn has(&self, _cx: &mut Cx, key: Symbol) -> Result<bool> {
267 Ok(self.read()?.contains_key(&key))
268 }
269
270 fn del(&self, cx: &mut Cx, key: Symbol) -> Result<Value> {
271 match self.write()?.remove(&key) {
272 Some(entry) => entry.force(cx),
273 None => cx.factory().nil(),
274 }
275 }
276
277 fn keys(&self, _cx: &mut Cx) -> Result<Vec<Symbol>> {
278 Ok(self.read()?.keys().cloned().collect())
279 }
280
281 fn entries(&self, cx: &mut Cx) -> Result<Vec<(Symbol, Value)>> {
282 let snapshot: Vec<(Symbol, Arc<LazyEntry>)> = self
283 .read()?
284 .iter()
285 .map(|(key, entry)| (key.clone(), entry.clone()))
286 .collect();
287 let mut out = Vec::with_capacity(snapshot.len());
288 for (key, entry) in snapshot {
289 out.push((key, entry.force(cx)?));
290 }
291 Ok(out)
292 }
293
294 fn len(&self, _cx: &mut Cx) -> Result<usize> {
295 Ok(self.read()?.len())
296 }
297
298 fn clear(&self, _cx: &mut Cx) -> Result<()> {
299 self.write()?.clear();
300 Ok(())
301 }
302}