runmat_runtime/
workspace.rs1use runmat_builtins::Value;
2
3#[cfg(test)]
4use once_cell::sync::Lazy;
5#[cfg(test)]
6use std::sync::Mutex;
7
8type AssignFn = fn(&str, Value) -> Result<(), String>;
11type ClearFn = fn() -> Result<(), String>;
12type RemoveFn = fn(&str) -> Result<(), String>;
13
14pub struct WorkspaceResolver {
15 pub lookup: fn(&str) -> Option<Value>,
16 pub snapshot: fn() -> Vec<(String, Value)>,
17 pub globals: fn() -> Vec<String>,
18 pub assign: Option<AssignFn>,
19 pub clear: Option<ClearFn>,
20 pub remove: Option<RemoveFn>,
21}
22
23mod resolver_storage {
24 use super::WorkspaceResolver;
25
26 pub(super) fn set(resolver: WorkspaceResolver) {
27 imp::set(resolver)
28 }
29
30 pub(super) fn with<R>(f: impl FnOnce(Option<&WorkspaceResolver>) -> R) -> R {
31 imp::with(f)
32 }
33
34 #[cfg(test)]
35 mod imp {
36 use super::WorkspaceResolver;
37 use std::cell::RefCell;
38
39 thread_local! {
44 static RESOLVER: RefCell<Option<WorkspaceResolver>> = const { RefCell::new(None) };
45 }
46
47 pub(super) fn set(resolver: WorkspaceResolver) {
48 RESOLVER.with(|slot| {
49 *slot.borrow_mut() = Some(resolver);
50 });
51 }
52
53 pub(super) fn with<R>(f: impl FnOnce(Option<&WorkspaceResolver>) -> R) -> R {
54 RESOLVER.with(|slot| {
55 let guard = slot.borrow();
56 f(guard.as_ref())
57 })
58 }
59 }
60
61 #[cfg(not(test))]
62 mod imp {
63 use super::WorkspaceResolver;
64 use once_cell::sync::Lazy;
65 use std::sync::RwLock;
66
67 static RESOLVER: Lazy<RwLock<Option<WorkspaceResolver>>> = Lazy::new(|| RwLock::new(None));
68
69 pub(super) fn set(resolver: WorkspaceResolver) {
70 let mut guard = RESOLVER
71 .write()
72 .unwrap_or_else(|poison| poison.into_inner());
73 *guard = Some(resolver);
74 }
75
76 pub(super) fn with<R>(f: impl FnOnce(Option<&WorkspaceResolver>) -> R) -> R {
77 let guard = RESOLVER.read().unwrap_or_else(|poison| poison.into_inner());
78 f(guard.as_ref())
79 }
80 }
81}
82
83#[cfg(test)]
84static TEST_WORKSPACE_LOCK: Lazy<Mutex<()>> = Lazy::new(|| Mutex::new(()));
85
86pub fn register_workspace_resolver(resolver: WorkspaceResolver) {
89 resolver_storage::set(resolver);
90}
91
92pub fn lookup(name: &str) -> Option<Value> {
94 resolver_storage::with(|resolver| resolver.and_then(|r| (r.lookup)(name)))
95}
96
97pub fn snapshot() -> Option<Vec<(String, Value)>> {
100 resolver_storage::with(|resolver| resolver.map(|r| (r.snapshot)()))
101}
102
103pub fn global_names() -> Vec<String> {
105 resolver_storage::with(|resolver| resolver.map(|r| (r.globals)()).unwrap_or_default())
106}
107
108pub fn assign(name: &str, value: Value) -> Result<(), String> {
109 resolver_storage::with(|resolver| {
110 let resolver = resolver.ok_or_else(|| "workspace state unavailable".to_string())?;
111 let assign = resolver
112 .assign
113 .ok_or_else(|| "workspace assignment unavailable".to_string())?;
114 (assign)(name, value)
115 })
116}
117
118pub fn clear() -> Result<(), String> {
119 resolver_storage::with(|resolver| {
120 let resolver = resolver.ok_or_else(|| "workspace state unavailable".to_string())?;
121 let clear = resolver
122 .clear
123 .ok_or_else(|| "workspace clearing unavailable".to_string())?;
124 (clear)()
125 })
126}
127
128pub fn remove(name: &str) -> Result<(), String> {
129 resolver_storage::with(|resolver| {
130 let resolver = resolver.ok_or_else(|| "workspace state unavailable".to_string())?;
131 let remove = resolver
132 .remove
133 .ok_or_else(|| "workspace removal unavailable".to_string())?;
134 (remove)(name)
135 })
136}
137
138pub fn is_available() -> bool {
140 resolver_storage::with(|resolver| resolver.is_some())
141}
142
143#[cfg(test)]
144pub(crate) fn test_guard() -> std::sync::MutexGuard<'static, ()> {
145 TEST_WORKSPACE_LOCK
146 .lock()
147 .unwrap_or_else(|poison| poison.into_inner())
148}