Skip to main content

sim_lib_expr_tree/
handle.rs

1use std::{
2    collections::BTreeMap,
3    sync::{Arc, Mutex},
4};
5
6use sim_kernel::{Cx, Object, ObjectCompat, Result};
7
8use crate::runtime::TreeState;
9
10const MAX_STORAGE_NAME_BYTES: usize = 256;
11
12// sim-non-citizen(reason = "live writer scope and backend authority", kind = "handle", descriptor = "")
13/// Opaque live expression-tree handle.
14///
15/// Handles are cloneable references to one writer scope. Their default object
16/// expression is `core/opaque-object`; they never expose Citizen reconstruction.
17#[derive(Clone)]
18pub struct TreeHandle {
19    pub(crate) state: Arc<Mutex<TreeState>>,
20}
21
22impl TreeHandle {
23    fn new(state: Arc<Mutex<TreeState>>) -> Self {
24        Self { state }
25    }
26}
27
28impl Object for TreeHandle {
29    fn display(&self, _cx: &mut Cx) -> Result<String> {
30        let state = self
31            .state
32            .lock()
33            .map_err(|_| sim_kernel::Error::Eval("expression-tree state poisoned".to_owned()))?;
34        Ok(format!("#<expr-tree {}>", state.storage_name()))
35    }
36
37    fn as_any(&self) -> &dyn std::any::Any {
38        self
39    }
40}
41
42impl ObjectCompat for TreeHandle {}
43
44pub(crate) struct TreeRuntime {
45    stores: Mutex<BTreeMap<String, Arc<Mutex<TreeState>>>>,
46}
47
48impl TreeRuntime {
49    pub(crate) fn new() -> Self {
50        Self {
51            stores: Mutex::new(BTreeMap::new()),
52        }
53    }
54
55    pub(crate) fn open(
56        &self,
57        cx: &Cx,
58        storage_name: &str,
59    ) -> std::result::Result<TreeHandle, String> {
60        if storage_name.is_empty() || storage_name.len() > MAX_STORAGE_NAME_BYTES {
61            return Err(format!(
62                "storage name must contain 1..={MAX_STORAGE_NAME_BYTES} bytes"
63            ));
64        }
65        let mut stores = self
66            .stores
67            .lock()
68            .map_err(|_| "expression-tree storage registry poisoned".to_owned())?;
69        let state = match stores.get(storage_name) {
70            Some(state) => Arc::clone(state),
71            None => {
72                let state = Arc::new(Mutex::new(TreeState::new(cx, storage_name.to_owned())?));
73                stores.insert(storage_name.to_owned(), Arc::clone(&state));
74                state
75            }
76        };
77        Ok(TreeHandle::new(state))
78    }
79}