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    /// Unique ID for IR cache lookup (assigned by the evaluator).
396    pub ir_arity_id: u64,
397}
398
399// ── CljxFn ────────────────────────────────────────────────────────────────────
400
401/// An interpreted Clojure closure with captured environment.
402#[derive(Debug, Clone)]
403pub struct CljxFn {
404    pub name: Option<Arc<str>>,
405    pub arities: Vec<CljxFnArity>,
406    /// Names of closed-over bindings (parallel to `closed_over_vals`).
407    pub closed_over_names: Vec<Arc<str>>,
408    /// Values of closed-over bindings (parallel to `closed_over_names`).
409    pub closed_over_vals: Vec<Value>,
410    /// True if this function was defined with `defmacro`.
411    pub is_macro: bool,
412    /// Namespace in which this function was defined (for macro hygiene).
413    pub defining_ns: Arc<str>,
414}
415
416impl CljxFn {
417    pub fn new(
418        name: Option<Arc<str>>,
419        arities: Vec<CljxFnArity>,
420        closed_over_names: Vec<Arc<str>>,
421        closed_over_vals: Vec<Value>,
422        is_macro: bool,
423        defining_ns: Arc<str>,
424    ) -> Self {
425        Self {
426            name,
427            arities,
428            closed_over_names,
429            closed_over_vals,
430            is_macro,
431            defining_ns,
432        }
433    }
434}
435
436impl cljrs_gc::Trace for CljxFn {
437    fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
438        for v in &self.closed_over_vals {
439            v.trace(visitor);
440        }
441    }
442}
443
444// ── BoundFn ──────────────────────────────────────────────────────────────────
445
446/// A function wrapped with captured dynamic bindings.
447/// When called, the captured bindings are pushed as a frame before delegating
448/// to the wrapped function. This means captured bindings override the caller's
449/// for the same var, but vars not in the capture fall through normally.
450#[derive(Debug)]
451pub struct BoundFn {
452    /// The wrapped callable.
453    pub wrapped: Value,
454    /// Captured dynamic bindings (merged flat frame; opaque to cljrs-value).
455    pub captured_bindings: HashMap<usize, Value>,
456}
457
458impl cljrs_gc::Trace for BoundFn {
459    fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
460        self.wrapped.trace(visitor);
461        for val in self.captured_bindings.values() {
462            val.trace(visitor);
463        }
464    }
465}
466
467// ── Thunk / LazySeq ───────────────────────────────────────────────────────────
468
469/// A deferred computation that produces a `Value` when forced.
470pub trait Thunk: Send + Sync + std::fmt::Debug + cljrs_gc::Trace {
471    fn force(&self) -> Result<Value, String>;
472}
473
474/// Internal state of a lazy sequence cell.
475pub enum LazySeqState {
476    /// Thunk not yet evaluated.
477    Pending(Box<dyn Thunk>),
478    /// Result cached after first force.
479    Forced(Value),
480    /// Thunk evaluation failed; error message is cached.
481    Error(String),
482}
483
484/// A lazy sequence that forces its thunk exactly once and caches the result.
485pub struct LazySeq {
486    pub state: Mutex<LazySeqState>,
487}
488
489impl LazySeq {
490    pub fn new(thunk: Box<dyn Thunk>) -> Self {
491        Self {
492            state: Mutex::new(LazySeqState::Pending(thunk)),
493        }
494    }
495
496    /// Realize the sequence: force the thunk on first call, return cached value on subsequent calls.
497    /// On error, returns `Value::Nil` and caches the error (retrievable via `error()`).
498    pub fn realize(&self) -> Value {
499        let thunk = {
500            let mut guard = self.state.lock().unwrap();
501            match &*guard {
502                LazySeqState::Forced(v) => return v.clone(),
503                LazySeqState::Error(_) => return Value::Nil,
504                LazySeqState::Pending(_) => {}
505            }
506            // Replace the pending state with a temporary Forced(Nil), extract the thunk.
507            let prev = mem::replace(&mut *guard, LazySeqState::Forced(Value::Nil));
508            let LazySeqState::Pending(thunk) = prev else {
509                unreachable!("state was not Pending")
510            };
511            thunk
512            // guard dropped here — lock released before forcing
513        };
514        // Force the thunk WITHOUT holding the lock. This ensures GC's
515        // lock().unwrap() in LazySeq::trace() will not deadlock.
516        match thunk.force() {
517            Ok(result) => {
518                *self.state.lock().unwrap() = LazySeqState::Forced(result.clone());
519                result
520            }
521            Err(msg) => {
522                *self.state.lock().unwrap() = LazySeqState::Error(msg);
523                Value::Nil
524            }
525        }
526    }
527
528    /// Return the cached error message, if the thunk failed.
529    pub fn error(&self) -> Option<String> {
530        let guard = self.state.lock().unwrap();
531        if let LazySeqState::Error(e) = &*guard {
532            Some(e.clone())
533        } else {
534            None
535        }
536    }
537}
538
539impl std::fmt::Debug for LazySeq {
540    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
541        write!(f, "LazySeq(...)")
542    }
543}
544
545impl cljrs_gc::Trace for LazySeq {
546    fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
547        // Safe to lock unconditionally: realize() drops the lock before entering
548        // eval (thunk.force()), so the lock is never held across a GC safepoint.
549        {
550            let state = self.state.lock().unwrap();
551            match &*state {
552                LazySeqState::Pending(thunk) => thunk.trace(visitor),
553                LazySeqState::Forced(v) => v.trace(visitor),
554                LazySeqState::Error(_) => {}
555            }
556        }
557    }
558}
559
560// ── CljxCons ──────────────────────────────────────────────────────────────────
561
562/// A lazy cons cell: head element + tail (may be a `LazySeq`, `List`, or `Nil`).
563///
564/// Used when `cons` is called with a `LazySeq` or `Cons` tail, enabling lazy
565/// sequences without eagerly realizing them.
566#[derive(Debug, Clone)]
567pub struct CljxCons {
568    pub head: Value,
569    pub tail: Value,
570}
571
572impl cljrs_gc::Trace for CljxCons {
573    fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
574        self.head.trace(visitor);
575        self.tail.trace(visitor);
576    }
577}
578
579// ── Volatile ──────────────────────────────────────────────────────────────────
580
581/// Non-atomic mutable cell (single-thread performance, no CAS).
582pub struct Volatile {
583    pub value: Mutex<Value>,
584}
585
586impl Volatile {
587    pub fn new(v: Value) -> Self {
588        Self {
589            value: Mutex::new(v),
590        }
591    }
592
593    pub fn deref(&self) -> Value {
594        self.value.lock().unwrap().clone()
595    }
596
597    pub fn reset(&self, v: Value) -> Value {
598        *self.value.lock().unwrap() = v.clone();
599        v
600    }
601}
602
603impl std::fmt::Debug for Volatile {
604    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
605        write!(f, "Volatile")
606    }
607}
608
609impl cljrs_gc::Trace for Volatile {
610    fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
611        {
612            let value = self.value.lock().unwrap();
613            value.trace(visitor);
614        }
615    }
616}
617
618// ── Delay ─────────────────────────────────────────────────────────────────────
619
620/// Internal state of a delay cell.
621pub enum DelayState {
622    Pending(Box<dyn Thunk>),
623    Forced(Value),
624}
625
626/// A lazy one-time computation (forced at most once, result cached).
627pub struct Delay {
628    pub state: Mutex<DelayState>,
629}
630
631impl Delay {
632    pub fn new(thunk: Box<dyn Thunk>) -> Self {
633        Self {
634            state: Mutex::new(DelayState::Pending(thunk)),
635        }
636    }
637
638    /// Force the delay and cache the result.
639    /// Returns the value on success, or an error message on failure.
640    pub fn force(&self) -> Result<Value, String> {
641        let thunk = {
642            let mut guard = self.state.lock().unwrap();
643            if let DelayState::Forced(v) = &*guard {
644                return Ok(v.clone());
645            }
646            let prev = mem::replace(&mut *guard, DelayState::Forced(Value::Nil));
647            let DelayState::Pending(thunk) = prev else {
648                unreachable!("state was not Pending")
649            };
650            thunk
651            // guard dropped here — lock released before forcing
652        };
653        // Force the thunk WITHOUT holding the lock so GC's lock().unwrap() in
654        // Delay::trace() will not deadlock.
655        let result = thunk.force()?;
656        *self.state.lock().unwrap() = DelayState::Forced(result.clone());
657        Ok(result)
658    }
659
660    /// True if the delay has already been forced.
661    pub fn is_realized(&self) -> bool {
662        matches!(&*self.state.lock().unwrap(), DelayState::Forced(_))
663    }
664}
665
666impl std::fmt::Debug for Delay {
667    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
668        write!(f, "Delay")
669    }
670}
671
672impl cljrs_gc::Trace for Delay {
673    fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
674        // Safe to lock unconditionally: force() drops the lock before entering
675        // eval (thunk.force()), so the lock is never held across a GC safepoint.
676        {
677            let state = self.state.lock().unwrap();
678            match &*state {
679                DelayState::Pending(thunk) => thunk.trace(visitor),
680                DelayState::Forced(v) => v.trace(visitor),
681            }
682        }
683    }
684}
685
686// ── CljxPromise ───────────────────────────────────────────────────────────────
687
688/// A one-shot rendezvous (promise).
689pub struct CljxPromise {
690    pub value: Mutex<Option<Value>>,
691    pub cond: Condvar,
692}
693
694impl CljxPromise {
695    pub fn new() -> Self {
696        Self {
697            value: Mutex::new(None),
698            cond: Condvar::new(),
699        }
700    }
701
702    /// Deliver a value (no-op if already delivered).
703    pub fn deliver(&self, v: Value) {
704        let mut guard = self.value.lock().unwrap();
705        if guard.is_none() {
706            *guard = Some(v);
707            self.cond.notify_all();
708        }
709    }
710
711    /// Block until a value is available, then return it.
712    pub fn deref_blocking(&self) -> Value {
713        let mut guard = self.value.lock().unwrap();
714        while guard.is_none() {
715            guard = self.cond.wait(guard).unwrap();
716        }
717        guard.as_ref().unwrap().clone()
718    }
719
720    /// True if already delivered.
721    pub fn is_realized(&self) -> bool {
722        self.value.lock().unwrap().is_some()
723    }
724}
725
726impl Default for CljxPromise {
727    fn default() -> Self {
728        Self::new()
729    }
730}
731
732impl std::fmt::Debug for CljxPromise {
733    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
734        write!(f, "Promise")
735    }
736}
737
738impl cljrs_gc::Trace for CljxPromise {
739    fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
740        {
741            let value = self.value.lock().unwrap();
742            if let Some(v) = value.as_ref() {
743                v.trace(visitor);
744            }
745        }
746    }
747}
748
749// ── CljxFuture ────────────────────────────────────────────────────────────────
750
751/// Thread-pool future state.
752pub enum FutureState {
753    Running,
754    Done(Value),
755    Failed(String),
756    Cancelled,
757}
758
759/// A future value computed asynchronously on another thread.
760pub struct CljxFuture {
761    pub state: Mutex<FutureState>,
762    pub cond: Condvar,
763}
764
765impl CljxFuture {
766    pub fn new() -> Self {
767        Self {
768            state: Mutex::new(FutureState::Running),
769            cond: Condvar::new(),
770        }
771    }
772
773    /// True if done, failed, or cancelled (not still running).
774    pub fn is_done(&self) -> bool {
775        !matches!(&*self.state.lock().unwrap(), FutureState::Running)
776    }
777
778    /// True if explicitly cancelled.
779    pub fn is_cancelled(&self) -> bool {
780        matches!(&*self.state.lock().unwrap(), FutureState::Cancelled)
781    }
782}
783
784impl Default for CljxFuture {
785    fn default() -> Self {
786        Self::new()
787    }
788}
789
790impl std::fmt::Debug for CljxFuture {
791    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
792        write!(f, "Future")
793    }
794}
795
796impl cljrs_gc::Trace for CljxFuture {
797    fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
798        {
799            let state = self.state.lock().unwrap();
800            if let FutureState::Done(v) = &*state {
801                v.trace(visitor);
802            }
803        }
804    }
805}
806
807// ── Agent ─────────────────────────────────────────────────────────────────────
808
809/// A Clojure agent action: takes the current state, returns the new state.
810pub type AgentFn = Box<dyn FnOnce(Value) -> Result<Value, Value> + Send>;
811
812/// Messages sent to an agent's worker thread.
813pub enum AgentMsg {
814    Update(AgentFn),
815    Shutdown,
816}
817
818/// A Clojure agent — asynchronous state update queue.
819pub struct Agent {
820    /// Current state, shared between the Value::Agent handle and the worker thread.
821    pub state: Arc<Mutex<Value>>,
822    /// Last error, shared similarly.
823    pub error: Arc<Mutex<Option<Value>>>,
824    /// Channel to send actions to the worker thread.
825    pub sender: Mutex<std::sync::mpsc::SyncSender<AgentMsg>>,
826    pub watches: Mutex<Vec<(Value, Value)>>,
827}
828
829impl Agent {
830    pub fn get_state(&self) -> Value {
831        self.state.lock().unwrap().clone()
832    }
833
834    pub fn get_error(&self) -> Option<Value> {
835        self.error.lock().unwrap().clone()
836    }
837
838    pub fn clear_error(&self) {
839        *self.error.lock().unwrap() = None;
840    }
841}
842
843impl std::fmt::Debug for Agent {
844    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
845        write!(f, "Agent")
846    }
847}
848
849impl cljrs_gc::Trace for Agent {
850    fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
851        {
852            let state = self.state.lock().unwrap();
853            state.trace(visitor);
854        }
855        {
856            let error = self.error.lock().unwrap();
857            if let Some(e) = error.as_ref() {
858                e.trace(visitor);
859            }
860        }
861        {
862            let watches = self.watches.lock().unwrap();
863            for (key, f) in watches.iter() {
864                key.trace(visitor);
865                f.trace(visitor);
866            }
867        }
868    }
869}