Skip to main content

swan_core/value/
promise.rs

1//! Single-assignment promises for Swan.
2//!
3//! A [`Promise`] is a shared, mutable cell that is either *pending* or
4//! *resolved* with a [`Value`]. Callbacks registered with [`Promise::then`]
5//! (or [`Promise::then_apply`]) run when the promise resolves and produce a
6//! new child promise holding the callback's result, so promises form a chain.
7//!
8//! Semantics:
9//! - **Resolve once.** The first call to [`Promise::resolve`] wins; later calls
10//!   are ignored.
11//! - **Never rejects.** There is no failure state. Resolving with
12//!   `Value::Undefined(_)` stores `Value::Nothing` instead.
13//! - **Synchronous dispatch.** Callbacks run immediately on the caller's stack,
14//!   either inside `resolve` (if registered while pending) or inside `then`
15//!   (if the promise is already resolved). There is no event loop or scheduler
16//!   in this module.
17//! - **Shared identity.** Cloning a `Promise` clones the `Rc` handle, not the
18//!   state; all clones observe the same resolution.
19//! - **No flattening.** If a callback returns a `Value::Promise`, the child
20//!   resolves to that promise value; it is not unwrapped.
21
22use std::cell::RefCell;
23use std::fmt;
24use std::rc::Rc;
25use crate::value::Value;
26use crate::evaluator::eval_apply;
27
28/// A Rust callback queued on a pending promise.
29///
30/// It receives the parent's resolved value and returns the value that will
31/// resolve the corresponding child promise.
32pub type PromiseCallback = Rc<dyn Fn(Value) -> Value>;
33
34/// The shared state of a [`Promise`], kept behind `Rc<RefCell<..>>`.
35#[derive(Clone)]
36pub struct PromiseData {
37    /// `None` while pending; `Some(v)` once resolved. Set at most once and
38    /// never `Value::Undefined`.
39    pub value: Option<Value>,
40
41    /// Callbacks waiting for resolution, each paired with the child
42    /// `Value::Promise` to resolve with that callback's result. Always empty
43    /// once the promise is resolved (drained by [`Promise::resolve`]).
44    pub children: Vec<(PromiseCallback, Value)>,
45}
46
47/// Prints the resolved value (if any) and only the *number* of queued
48/// children, since callbacks are opaque closures and cannot be displayed.
49impl fmt::Debug for PromiseData {
50    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51        f.debug_struct("PromiseData")
52            .field("value", &self.value)
53            .field("children_count", &self.children.len())
54            .finish()
55    }
56}
57
58/// A single-assignment value that may not be available yet.
59///
60/// A promise is *pending* until [`Promise::resolve`] gives it a value, and
61/// *resolved* afterwards. Callbacks registered with [`Promise::then`] run when
62/// it resolves.
63///
64/// - **Resolve once.** The first call to `resolve` wins; later calls are
65///   ignored.
66/// - **Never rejects.** There is no failure state. Resolving with
67///   [`Value::Undefined`] stores [`Value::Nothing`] instead.
68/// - **Synchronous dispatch.** Callbacks run on the caller's stack, inside
69///   `resolve` (if registered while pending) or inside `then` (if already
70///   resolved). There is no scheduler in this crate.
71/// - **Shared identity.** `Promise` is a cheap handle: clones observe the same
72///   resolution.
73/// - **No flattening.** If a callback returns a `Value::Promise`, the child
74///   promise resolves to that promise; it is not unwrapped.
75///
76/// The inner `Rc<RefCell<PromiseData>>` is public so that hosts can inspect
77/// the state; prefer `then` and `resolve` to change it.
78///
79/// # Examples
80///
81/// ```
82/// use swan_core::value::{Promise, Value};
83///
84/// let p = Promise::new();
85/// let child = p.then(|v| match v {
86///     Value::Numb(n) => Value::Numb(n + 1.0),
87///     other => other,
88/// });
89/// p.resolve(Value::Numb(41.0));
90///
91/// if let Value::Promise(child) = child {
92///     assert_eq!(child.0.borrow().value, Some(Value::Numb(42.0)));
93/// }
94/// ```
95#[derive(Clone, Debug)]
96pub struct Promise(pub Rc<RefCell<PromiseData>>);
97
98impl Promise {
99    /// Creates a new pending promise with no value and no queued callbacks.
100    pub fn new() -> Self {
101        Self(Rc::new(RefCell::new(PromiseData {
102            value: None,
103            children: Vec::new(),
104        })))
105    }
106
107    /// Registers a Rust callback and returns a child promise (as `Value::Promise`)
108    /// that resolves to the callback's result.
109    ///
110    /// - If `self` is already resolved, `f` is called immediately with a clone of
111    ///   the value and the returned child is already resolved.
112    /// - If `self` is pending, `f` is queued and runs when `self` is resolved.
113    ///
114    /// `f` is invoked at most once. If `f` returns `Value::Undefined`, the child
115    /// resolves to `Value::Nothing` (see [`Promise::resolve`]).
116    pub fn then(&self, f: impl Fn(Value) -> Value + 'static) -> Value {
117        let callback: PromiseCallback = Rc::new(f);
118        let child = Promise::new();
119        let child_val = Value::Promise(child.clone());
120
121        let resolved = self.0.borrow().value.clone();
122        match resolved {
123            Some(v) => child.resolve(callback(v)),
124            None => self.0.borrow_mut().children.push((callback, child_val.clone())),
125        }
126
127        child_val
128    }
129
130    /// Like [`Promise::then`], but takes a Swan callable instead of a Rust closure.
131    ///
132    /// The callable `f` is applied to the resolved value via
133    /// [`eval_apply`], with `pos` as the source position used in any resulting
134    /// `Undefined` (for example, when `f` is not callable). Such an `Undefined`
135    /// result resolves the child to `Value::Nothing`.
136    pub fn then_apply(&self, f: Value, pos: usize) -> Value {
137        self.then(move |v| eval_apply(f.clone(), v, pos))
138    }
139
140    /// Resolves this promise with `v` and runs all queued callbacks.
141    ///
142    /// - Only the first call has any effect; if the promise is already resolved,
143    ///   `v` is ignored and nothing runs.
144    /// - `Value::Undefined(_)` is replaced by `Value::Nothing`, so a promise never
145    ///   holds an undefined value.
146    /// - Each queued callback is called in registration order with a clone of the
147    ///   resolved value, and its result resolves the associated child promise.
148    ///   Resolution therefore cascades down the chain synchronously.
149    /// - The internal borrow is released before callbacks run, so callbacks may
150    ///   safely call `then` on this promise (they will see it already resolved).
151    pub fn resolve(&self, v: Value) {
152        let mut inner = self.0.borrow_mut();
153
154        // Promises resolve only once
155        if inner.value.is_some() {
156            return;
157        }
158
159        // A swan promise never rejects; undefined values resolve to Nothing
160        let resolve_val = if matches!(v, Value::Undefined(_)) {
161            Value::Nothing
162        } else {
163            v
164        };
165
166        inner.value = Some(resolve_val.clone());
167
168        // Drain children to drop mutable borrow on inner before invoking closures
169        let children = std::mem::take(&mut inner.children);
170        drop(inner);
171
172        for (f, child_val) in children {
173            let res = f(resolve_val.clone());
174            if let Value::Promise(child) = child_val {
175                child.resolve(res);
176            }
177        }
178    }
179}