Skip to main content

cljrs_value/
types.rs

1//! Stub types for Phase 4/7 that are referenced by the Value enum.
2
3#![allow(unused)]
4
5use std::collections::HashMap;
6use std::mem;
7use std::sync::{Arc, Condvar, Mutex};
8
9use cljrs_gc::GcPtr;
10use cljrs_reader::Form;
11
12use crate::Value;
13
14// ── Protocol ──────────────────────────────────────────────────────────────────
15
16/// Inner map type for protocol implementations: method_name → impl fn.
17pub type MethodMap = HashMap<Arc<str>, Value>;
18
19/// A Clojure protocol — an interface-like construct with named methods.
20#[derive(Debug)]
21pub struct Protocol {
22    pub name: Arc<str>,
23    pub ns: Arc<str>,
24    pub methods: Vec<ProtocolMethod>,
25    /// type_tag → { method_name → impl fn }
26    pub impls: Mutex<HashMap<Arc<str>, MethodMap>>,
27}
28
29impl Protocol {
30    pub fn new(name: Arc<str>, ns: Arc<str>, methods: Vec<ProtocolMethod>) -> Self {
31        Self {
32            name,
33            ns,
34            methods,
35            impls: Mutex::new(HashMap::new()),
36        }
37    }
38}
39
40impl cljrs_gc::Trace for Protocol {
41    fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
42        {
43            let impls = self.impls.lock().unwrap();
44            for method_map in impls.values() {
45                for v in method_map.values() {
46                    v.trace(visitor);
47                }
48            }
49        }
50    }
51}
52
53/// One method signature declared in a `defprotocol`.
54#[derive(Debug, Clone)]
55pub struct ProtocolMethod {
56    pub name: Arc<str>,
57    pub min_arity: usize,
58    pub variadic: bool,
59}
60
61impl cljrs_gc::Trace for ProtocolMethod {
62    fn trace(&self, _: &mut cljrs_gc::MarkVisitor) {}
63}
64
65// ── ProtocolFn ────────────────────────────────────────────────────────────────
66
67/// Callable that dispatches a single protocol method on the type of `args[0]`.
68#[derive(Debug)]
69pub struct ProtocolFn {
70    pub protocol: GcPtr<Protocol>,
71    pub method_name: Arc<str>,
72    pub min_arity: usize,
73    pub variadic: bool,
74}
75
76impl cljrs_gc::Trace for ProtocolFn {
77    fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
78        use cljrs_gc::GcVisitor as _;
79        visitor.visit(&self.protocol);
80    }
81}
82
83// ── MultiFn ───────────────────────────────────────────────────────────────────
84
85/// A Clojure multimethod — arbitrary dispatch via a user-supplied function.
86#[derive(Debug)]
87pub struct MultiFn {
88    pub name: Arc<str>,
89    pub dispatch_fn: Value,
90    /// pr_str(dispatch-val) → implementation fn
91    pub methods: Mutex<HashMap<String, Value>>,
92    /// recorded preferences (for future derive/hierarchy)
93    pub prefers: Mutex<HashMap<String, Vec<String>>>,
94    /// normally ":default"
95    pub default_dispatch: String,
96}
97
98impl MultiFn {
99    pub fn new(name: Arc<str>, dispatch_fn: Value, default_dispatch: String) -> Self {
100        Self {
101            name,
102            dispatch_fn,
103            methods: Mutex::new(HashMap::new()),
104            prefers: Mutex::new(HashMap::new()),
105            default_dispatch,
106        }
107    }
108}
109
110impl cljrs_gc::Trace for MultiFn {
111    fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
112        self.dispatch_fn.trace(visitor);
113        {
114            let methods = self.methods.lock().unwrap();
115            for v in methods.values() {
116                v.trace(visitor);
117            }
118        }
119    }
120}
121
122// ── Var ───────────────────────────────────────────────────────────────────────
123
124/// A Clojure var — a namespace-interned mutable root binding.
125#[derive(Debug)]
126pub struct Var {
127    pub namespace: Arc<str>,
128    pub name: Arc<str>,
129    pub value: Mutex<Option<Value>>,
130    pub is_macro: bool,
131    /// Metadata map (e.g. `{:dynamic true}`).
132    pub meta: Mutex<Option<Value>>,
133    pub watches: Mutex<Vec<(Value, Value)>>,
134}
135
136impl Var {
137    pub fn new(namespace: impl Into<Arc<str>>, name: impl Into<Arc<str>>) -> Self {
138        Self {
139            namespace: namespace.into(),
140            name: name.into(),
141            value: Mutex::new(None),
142            is_macro: false,
143            meta: Mutex::new(None),
144            watches: Mutex::new(Vec::new()),
145        }
146    }
147
148    pub fn is_bound(&self) -> bool {
149        self.value.lock().unwrap().is_some()
150    }
151
152    pub fn deref(&self) -> Option<Value> {
153        self.value.lock().unwrap().clone()
154    }
155
156    pub fn bind(&self, v: Value) {
157        *self.value.lock().unwrap() = Some(v);
158    }
159
160    pub fn get_meta(&self) -> Option<Value> {
161        self.meta.lock().unwrap().clone()
162    }
163
164    pub fn set_meta(&self, m: Value) {
165        *self.meta.lock().unwrap() = Some(m);
166    }
167
168    pub fn full_name(&self) -> String {
169        format!("{}/{}", self.namespace, self.name)
170    }
171}
172
173impl cljrs_gc::Trace for Var {
174    fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
175        {
176            let value = self.value.lock().unwrap();
177            if let Some(v) = value.as_ref() {
178                v.trace(visitor);
179            }
180        }
181        {
182            let meta = self.meta.lock().unwrap();
183            if let Some(m) = meta.as_ref() {
184                m.trace(visitor);
185            }
186        }
187        {
188            let watches = self.watches.lock().unwrap();
189            for (key, f) in watches.iter() {
190                key.trace(visitor);
191                f.trace(visitor);
192            }
193        }
194    }
195}
196
197// ── Atom ──────────────────────────────────────────────────────────────────────
198
199/// A Clojure atom — a thread-safe mutable reference.
200#[derive(Debug)]
201pub struct Atom {
202    pub value: Mutex<Value>,
203    pub meta: Mutex<Option<Value>>,
204    pub validator: Mutex<Option<Value>>,
205    pub watches: Mutex<Vec<(Value, Value)>>,
206}
207
208impl Atom {
209    pub fn new(v: Value) -> Self {
210        Self {
211            value: Mutex::new(v),
212            meta: Mutex::new(None),
213            validator: Mutex::new(None),
214            watches: Mutex::new(Vec::new()),
215        }
216    }
217
218    pub fn deref(&self) -> Value {
219        self.value.lock().unwrap().clone()
220    }
221
222    pub fn reset(&self, v: Value) -> Value {
223        let mut guard = self.value.lock().unwrap();
224        *guard = v.clone();
225        v
226    }
227
228    pub fn get_meta(&self) -> Option<Value> {
229        self.meta.lock().unwrap().clone()
230    }
231
232    pub fn set_meta(&self, m: Option<Value>) {
233        *self.meta.lock().unwrap() = m;
234    }
235
236    pub fn get_validator(&self) -> Option<Value> {
237        self.validator.lock().unwrap().clone()
238    }
239
240    pub fn set_validator(&self, vf: Option<Value>) {
241        *self.validator.lock().unwrap() = vf;
242    }
243}
244
245impl cljrs_gc::Trace for Atom {
246    fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
247        {
248            let value = self.value.lock().unwrap();
249            value.trace(visitor);
250        }
251        {
252            let meta = self.meta.lock().unwrap();
253            if let Some(m) = meta.as_ref() {
254                m.trace(visitor);
255            }
256        }
257        {
258            let validator = self.validator.lock().unwrap();
259            if let Some(vf) = validator.as_ref() {
260                vf.trace(visitor);
261            }
262        }
263        {
264            let watches = self.watches.lock().unwrap();
265            for (key, f) in watches.iter() {
266                key.trace(visitor);
267                f.trace(visitor);
268            }
269        }
270    }
271}
272
273// ── Namespace ─────────────────────────────────────────────────────────────────
274
275/// A Clojure namespace with intern table, refers, and aliases.
276#[derive(Debug)]
277pub struct Namespace {
278    pub name: Arc<str>,
279    /// Vars interned directly in this namespace.
280    pub interns: Mutex<HashMap<Arc<str>, GcPtr<Var>>>,
281    /// Vars referred from other namespaces (e.g. clojure.core).
282    pub refers: Mutex<HashMap<Arc<str>, GcPtr<Var>>>,
283    /// Namespace aliases: short-name → full namespace name.
284    pub aliases: Mutex<HashMap<Arc<str>, Arc<str>>>,
285}
286
287impl Namespace {
288    pub fn new(name: impl Into<Arc<str>>) -> Self {
289        Self {
290            name: name.into(),
291            interns: Mutex::new(HashMap::new()),
292            refers: Mutex::new(HashMap::new()),
293            aliases: Mutex::new(HashMap::new()),
294        }
295    }
296}
297
298impl cljrs_gc::Trace for Namespace {
299    fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
300        use cljrs_gc::GcVisitor as _;
301        {
302            let interns = self.interns.lock().unwrap();
303            for var in interns.values() {
304                visitor.visit(var);
305            }
306        }
307        {
308            let refers = self.refers.lock().unwrap();
309            for var in refers.values() {
310                visitor.visit(var);
311            }
312        }
313    }
314}
315
316// ── NativeFn ──────────────────────────────────────────────────────────────────
317
318/// A Rust function callable from Clojure.
319/// Legacy type alias kept for source compatibility. Bare `fn` pointers
320/// implement `Fn` and can be passed anywhere a `NativeFnFunc` is expected.
321pub type NativeFnPtr = fn(&[Value]) -> crate::error::ValueResult<Value>;
322
323/// The callable stored inside a `NativeFn`. Supports both bare function
324/// pointers and closures that capture state.
325pub type NativeFnFunc = Arc<dyn Fn(&[Value]) -> crate::error::ValueResult<Value> + Send + Sync>;
326
327#[derive(Clone, Debug)]
328pub enum Arity {
329    Fixed(usize),
330    Variadic { min: usize },
331}
332
333pub struct NativeFn {
334    pub name: Arc<str>,
335    pub arity: Arity,
336    pub func: NativeFnFunc,
337}
338
339impl NativeFn {
340    /// Create from a bare function pointer (backwards-compatible).
341    pub fn new(name: impl Into<Arc<str>>, arity: Arity, func: NativeFnPtr) -> Self {
342        Self {
343            name: name.into(),
344            arity,
345            func: Arc::new(func),
346        }
347    }
348
349    /// Create from a closure or any `Fn(&[Value]) -> ValueResult<Value>`.
350    pub fn with_closure(
351        name: impl Into<Arc<str>>,
352        arity: Arity,
353        func: impl Fn(&[Value]) -> crate::error::ValueResult<Value> + Send + Sync + 'static,
354    ) -> Self {
355        Self {
356            name: name.into(),
357            arity,
358            func: Arc::new(func),
359        }
360    }
361}
362
363impl std::fmt::Debug for NativeFn {
364    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
365        f.debug_struct("NativeFn")
366            .field("name", &self.name)
367            .field("arity", &self.arity)
368            .field("func", &"<fn>")
369            .finish()
370    }
371}
372
373impl cljrs_gc::Trace for NativeFn {
374    fn trace(&self, _: &mut cljrs_gc::MarkVisitor) {}
375}
376
377// ── CljxFnArity ───────────────────────────────────────────────────────────────
378
379/// One arity branch of a Clojure function.
380#[derive(Debug, Clone)]
381pub struct CljxFnArity {
382    /// Simple parameter names (no `&`).
383    /// For destructured params, these are gensym'd names.
384    pub params: Vec<Arc<str>>,
385    /// The name after `&`, if any.
386    pub rest_param: Option<Arc<str>>,
387    /// The body forms for this arity.
388    pub body: Vec<Form>,
389    /// Destructuring patterns: (param_index, original_form).
390    /// After binding the gensym'd param, these patterns are applied
391    /// via `bind_pattern` to destructure the value.
392    pub destructure_params: Vec<(usize, Form)>,
393    /// If the rest param is destructured, the original form.
394    pub destructure_rest: Option<Form>,
395}
396
397// ── CljxFn ────────────────────────────────────────────────────────────────────
398
399/// An interpreted Clojure closure with captured environment.
400#[derive(Debug, Clone)]
401pub struct CljxFn {
402    pub name: Option<Arc<str>>,
403    pub arities: Vec<CljxFnArity>,
404    /// Names of closed-over bindings (parallel to `closed_over_vals`).
405    pub closed_over_names: Vec<Arc<str>>,
406    /// Values of closed-over bindings (parallel to `closed_over_names`).
407    pub closed_over_vals: Vec<Value>,
408    /// True if this function was defined with `defmacro`.
409    pub is_macro: bool,
410    /// Namespace in which this function was defined (for macro hygiene).
411    pub defining_ns: Arc<str>,
412}
413
414impl CljxFn {
415    pub fn new(
416        name: Option<Arc<str>>,
417        arities: Vec<CljxFnArity>,
418        closed_over_names: Vec<Arc<str>>,
419        closed_over_vals: Vec<Value>,
420        is_macro: bool,
421        defining_ns: Arc<str>,
422    ) -> Self {
423        Self {
424            name,
425            arities,
426            closed_over_names,
427            closed_over_vals,
428            is_macro,
429            defining_ns,
430        }
431    }
432}
433
434impl cljrs_gc::Trace for CljxFn {
435    fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
436        for v in &self.closed_over_vals {
437            v.trace(visitor);
438        }
439    }
440}
441
442// ── BoundFn ──────────────────────────────────────────────────────────────────
443
444/// A function wrapped with captured dynamic bindings.
445/// When called, the captured bindings are pushed as a frame before delegating
446/// to the wrapped function. This means captured bindings override the caller's
447/// for the same var, but vars not in the capture fall through normally.
448#[derive(Debug)]
449pub struct BoundFn {
450    /// The wrapped callable.
451    pub wrapped: Value,
452    /// Captured dynamic bindings (merged flat frame; opaque to cljrs-value).
453    pub captured_bindings: HashMap<usize, Value>,
454}
455
456impl cljrs_gc::Trace for BoundFn {
457    fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
458        self.wrapped.trace(visitor);
459        for val in self.captured_bindings.values() {
460            val.trace(visitor);
461        }
462    }
463}
464
465// ── Thunk / LazySeq ───────────────────────────────────────────────────────────
466
467/// A deferred computation that produces a `Value` when forced.
468pub trait Thunk: Send + Sync + std::fmt::Debug + cljrs_gc::Trace {
469    fn force(&self) -> Result<Value, String>;
470}
471
472/// Internal state of a lazy sequence cell.
473pub enum LazySeqState {
474    /// Thunk not yet evaluated.
475    Pending(Box<dyn Thunk>),
476    /// Result cached after first force.
477    Forced(Value),
478    /// Thunk evaluation failed; error message is cached.
479    Error(String),
480}
481
482/// A lazy sequence that forces its thunk exactly once and caches the result.
483pub struct LazySeq {
484    pub state: Mutex<LazySeqState>,
485}
486
487impl LazySeq {
488    pub fn new(thunk: Box<dyn Thunk>) -> Self {
489        Self {
490            state: Mutex::new(LazySeqState::Pending(thunk)),
491        }
492    }
493
494    /// Realize the sequence: force the thunk on first call, return cached value on subsequent calls.
495    /// On error, returns `Value::Nil` and caches the error (retrievable via `error()`).
496    pub fn realize(&self) -> Value {
497        let thunk = {
498            let mut guard = self.state.lock().unwrap();
499            match &*guard {
500                LazySeqState::Forced(v) => return v.clone(),
501                LazySeqState::Error(_) => return Value::Nil,
502                LazySeqState::Pending(_) => {}
503            }
504            // Replace the pending state with a temporary Forced(Nil), extract the thunk.
505            let prev = mem::replace(&mut *guard, LazySeqState::Forced(Value::Nil));
506            let LazySeqState::Pending(thunk) = prev else {
507                unreachable!("state was not Pending")
508            };
509            thunk
510            // guard dropped here — lock released before forcing
511        };
512        // Force the thunk WITHOUT holding the lock. This ensures GC's
513        // lock().unwrap() in LazySeq::trace() will not deadlock.
514        match thunk.force() {
515            Ok(result) => {
516                *self.state.lock().unwrap() = LazySeqState::Forced(result.clone());
517                result
518            }
519            Err(msg) => {
520                *self.state.lock().unwrap() = LazySeqState::Error(msg);
521                Value::Nil
522            }
523        }
524    }
525
526    /// Return the cached error message, if the thunk failed.
527    pub fn error(&self) -> Option<String> {
528        let guard = self.state.lock().unwrap();
529        if let LazySeqState::Error(e) = &*guard {
530            Some(e.clone())
531        } else {
532            None
533        }
534    }
535}
536
537impl std::fmt::Debug for LazySeq {
538    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
539        write!(f, "LazySeq(...)")
540    }
541}
542
543impl cljrs_gc::Trace for LazySeq {
544    fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
545        // Safe to lock unconditionally: realize() drops the lock before entering
546        // eval (thunk.force()), so the lock is never held across a GC safepoint.
547        {
548            let state = self.state.lock().unwrap();
549            match &*state {
550                LazySeqState::Pending(thunk) => thunk.trace(visitor),
551                LazySeqState::Forced(v) => v.trace(visitor),
552                LazySeqState::Error(_) => {}
553            }
554        }
555    }
556}
557
558// ── CljxCons ──────────────────────────────────────────────────────────────────
559
560/// A lazy cons cell: head element + tail (may be a `LazySeq`, `List`, or `Nil`).
561///
562/// Used when `cons` is called with a `LazySeq` or `Cons` tail, enabling lazy
563/// sequences without eagerly realizing them.
564#[derive(Debug, Clone)]
565pub struct CljxCons {
566    pub head: Value,
567    pub tail: Value,
568}
569
570impl cljrs_gc::Trace for CljxCons {
571    fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
572        self.head.trace(visitor);
573        self.tail.trace(visitor);
574    }
575}
576
577// ── Volatile ──────────────────────────────────────────────────────────────────
578
579/// Non-atomic mutable cell (single-thread performance, no CAS).
580pub struct Volatile {
581    pub value: Mutex<Value>,
582}
583
584impl Volatile {
585    pub fn new(v: Value) -> Self {
586        Self {
587            value: Mutex::new(v),
588        }
589    }
590
591    pub fn deref(&self) -> Value {
592        self.value.lock().unwrap().clone()
593    }
594
595    pub fn reset(&self, v: Value) -> Value {
596        *self.value.lock().unwrap() = v.clone();
597        v
598    }
599}
600
601impl std::fmt::Debug for Volatile {
602    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
603        write!(f, "Volatile")
604    }
605}
606
607impl cljrs_gc::Trace for Volatile {
608    fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
609        {
610            let value = self.value.lock().unwrap();
611            value.trace(visitor);
612        }
613    }
614}
615
616// ── Delay ─────────────────────────────────────────────────────────────────────
617
618/// Internal state of a delay cell.
619pub enum DelayState {
620    Pending(Box<dyn Thunk>),
621    Forced(Value),
622}
623
624/// A lazy one-time computation (forced at most once, result cached).
625pub struct Delay {
626    pub state: Mutex<DelayState>,
627}
628
629impl Delay {
630    pub fn new(thunk: Box<dyn Thunk>) -> Self {
631        Self {
632            state: Mutex::new(DelayState::Pending(thunk)),
633        }
634    }
635
636    /// Force the delay and cache the result.
637    /// Returns the value on success, or an error message on failure.
638    pub fn force(&self) -> Result<Value, String> {
639        let thunk = {
640            let mut guard = self.state.lock().unwrap();
641            if let DelayState::Forced(v) = &*guard {
642                return Ok(v.clone());
643            }
644            let prev = mem::replace(&mut *guard, DelayState::Forced(Value::Nil));
645            let DelayState::Pending(thunk) = prev else {
646                unreachable!("state was not Pending")
647            };
648            thunk
649            // guard dropped here — lock released before forcing
650        };
651        // Force the thunk WITHOUT holding the lock so GC's lock().unwrap() in
652        // Delay::trace() will not deadlock.
653        let result = thunk.force()?;
654        *self.state.lock().unwrap() = DelayState::Forced(result.clone());
655        Ok(result)
656    }
657
658    /// True if the delay has already been forced.
659    pub fn is_realized(&self) -> bool {
660        matches!(&*self.state.lock().unwrap(), DelayState::Forced(_))
661    }
662}
663
664impl std::fmt::Debug for Delay {
665    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
666        write!(f, "Delay")
667    }
668}
669
670impl cljrs_gc::Trace for Delay {
671    fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
672        // Safe to lock unconditionally: force() drops the lock before entering
673        // eval (thunk.force()), so the lock is never held across a GC safepoint.
674        {
675            let state = self.state.lock().unwrap();
676            match &*state {
677                DelayState::Pending(thunk) => thunk.trace(visitor),
678                DelayState::Forced(v) => v.trace(visitor),
679            }
680        }
681    }
682}
683
684// ── CljxPromise ───────────────────────────────────────────────────────────────
685
686/// A one-shot rendezvous (promise).
687pub struct CljxPromise {
688    pub value: Mutex<Option<Value>>,
689    pub cond: Condvar,
690}
691
692impl CljxPromise {
693    pub fn new() -> Self {
694        Self {
695            value: Mutex::new(None),
696            cond: Condvar::new(),
697        }
698    }
699
700    /// Deliver a value (no-op if already delivered).
701    pub fn deliver(&self, v: Value) {
702        let mut guard = self.value.lock().unwrap();
703        if guard.is_none() {
704            *guard = Some(v);
705            self.cond.notify_all();
706        }
707    }
708
709    /// Block until a value is available, then return it.
710    pub fn deref_blocking(&self) -> Value {
711        let mut guard = self.value.lock().unwrap();
712        while guard.is_none() {
713            guard = self.cond.wait(guard).unwrap();
714        }
715        guard.as_ref().unwrap().clone()
716    }
717
718    /// True if already delivered.
719    pub fn is_realized(&self) -> bool {
720        self.value.lock().unwrap().is_some()
721    }
722}
723
724impl Default for CljxPromise {
725    fn default() -> Self {
726        Self::new()
727    }
728}
729
730impl std::fmt::Debug for CljxPromise {
731    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
732        write!(f, "Promise")
733    }
734}
735
736impl cljrs_gc::Trace for CljxPromise {
737    fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
738        {
739            let value = self.value.lock().unwrap();
740            if let Some(v) = value.as_ref() {
741                v.trace(visitor);
742            }
743        }
744    }
745}
746
747// ── CljxFuture ────────────────────────────────────────────────────────────────
748
749/// Thread-pool future state.
750pub enum FutureState {
751    Running,
752    Done(Value),
753    Failed(String),
754    Cancelled,
755}
756
757/// A future value computed asynchronously on another thread.
758pub struct CljxFuture {
759    pub state: Mutex<FutureState>,
760    pub cond: Condvar,
761}
762
763impl CljxFuture {
764    pub fn new() -> Self {
765        Self {
766            state: Mutex::new(FutureState::Running),
767            cond: Condvar::new(),
768        }
769    }
770
771    /// True if done, failed, or cancelled (not still running).
772    pub fn is_done(&self) -> bool {
773        !matches!(&*self.state.lock().unwrap(), FutureState::Running)
774    }
775
776    /// True if explicitly cancelled.
777    pub fn is_cancelled(&self) -> bool {
778        matches!(&*self.state.lock().unwrap(), FutureState::Cancelled)
779    }
780}
781
782impl Default for CljxFuture {
783    fn default() -> Self {
784        Self::new()
785    }
786}
787
788impl std::fmt::Debug for CljxFuture {
789    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
790        write!(f, "Future")
791    }
792}
793
794impl cljrs_gc::Trace for CljxFuture {
795    fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
796        {
797            let state = self.state.lock().unwrap();
798            if let FutureState::Done(v) = &*state {
799                v.trace(visitor);
800            }
801        }
802    }
803}
804
805// ── Agent ─────────────────────────────────────────────────────────────────────
806
807/// A Clojure agent action: takes the current state, returns the new state.
808pub type AgentFn = Box<dyn FnOnce(Value) -> Result<Value, Value> + Send>;
809
810/// Messages sent to an agent's worker thread.
811pub enum AgentMsg {
812    Update(AgentFn),
813    Shutdown,
814}
815
816/// A Clojure agent — asynchronous state update queue.
817pub struct Agent {
818    /// Current state, shared between the Value::Agent handle and the worker thread.
819    pub state: Arc<Mutex<Value>>,
820    /// Last error, shared similarly.
821    pub error: Arc<Mutex<Option<Value>>>,
822    /// Channel to send actions to the worker thread.
823    pub sender: Mutex<std::sync::mpsc::SyncSender<AgentMsg>>,
824    pub watches: Mutex<Vec<(Value, Value)>>,
825}
826
827impl Agent {
828    pub fn get_state(&self) -> Value {
829        self.state.lock().unwrap().clone()
830    }
831
832    pub fn get_error(&self) -> Option<Value> {
833        self.error.lock().unwrap().clone()
834    }
835
836    pub fn clear_error(&self) {
837        *self.error.lock().unwrap() = None;
838    }
839}
840
841impl std::fmt::Debug for Agent {
842    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
843        write!(f, "Agent")
844    }
845}
846
847impl cljrs_gc::Trace for Agent {
848    fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
849        {
850            let state = self.state.lock().unwrap();
851            state.trace(visitor);
852        }
853        {
854            let error = self.error.lock().unwrap();
855            if let Some(e) = error.as_ref() {
856                e.trace(visitor);
857            }
858        }
859        {
860            let watches = self.watches.lock().unwrap();
861            for (key, f) in watches.iter() {
862                key.trace(visitor);
863                f.trace(visitor);
864            }
865        }
866    }
867}