Skip to main content

sim_lib_sequence/
lazy.rs

1use std::sync::{Arc, Mutex};
2
3use sim_kernel::{
4    CORE_SEQUENCE_CLASS_ID, ClassRef, Cx, Error, ListSequence, Object, ObjectCompat, Result,
5    Sequence, SequenceItem, Symbol, Value, seq_next_value,
6};
7
8/// Element generator backing a [`LazySequence`].
9///
10/// Called with the zero-based index of the next element; returns `Some(value)`
11/// to yield it or `None` to signal exhaustion.
12pub type SequenceProducer =
13    Arc<dyn Fn(&mut Cx, usize) -> Result<Option<Value>> + Send + Sync + 'static>;
14
15#[sim_citizen_derive::non_citizen(
16    reason = "live lazy sequence producer; reconstruct from the source sequence expression",
17    kind = "handle",
18    descriptor = "core/Sequence"
19)]
20/// On-demand sequence object driven by a [`SequenceProducer`].
21///
22/// Implements the kernel [`Sequence`] contract over a closure: elements are
23/// pulled lazily and cached for peeking, so the kernel sequence protocol drives
24/// arbitrary generated data without materializing it.
25#[derive(Clone)]
26pub struct LazySequence {
27    producer: SequenceProducer,
28    state: Arc<Mutex<LazySequenceState>>,
29}
30
31#[derive(Debug, Default)]
32struct LazySequenceState {
33    index: usize,
34    pending: Option<SequenceItem>,
35    done: bool,
36    closed: bool,
37}
38
39impl LazySequence {
40    /// Build a lazy sequence from an element producer.
41    pub fn new(producer: SequenceProducer) -> Self {
42        Self {
43            producer,
44            state: Arc::new(Mutex::new(LazySequenceState::default())),
45        }
46    }
47
48    fn produce(&self, cx: &mut Cx) -> Result<Option<SequenceItem>> {
49        let index = {
50            let mut state = self
51                .state
52                .lock()
53                .map_err(|_| Error::PoisonedLock("lazy sequence"))?;
54            if state.closed || state.done {
55                return Ok(None);
56            }
57            let index = state.index;
58            state.index += 1;
59            index
60        };
61        match (self.producer)(cx, index)? {
62            Some(value) => Ok(Some(SequenceItem::new(value))),
63            None => {
64                self.mark_done()?;
65                Ok(None)
66            }
67        }
68    }
69
70    fn mark_done(&self) -> Result<()> {
71        let mut state = self
72            .state
73            .lock()
74            .map_err(|_| Error::PoisonedLock("lazy sequence"))?;
75        state.done = true;
76        Ok(())
77    }
78}
79
80impl Object for LazySequence {
81    fn display(&self, _cx: &mut Cx) -> Result<String> {
82        Ok("#<lazy-sequence>".to_owned())
83    }
84
85    fn as_any(&self) -> &dyn std::any::Any {
86        self
87    }
88}
89
90impl ObjectCompat for LazySequence {
91    fn class(&self, cx: &mut Cx) -> Result<ClassRef> {
92        cx.factory().class_stub(
93            CORE_SEQUENCE_CLASS_ID,
94            Symbol::qualified("core", "Sequence"),
95        )
96    }
97
98    fn as_sequence(&self) -> Option<&dyn Sequence> {
99        Some(self)
100    }
101}
102
103impl Sequence for LazySequence {
104    fn next_item(&self, cx: &mut Cx) -> Result<Option<SequenceItem>> {
105        {
106            let mut state = self
107                .state
108                .lock()
109                .map_err(|_| Error::PoisonedLock("lazy sequence"))?;
110            if state.closed {
111                return Ok(None);
112            }
113            if let Some(item) = state.pending.take() {
114                return Ok(Some(item));
115            }
116        }
117        self.produce(cx)
118    }
119
120    fn close(&self, _cx: &mut Cx) -> Result<()> {
121        let mut state = self
122            .state
123            .lock()
124            .map_err(|_| Error::PoisonedLock("lazy sequence"))?;
125        state.pending = None;
126        state.closed = true;
127        state.done = true;
128        Ok(())
129    }
130
131    fn peek_item(&self, cx: &mut Cx) -> Result<Option<SequenceItem>> {
132        {
133            let state = self
134                .state
135                .lock()
136                .map_err(|_| Error::PoisonedLock("lazy sequence"))?;
137            if state.closed {
138                return Ok(None);
139            }
140            if let Some(item) = state.pending.clone() {
141                return Ok(Some(item));
142            }
143        }
144        let item = self.produce(cx)?;
145        if let Some(item) = item.clone() {
146            let mut state = self
147                .state
148                .lock()
149                .map_err(|_| Error::PoisonedLock("lazy sequence"))?;
150            state.pending = Some(item);
151        }
152        Ok(item)
153    }
154
155    fn is_done(&self, _cx: &mut Cx) -> Result<bool> {
156        let state = self
157            .state
158            .lock()
159            .map_err(|_| Error::PoisonedLock("lazy sequence"))?;
160        Ok((state.done || state.closed) && state.pending.is_none())
161    }
162}
163
164/// Wrap a [`SequenceProducer`] as a runtime sequence [`Value`].
165///
166/// The primary entry point for constructing lazy sequences: builds a
167/// [`LazySequence`] and boxes it as an opaque kernel object.
168///
169/// # Examples
170///
171/// ```
172/// use std::sync::Arc;
173/// use sim_kernel::{Cx, DefaultFactory, NoopEvalPolicy, Symbol};
174/// use sim_lib_sequence::{force_sequence_bounded, lazy_sequence_value};
175///
176/// let mut cx = Cx::new(Arc::new(NoopEvalPolicy), Arc::new(DefaultFactory));
177/// // A producer of two elements, then exhaustion.
178/// let seq = lazy_sequence_value(&mut cx, Arc::new(|cx: &mut Cx, index| {
179///     if index >= 2 {
180///         return Ok(None);
181///     }
182///     let value = cx
183///         .factory()
184///         .number_literal(Symbol::qualified("test", "u64"), index.to_string())?;
185///     Ok(Some(value))
186/// }))?;
187///
188/// let forced = force_sequence_bounded(&mut cx, &seq, 8, "doc")?;
189/// assert_eq!(forced.len(), 2);
190/// # Ok::<(), sim_kernel::Error>(())
191/// ```
192pub fn lazy_sequence_value(cx: &mut Cx, producer: SequenceProducer) -> Result<Value> {
193    cx.factory().opaque(Arc::new(LazySequence::new(producer)))
194}
195
196/// Adapt a list [`Value`] into a sequence [`Value`].
197///
198/// Bridges the kernel list contract to the sequence contract so list data can
199/// be consumed through the sequence organ.
200pub fn sequence_from_list_value(cx: &mut Cx, list: Value) -> Result<Value> {
201    cx.factory().opaque(Arc::new(ListSequence::new(list)))
202}
203
204/// Drive a sequence to completion, refusing to exceed `max` elements.
205///
206/// Pulls up to `max` elements; if the sequence still has more, fails with
207/// `context` in the message. Guards the sequence organ against unbounded
208/// lazy producers.
209pub fn force_sequence_bounded(
210    cx: &mut Cx,
211    sequence: &Value,
212    max: usize,
213    context: &str,
214) -> Result<Vec<Value>> {
215    let mut out = Vec::new();
216    for _ in 0..max {
217        let Some(item) = seq_next_value(cx, sequence)? else {
218            return Ok(out);
219        };
220        out.push(item.into_value(cx)?);
221    }
222    if sequence_has_more(cx, sequence)? {
223        return Err(Error::Eval(format!(
224            "{context}: sequence exceeds force bound {max}"
225        )));
226    }
227    Ok(out)
228}
229
230fn sequence_has_more(cx: &mut Cx, sequence: &Value) -> Result<bool> {
231    if let Some(sequence) = sequence.object().as_sequence() {
232        return sequence.peek_item(cx).map(|item| item.is_some());
233    }
234    seq_next_value(cx, sequence).map(|item| item.is_some())
235}