runmat_runtime/
workspace.rs

1use once_cell::sync::Lazy;
2use runmat_builtins::Value;
3use std::sync::RwLock;
4
5/// Resolver used by the runtime to access the caller workspace when builtins
6/// (such as `save`) need to look up variables by name.
7pub struct WorkspaceResolver {
8    pub lookup: fn(&str) -> Option<Value>,
9    pub snapshot: fn() -> Vec<(String, Value)>,
10    pub globals: fn() -> Vec<String>,
11}
12
13static RESOLVER: Lazy<RwLock<Option<WorkspaceResolver>>> = Lazy::new(|| RwLock::new(None));
14
15/// Register the workspace resolver. Ignition installs this once during
16/// initialization so that language builtins can query variables lazily.
17pub fn register_workspace_resolver(resolver: WorkspaceResolver) {
18    if let Ok(mut guard) = RESOLVER.write() {
19        *guard = Some(resolver);
20    }
21}
22
23/// Lookup a variable by name in the active workspace.
24pub fn lookup(name: &str) -> Option<Value> {
25    RESOLVER
26        .read()
27        .ok()
28        .and_then(|guard| guard.as_ref().and_then(|resolver| (resolver.lookup)(name)))
29}
30
31/// Snapshot the active workspace into a vector of `(name, value)` pairs.
32/// Returns `None` when no resolver/workspace is active.
33pub fn snapshot() -> Option<Vec<(String, Value)>> {
34    RESOLVER
35        .read()
36        .ok()
37        .and_then(|guard| guard.as_ref().map(|resolver| (resolver.snapshot)()))
38}
39
40/// Return the list of global variable names visible to the active workspace.
41pub fn global_names() -> Vec<String> {
42    RESOLVER
43        .read()
44        .ok()
45        .and_then(|guard| guard.as_ref().map(|resolver| (resolver.globals)()))
46        .unwrap_or_default()
47}
48
49/// Returns true when a resolver has been registered.
50pub fn is_available() -> bool {
51    RESOLVER.read().map(|g| g.is_some()).unwrap_or(false)
52}