Skip to main content

hara_native/kernel/
var.rs

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}