1use std::any::Any;
2use std::cell::RefCell;
3use std::collections::HashMap;
4use std::fmt;
5use std::rc::Rc;
6
7use crate::lang::data::{Atom, Metadata, Symbol};
8use crate::lang::protocol::{IDeref, IDisplay, INamespaced, IReset};
9
10thread_local! {
11 static DYNAMIC_BINDINGS: RefCell<HashMap<usize, Vec<Box<dyn Any>>>> = RefCell::new(HashMap::new());
12}
13
14#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
15pub enum VarOrigin {
16 #[default]
17 Source,
18 HalFallback,
19 RustLibrary,
20 RuntimePrimitive,
21}
22
23#[derive(Debug, Clone, Default, PartialEq)]
24pub struct VarMetadata {
25 pub hara: Option<Rc<Metadata>>,
26 pub control: bool,
27 pub dynamic: bool,
28 pub macro_form: bool,
29 pub doc: Option<String>,
30 pub arglists: Vec<String>,
31 pub origin: VarOrigin,
32 pub extra: HashMap<String, String>,
33}
34
35#[derive(Debug, Clone)]
36pub struct Var<V> {
37 identity: Rc<()>,
38 symbol: Symbol,
39 value: Atom<V>,
40 metadata: Rc<RefCell<VarMetadata>>,
41 schema_contract: Rc<RefCell<Option<V>>>,
42}
43impl<V> Var<V> {
44 pub fn new(path: impl AsRef<str>, value: V) -> Self {
45 Self {
46 identity: Rc::new(()),
47 symbol: Symbol::parse(path.as_ref()),
48 value: Atom::new(value),
49 metadata: Rc::new(RefCell::new(VarMetadata::default())),
50 schema_contract: Rc::new(RefCell::new(None)),
51 }
52 }
53 pub fn with_metadata(path: impl AsRef<str>, value: V, metadata: VarMetadata) -> Self {
54 Self {
55 identity: Rc::new(()),
56 symbol: Symbol::parse(path.as_ref()),
57 value: Atom::new(value),
58 metadata: Rc::new(RefCell::new(metadata)),
59 schema_contract: Rc::new(RefCell::new(None)),
60 }
61 }
62 pub fn symbol(&self) -> &Symbol {
63 &self.symbol
64 }
65 pub fn metadata(&self) -> VarMetadata {
66 self.metadata.borrow().clone()
67 }
68 pub fn hara_metadata(&self) -> Option<Rc<Metadata>> {
69 self.metadata.borrow().hara.clone()
70 }
71 pub fn set_hara_metadata(&self, metadata: Option<Rc<Metadata>>) {
72 self.metadata.borrow_mut().hara = metadata;
73 }
74 pub fn set_metadata(&self, metadata: VarMetadata) -> VarMetadata {
75 std::mem::replace(&mut *self.metadata.borrow_mut(), metadata)
76 }
77 pub fn update_metadata(&self, update: impl FnOnce(&mut VarMetadata)) {
78 update(&mut self.metadata.borrow_mut());
79 }
80 pub fn origin(&self) -> VarOrigin {
81 self.metadata.borrow().origin
82 }
83 pub fn set_origin(&self, origin: VarOrigin) {
84 self.metadata.borrow_mut().origin = origin;
85 }
86 pub fn identity_address(&self) -> usize {
87 self.identity_key()
88 }
89 pub fn same_identity(&self, other: &Self) -> bool {
90 Rc::ptr_eq(&self.identity, &other.identity)
91 }
92 fn identity_key(&self) -> usize {
93 Rc::as_ptr(&self.identity) as usize
94 }
95 pub fn is_control(&self) -> bool {
96 self.metadata.borrow().control
97 || self
98 .metadata
99 .borrow()
100 .hara
101 .as_ref()
102 .is_some_and(|meta| meta.flag("control"))
103 }
104 pub fn is_dynamic(&self) -> bool {
105 self.metadata.borrow().dynamic
106 || self
107 .metadata
108 .borrow()
109 .hara
110 .as_ref()
111 .is_some_and(|meta| meta.flag("dynamic"))
112 }
113 pub fn is_macro(&self) -> bool {
114 self.metadata.borrow().macro_form
115 || self
116 .metadata
117 .borrow()
118 .hara
119 .as_ref()
120 .is_some_and(|meta| meta.flag("macro"))
121 }
122}
123impl<V: Clone + 'static> Var<V> {
124 pub fn requalify(&self, path: impl AsRef<str>) -> Self {
125 Self {
126 identity: self.identity.clone(),
127 symbol: Symbol::parse(path.as_ref()),
128 value: self.value.clone(),
129 metadata: self.metadata.clone(),
130 schema_contract: self.schema_contract.clone(),
131 }
132 }
133 pub fn deref_value(&self) -> V {
134 let key = self.identity_key();
135 DYNAMIC_BINDINGS.with(|bindings| {
136 bindings
137 .borrow()
138 .get(&key)
139 .and_then(|stack| stack.last())
140 .and_then(|value| value.downcast_ref::<V>())
141 .cloned()
142 .unwrap_or_else(|| self.value.deref_value())
143 })
144 }
145 pub fn schema_contract(&self) -> Option<V> {
146 self.schema_contract.borrow().clone()
147 }
148 pub fn set_schema_contract(&self, contract: Option<V>) {
149 *self.schema_contract.borrow_mut() = contract;
150 }
151 pub fn bind(&self, value: V) {
152 let key = self.identity_key();
153 DYNAMIC_BINDINGS.with(|bindings| {
154 bindings
155 .borrow_mut()
156 .entry(key)
157 .or_default()
158 .push(Box::new(value));
159 });
160 }
161 pub fn unbind(&self) -> Result<V, String> {
162 let key = self.identity_key();
163 DYNAMIC_BINDINGS.with(|bindings| {
164 let mut bindings = bindings.borrow_mut();
165 let stack = bindings
166 .get_mut(&key)
167 .ok_or_else(|| "Var has no dynamic binding".to_string())?;
168 let value = stack
169 .pop()
170 .and_then(|value| value.downcast::<V>().ok())
171 .map(|value| *value)
172 .ok_or_else(|| "Var dynamic binding type mismatch".to_string())?;
173 if stack.is_empty() {
174 bindings.remove(&key);
175 }
176 Ok(value)
177 })
178 }
179 pub fn reset_value(&self, value: V) -> V {
180 self.value.reset(value).expect("unvalidated var")
181 }
182}
183impl<V: Clone + 'static> IDeref for Var<V> {
184 type Output = V;
185 fn deref(&self) -> V {
186 self.deref_value()
187 }
188}
189impl<V: Clone + 'static> IReset<V> for Var<V> {
190 type Error = String;
191 fn reset(&self, value: V) -> Result<V, String> {
192 Ok(self.reset_value(value))
193 }
194}
195impl<V> INamespaced for Var<V> {
196 fn get_name(&self) -> &str {
197 self.symbol.get_name()
198 }
199 fn get_namespace(&self) -> Option<&str> {
200 self.symbol.get_namespace()
201 }
202}
203impl<V> IDisplay for Var<V> {
204 fn display(&self) -> String {
205 format!("#'{}", self.symbol.as_str())
206 }
207}
208impl<V> fmt::Display for Var<V> {
209 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
210 f.write_str(&self.display())
211 }
212}
213
214#[cfg(test)]
215mod tests {
216 use super::{Var, VarMetadata};
217 use crate::lang::protocol::{IDeref, IDisplay, INamespaced};
218 #[test]
219 fn is_namespaced_resettable_and_metadata_driven() {
220 let var = Var::with_metadata(
221 "hello/value",
222 1,
223 VarMetadata {
224 dynamic: true,
225 ..Default::default()
226 },
227 );
228 assert_eq!(var.get_namespace(), Some("hello"));
229 assert_eq!(var.display(), "#'hello/value");
230 assert_eq!(var.reset_value(2), 2);
231 assert_eq!(var.deref(), 2);
232 assert!(var.is_dynamic());
233 }
234 #[test]
235 fn dynamic_bindings_are_nested_thread_local_and_share_identity() {
236 let var = Var::new("hello/value", 1);
237 let alias = var.clone();
238 assert!(var.same_identity(&alias));
239 var.bind(2);
240 assert_eq!(alias.deref(), 2);
241 alias.bind(3);
242 assert_eq!(var.deref(), 3);
243 assert_eq!(var.unbind().unwrap(), 3);
244 assert_eq!(alias.deref(), 2);
245 assert_eq!(alias.unbind().unwrap(), 2);
246 assert_eq!(var.deref(), 1);
247 assert!(var.unbind().is_err());
248 }
249 #[test]
250 fn cloned_vars_share_metadata_updates() {
251 let var = Var::new("hello/value", 1);
252 let alias = var.clone();
253 var.update_metadata(|meta| {
254 meta.doc = Some("A value".into());
255 meta.arglists.push("[x]".into());
256 });
257 assert_eq!(alias.metadata().doc.as_deref(), Some("A value"));
258 assert_eq!(alias.metadata().arglists, vec!["[x]"]);
259 let old = alias.set_metadata(VarMetadata {
260 dynamic: true,
261 ..Default::default()
262 });
263 assert_eq!(old.doc.as_deref(), Some("A value"));
264 assert!(var.is_dynamic());
265 }
266 #[test]
267 fn requalification_preserves_identity_root_metadata_and_bindings() {
268 let var = Var::new("value", 1);
269 var.update_metadata(|meta| meta.dynamic = true);
270 let qualified = var.requalify("hello/value");
271 assert!(var.same_identity(&qualified));
272 assert_eq!(qualified.symbol().as_str(), "hello/value");
273 qualified.reset_value(2);
274 assert_eq!(var.deref(), 2);
275 var.bind(3);
276 assert_eq!(qualified.deref(), 3);
277 qualified.unbind().unwrap();
278 assert!(qualified.is_dynamic());
279 }
280}