Skip to main content

hydro_lang/live_collections/sliced/
style.rs

1//! Styled wrappers for live collections used with the `sliced!` macro.
2//!
3//! This module provides wrapper types that store both a collection and its associated
4//! non-determinism guard, allowing the nondet to be properly passed through during slicing.
5
6#[cfg(stageleft_runtime)]
7use std::marker::PhantomData;
8
9use super::Slicable;
10#[cfg(stageleft_runtime)]
11use crate::forward_handle::{CycleCollection, CycleCollectionWithInitial};
12use crate::forward_handle::{TickCycle, TickCycleHandle};
13use crate::live_collections::boundedness::{Bounded, Boundedness, Unbounded};
14use crate::live_collections::keyed_singleton::{BoundedValue, KeyedSingletonBound};
15use crate::live_collections::singleton::SingletonBound;
16use crate::live_collections::stream::{Ordering, Retries};
17use crate::location::Location;
18use crate::location::tick::{DeferTick, Tick};
19use crate::nondet::NonDet;
20
21/// Default style wrapper that stores a collection and its non-determinism guard.
22///
23/// This is used by the `sliced!` macro when no explicit style is specified. This style is
24/// deprecated; use the explicit [`batch`] or [`snapshot`] styles instead.
25pub struct Default<T> {
26    pub(crate) collection: T,
27    pub(crate) nondet: NonDet,
28}
29
30impl<T> Default<T> {
31    /// Creates a new default-styled wrapper.
32    pub fn new(collection: T, nondet: NonDet) -> Self {
33        Self { collection, nondet }
34    }
35}
36
37/// Helper function for unstyled `use` in `sliced!` macro - wraps the collection in Default style.
38#[doc(hidden)]
39#[deprecated(
40    note = "use `use::batch(...)` for stream-like collections or `use::snapshot(...)` for singleton-like collections instead"
41)]
42pub fn default<T>(t: T, nondet: NonDet) -> Default<T> {
43    Default::new(t, nondet)
44}
45
46/// Batch style wrapper that stores a stream-like collection and its non-determinism guard.
47///
48/// This is used by the `sliced!` macro when `use::batch(...)` is specified.
49pub struct Batch<T> {
50    pub(crate) collection: T,
51    pub(crate) nondet: NonDet,
52}
53
54impl<T> Batch<T> {
55    /// Creates a new batch-styled wrapper.
56    pub fn new(collection: T, nondet: NonDet) -> Self {
57        Self { collection, nondet }
58    }
59}
60
61/// Wraps a stream-like live collection (such as a [`Stream`](crate::live_collections::Stream),
62/// [`KeyedStream`](crate::live_collections::KeyedStream), or a
63/// [`KeyedSingleton`](crate::live_collections::KeyedSingleton) with bounded values) to be
64/// sliced into non-deterministic batches of asynchronously arriving elements.
65pub fn batch<T>(t: T, nondet: NonDet) -> Batch<T> {
66    Batch::new(t, nondet)
67}
68
69/// Snapshot style wrapper that stores a singleton-like collection and its non-determinism guard.
70///
71/// This is used by the `sliced!` macro when `use::snapshot(...)` is specified.
72pub struct Snapshot<T> {
73    pub(crate) collection: T,
74    pub(crate) nondet: NonDet,
75}
76
77impl<T> Snapshot<T> {
78    /// Creates a new snapshot-styled wrapper.
79    pub fn new(collection: T, nondet: NonDet) -> Self {
80        Self { collection, nondet }
81    }
82}
83
84/// Wraps a singleton-like live collection (such as a
85/// [`Singleton`](crate::live_collections::Singleton),
86/// [`Optional`](crate::live_collections::Optional), or a
87/// [`KeyedSingleton`](crate::live_collections::KeyedSingleton) with asynchronously updated
88/// values) to be sliced into non-deterministic snapshots of its continuously changing value.
89pub fn snapshot<T>(t: T, nondet: NonDet) -> Snapshot<T> {
90    Snapshot::new(t, nondet)
91}
92
93/// Atomic style wrapper that stores a collection and its non-determinism guard.
94///
95/// This is used by the `sliced!` macro when `use::atomic(...)` is specified.
96pub struct Atomic<T> {
97    pub(crate) collection: T,
98    pub(crate) nondet: NonDet,
99}
100
101impl<T> Atomic<T> {
102    /// Creates a new atomic-styled wrapper.
103    pub fn new(collection: T, nondet: NonDet) -> Self {
104        Self { collection, nondet }
105    }
106}
107
108/// Wraps a live collection to be treated atomically during slicing.
109pub fn atomic<T>(t: T, nondet: NonDet) -> Atomic<T> {
110    Atomic::new(t, nondet)
111}
112
113/// Creates a stateful cycle with an initial value for use in `sliced!`.
114///
115/// The tick (which is the source of truth for lifetimes) is bound first, returning a
116/// [`StateBuilder`] which accepts the user-provided initializer via [`StateBuilder::build`].
117/// This two-step layout ensures that type errors caused by a bad initializer are attributed
118/// to the initializer argument rather than the tick or the entire macro invocation.
119///
120/// The initial value is computed from a closure that receives the location
121/// for the body of the slice.
122///
123/// The initial value is used on the first iteration, and subsequent iterations receive
124/// the value assigned to the mutable binding at the end of the previous iteration.
125#[cfg(stageleft_runtime)]
126pub fn state<'t, S, L>(tick: &'t Tick<L>) -> StateBuilder<'t, S, L> {
127    StateBuilder {
128        tick,
129        _phantom: PhantomData,
130    }
131}
132
133/// Builder returned by [`state`], which accepts the user-provided initializer.
134#[cfg(stageleft_runtime)]
135pub struct StateBuilder<'t, S, L> {
136    tick: &'t Tick<L>,
137    _phantom: PhantomData<S>,
138}
139
140#[cfg(stageleft_runtime)]
141impl<'t, 'a, S, L: Location<'a>> StateBuilder<'t, S, L> {
142    /// Supplies the initializer closure and creates the stateful cycle.
143    ///
144    /// The initializer takes the tick at the builder's `'t` lifetime (rather than a
145    /// higher-ranked `for<'x>` bound), since the builder already stores the tick reference.
146    /// This way, an initializer that requires a specific tick reference lifetime produces a
147    /// borrow error directly on the tick, instead of a confusing "implementation of `Fn` is
148    /// not general enough" error that blames an unrelated variable.
149    #[expect(
150        private_bounds,
151        reason = "only Hydro collections can implement CycleCollectionWithInitial"
152    )]
153    pub fn build(self, initial_fn: impl FnOnce(&'t Tick<L>) -> S) -> (TickCycleHandle<'a, S>, S)
154    where
155        S: CycleCollectionWithInitial<'a, TickCycle, Location = Tick<L::DropConsistency>>,
156    {
157        let initial = initial_fn(self.tick);
158        initial.location().clone().cycle_with_initial(initial)
159    }
160}
161
162/// Creates a stateful cycle without an initial value for use in `sliced!`.
163///
164/// The tick (which is the source of truth for lifetimes) is bound first, returning a
165/// [`StateNullBuilder`] which creates the cycle via [`StateNullBuilder::build`].
166///
167/// On the first iteration, the state will be null/empty. Subsequent iterations receive
168/// the value assigned to the mutable binding at the end of the previous iteration.
169#[cfg(stageleft_runtime)]
170pub fn state_null<'t, S, L>(tick: &'t Tick<L>) -> StateNullBuilder<'t, S, L> {
171    StateNullBuilder {
172        tick,
173        _phantom: PhantomData,
174    }
175}
176
177/// Builder returned by [`state_null`], which creates the cycle.
178#[cfg(stageleft_runtime)]
179pub struct StateNullBuilder<'t, S, L> {
180    tick: &'t Tick<L>,
181    _phantom: PhantomData<S>,
182}
183
184#[cfg(stageleft_runtime)]
185impl<'t, 'a, S, L: Location<'a>> StateNullBuilder<'t, S, L> {
186    /// Creates the stateful cycle, which starts as null/empty on the first iteration.
187    #[expect(
188        private_bounds,
189        reason = "only Hydro collections can implement CycleCollection"
190    )]
191    pub fn build(self) -> (TickCycleHandle<'a, S>, S)
192    where
193        S: CycleCollection<'a, TickCycle, Location = Tick<L::DropConsistency>> + DeferTick,
194    {
195        self.tick.cycle::<S, _>()
196    }
197}
198
199// ============================================================================
200// Default style Slicable implementations
201//
202// All of these drop consistency because they are performing non-deterministic
203// batching / snapshotting.
204// ============================================================================
205
206impl<'a, T, L: Location<'a>, B: Boundedness, O: Ordering, R: Retries>
207    Slicable<'a, L::DropConsistency> for Default<crate::live_collections::Stream<T, L, B, O, R>>
208{
209    type Slice = crate::live_collections::Stream<T, Tick<L::DropConsistency>, Bounded, O, R>;
210    type Backtrace = crate::compile::ir::backtrace::Backtrace;
211
212    fn get_location(&self) -> L::DropConsistency {
213        self.collection.location().drop_consistency()
214    }
215    fn slice(self, tick: &Tick<L::DropConsistency>, backtrace: Self::Backtrace) -> Self::Slice {
216        let out = self.collection.batch(tick, self.nondet);
217        out.ir_node.borrow_mut().op_metadata_mut().backtrace = backtrace;
218        out
219    }
220}
221
222impl<'a, T, L: Location<'a>, B: SingletonBound> Slicable<'a, L::DropConsistency>
223    for Default<crate::live_collections::Singleton<T, L, B>>
224{
225    type Slice = crate::live_collections::Singleton<T, Tick<L::DropConsistency>, Bounded>;
226    type Backtrace = crate::compile::ir::backtrace::Backtrace;
227
228    fn get_location(&self) -> L::DropConsistency {
229        self.collection.location().drop_consistency()
230    }
231    fn slice(self, tick: &Tick<L::DropConsistency>, backtrace: Self::Backtrace) -> Self::Slice {
232        let out = self.collection.snapshot(tick, self.nondet);
233        out.ir_node.borrow_mut().op_metadata_mut().backtrace = backtrace;
234        out
235    }
236}
237
238impl<'a, T, L: Location<'a>, B: Boundedness> Slicable<'a, L::DropConsistency>
239    for Default<crate::live_collections::Optional<T, L, B>>
240{
241    type Slice = crate::live_collections::Optional<T, Tick<L::DropConsistency>, Bounded>;
242    type Backtrace = crate::compile::ir::backtrace::Backtrace;
243
244    fn get_location(&self) -> L::DropConsistency {
245        self.collection.location().drop_consistency()
246    }
247    fn slice(self, tick: &Tick<L::DropConsistency>, backtrace: Self::Backtrace) -> Self::Slice {
248        let out = self.collection.snapshot(tick, self.nondet);
249        out.ir_node.borrow_mut().op_metadata_mut().backtrace = backtrace;
250        out
251    }
252}
253
254impl<'a, K, V, L: Location<'a>, B: Boundedness, O: Ordering, R: Retries>
255    Slicable<'a, L::DropConsistency>
256    for Default<crate::live_collections::KeyedStream<K, V, L, B, O, R>>
257{
258    type Slice =
259        crate::live_collections::KeyedStream<K, V, Tick<L::DropConsistency>, Bounded, O, R>;
260    type Backtrace = crate::compile::ir::backtrace::Backtrace;
261
262    fn get_location(&self) -> L::DropConsistency {
263        self.collection.location().drop_consistency()
264    }
265    fn slice(self, tick: &Tick<L::DropConsistency>, backtrace: Self::Backtrace) -> Self::Slice {
266        let out = self.collection.batch(tick, self.nondet);
267        out.ir_node.borrow_mut().op_metadata_mut().backtrace = backtrace;
268        out
269    }
270}
271
272impl<'a, K, V, L: Location<'a>, B: KeyedSingletonBound<ValueBound = Unbounded>>
273    Slicable<'a, L::DropConsistency>
274    for Default<crate::live_collections::KeyedSingleton<K, V, L, B>>
275{
276    type Slice = crate::live_collections::KeyedSingleton<K, V, Tick<L::DropConsistency>, Bounded>;
277    type Backtrace = crate::compile::ir::backtrace::Backtrace;
278
279    fn get_location(&self) -> L::DropConsistency {
280        self.collection.location().drop_consistency()
281    }
282    fn slice(self, tick: &Tick<L::DropConsistency>, backtrace: Self::Backtrace) -> Self::Slice {
283        let out = self.collection.snapshot(tick, self.nondet);
284        out.ir_node.borrow_mut().op_metadata_mut().backtrace = backtrace;
285        out
286    }
287}
288
289impl<'a, K, V, L: Location<'a>> Slicable<'a, L::DropConsistency>
290    for Default<crate::live_collections::KeyedSingleton<K, V, L, BoundedValue>>
291{
292    type Slice = crate::live_collections::KeyedSingleton<K, V, Tick<L::DropConsistency>, Bounded>;
293    type Backtrace = crate::compile::ir::backtrace::Backtrace;
294
295    fn get_location(&self) -> L::DropConsistency {
296        self.collection.location().drop_consistency()
297    }
298    fn slice(self, tick: &Tick<L::DropConsistency>, backtrace: Self::Backtrace) -> Self::Slice {
299        let out = self.collection.batch(tick, self.nondet);
300        out.ir_node.borrow_mut().op_metadata_mut().backtrace = backtrace;
301        out
302    }
303}
304
305// ============================================================================
306// Batch style Slicable implementations (stream-like collections)
307//
308// All of these drop consistency because they are performing non-deterministic
309// batching.
310// ============================================================================
311
312impl<'a, T, L: Location<'a>, B: Boundedness, O: Ordering, R: Retries>
313    Slicable<'a, L::DropConsistency> for Batch<crate::live_collections::Stream<T, L, B, O, R>>
314{
315    type Slice = crate::live_collections::Stream<T, Tick<L::DropConsistency>, Bounded, O, R>;
316    type Backtrace = crate::compile::ir::backtrace::Backtrace;
317
318    fn get_location(&self) -> L::DropConsistency {
319        self.collection.location().drop_consistency()
320    }
321    fn slice(self, tick: &Tick<L::DropConsistency>, backtrace: Self::Backtrace) -> Self::Slice {
322        let out = self.collection.batch(tick, self.nondet);
323        out.ir_node.borrow_mut().op_metadata_mut().backtrace = backtrace;
324        out
325    }
326}
327
328impl<'a, K, V, L: Location<'a>, B: Boundedness, O: Ordering, R: Retries>
329    Slicable<'a, L::DropConsistency>
330    for Batch<crate::live_collections::KeyedStream<K, V, L, B, O, R>>
331{
332    type Slice =
333        crate::live_collections::KeyedStream<K, V, Tick<L::DropConsistency>, Bounded, O, R>;
334    type Backtrace = crate::compile::ir::backtrace::Backtrace;
335
336    fn get_location(&self) -> L::DropConsistency {
337        self.collection.location().drop_consistency()
338    }
339    fn slice(self, tick: &Tick<L::DropConsistency>, backtrace: Self::Backtrace) -> Self::Slice {
340        let out = self.collection.batch(tick, self.nondet);
341        out.ir_node.borrow_mut().op_metadata_mut().backtrace = backtrace;
342        out
343    }
344}
345
346impl<'a, K, V, L: Location<'a>> Slicable<'a, L::DropConsistency>
347    for Batch<crate::live_collections::KeyedSingleton<K, V, L, BoundedValue>>
348{
349    type Slice = crate::live_collections::KeyedSingleton<K, V, Tick<L::DropConsistency>, Bounded>;
350    type Backtrace = crate::compile::ir::backtrace::Backtrace;
351
352    fn get_location(&self) -> L::DropConsistency {
353        self.collection.location().drop_consistency()
354    }
355    fn slice(self, tick: &Tick<L::DropConsistency>, backtrace: Self::Backtrace) -> Self::Slice {
356        let out = self.collection.batch(tick, self.nondet);
357        out.ir_node.borrow_mut().op_metadata_mut().backtrace = backtrace;
358        out
359    }
360}
361
362// ============================================================================
363// Snapshot style Slicable implementations (singleton-like collections)
364//
365// All of these drop consistency because they are performing non-deterministic
366// snapshotting.
367// ============================================================================
368
369impl<'a, T, L: Location<'a>, B: SingletonBound> Slicable<'a, L::DropConsistency>
370    for Snapshot<crate::live_collections::Singleton<T, L, B>>
371{
372    type Slice = crate::live_collections::Singleton<T, Tick<L::DropConsistency>, Bounded>;
373    type Backtrace = crate::compile::ir::backtrace::Backtrace;
374
375    fn get_location(&self) -> L::DropConsistency {
376        self.collection.location().drop_consistency()
377    }
378    fn slice(self, tick: &Tick<L::DropConsistency>, backtrace: Self::Backtrace) -> Self::Slice {
379        let out = self.collection.snapshot(tick, self.nondet);
380        out.ir_node.borrow_mut().op_metadata_mut().backtrace = backtrace;
381        out
382    }
383}
384
385impl<'a, T, L: Location<'a>, B: Boundedness> Slicable<'a, L::DropConsistency>
386    for Snapshot<crate::live_collections::Optional<T, L, B>>
387{
388    type Slice = crate::live_collections::Optional<T, Tick<L::DropConsistency>, Bounded>;
389    type Backtrace = crate::compile::ir::backtrace::Backtrace;
390
391    fn get_location(&self) -> L::DropConsistency {
392        self.collection.location().drop_consistency()
393    }
394    fn slice(self, tick: &Tick<L::DropConsistency>, backtrace: Self::Backtrace) -> Self::Slice {
395        let out = self.collection.snapshot(tick, self.nondet);
396        out.ir_node.borrow_mut().op_metadata_mut().backtrace = backtrace;
397        out
398    }
399}
400
401impl<'a, K, V, L: Location<'a>, B: KeyedSingletonBound<ValueBound = Unbounded>>
402    Slicable<'a, L::DropConsistency>
403    for Snapshot<crate::live_collections::KeyedSingleton<K, V, L, B>>
404{
405    type Slice = crate::live_collections::KeyedSingleton<K, V, Tick<L::DropConsistency>, Bounded>;
406    type Backtrace = crate::compile::ir::backtrace::Backtrace;
407
408    fn get_location(&self) -> L::DropConsistency {
409        self.collection.location().drop_consistency()
410    }
411    fn slice(self, tick: &Tick<L::DropConsistency>, backtrace: Self::Backtrace) -> Self::Slice {
412        let out = self.collection.snapshot(tick, self.nondet);
413        out.ir_node.borrow_mut().op_metadata_mut().backtrace = backtrace;
414        out
415    }
416}
417
418// ============================================================================
419// Atomic style Slicable implementations
420// ============================================================================
421
422impl<'a, T, L: Location<'a>, B: Boundedness, O: Ordering, R: Retries>
423    Slicable<'a, L::DropConsistency>
424    for Atomic<crate::live_collections::Stream<T, crate::location::Atomic<L>, B, O, R>>
425{
426    type Slice = crate::live_collections::Stream<T, Tick<L::DropConsistency>, Bounded, O, R>;
427    type Backtrace = crate::compile::ir::backtrace::Backtrace;
428    fn get_location(&self) -> L::DropConsistency {
429        self.collection.location().tick.l.drop_consistency()
430    }
431
432    fn slice(self, tick: &Tick<L::DropConsistency>, backtrace: Self::Backtrace) -> Self::Slice {
433        let out = self.collection.batch_atomic(tick, self.nondet);
434        out.ir_node.borrow_mut().op_metadata_mut().backtrace = backtrace;
435        out
436    }
437}
438
439impl<'a, T, L: Location<'a>, B: SingletonBound> Slicable<'a, L::DropConsistency>
440    for Atomic<crate::live_collections::Singleton<T, crate::location::Atomic<L>, B>>
441{
442    type Slice = crate::live_collections::Singleton<T, Tick<L::DropConsistency>, Bounded>;
443    type Backtrace = crate::compile::ir::backtrace::Backtrace;
444    fn get_location(&self) -> L::DropConsistency {
445        self.collection.location().tick.l.drop_consistency()
446    }
447
448    fn slice(self, tick: &Tick<L::DropConsistency>, backtrace: Self::Backtrace) -> Self::Slice {
449        let out = self.collection.snapshot_atomic(tick, self.nondet);
450        out.ir_node.borrow_mut().op_metadata_mut().backtrace = backtrace;
451        out
452    }
453}
454
455impl<'a, T, L: Location<'a>, B: Boundedness> Slicable<'a, L::DropConsistency>
456    for Atomic<crate::live_collections::Optional<T, crate::location::Atomic<L>, B>>
457{
458    type Slice = crate::live_collections::Optional<T, Tick<L::DropConsistency>, Bounded>;
459    type Backtrace = crate::compile::ir::backtrace::Backtrace;
460    fn get_location(&self) -> L::DropConsistency {
461        self.collection.location().tick.l.drop_consistency()
462    }
463
464    fn slice(self, tick: &Tick<L::DropConsistency>, backtrace: Self::Backtrace) -> Self::Slice {
465        let out = self.collection.snapshot_atomic(tick, self.nondet);
466        out.ir_node.borrow_mut().op_metadata_mut().backtrace = backtrace;
467        out
468    }
469}
470
471impl<'a, K, V, L: Location<'a>, B: Boundedness, O: Ordering, R: Retries>
472    Slicable<'a, L::DropConsistency>
473    for Atomic<crate::live_collections::KeyedStream<K, V, crate::location::Atomic<L>, B, O, R>>
474{
475    type Slice =
476        crate::live_collections::KeyedStream<K, V, Tick<L::DropConsistency>, Bounded, O, R>;
477    type Backtrace = crate::compile::ir::backtrace::Backtrace;
478    fn get_location(&self) -> L::DropConsistency {
479        self.collection.location().tick.l.drop_consistency()
480    }
481
482    fn slice(self, tick: &Tick<L::DropConsistency>, backtrace: Self::Backtrace) -> Self::Slice {
483        let out = self.collection.batch_atomic(tick, self.nondet);
484        out.ir_node.borrow_mut().op_metadata_mut().backtrace = backtrace;
485        out
486    }
487}
488
489impl<'a, K, V, L: Location<'a>, B: KeyedSingletonBound<ValueBound = Unbounded>>
490    Slicable<'a, L::DropConsistency>
491    for Atomic<crate::live_collections::KeyedSingleton<K, V, crate::location::Atomic<L>, B>>
492{
493    type Slice = crate::live_collections::KeyedSingleton<K, V, Tick<L::DropConsistency>, Bounded>;
494    type Backtrace = crate::compile::ir::backtrace::Backtrace;
495    fn get_location(&self) -> L::DropConsistency {
496        self.collection.location().tick.l.drop_consistency()
497    }
498
499    fn slice(self, tick: &Tick<L::DropConsistency>, backtrace: Self::Backtrace) -> Self::Slice {
500        let out = self.collection.snapshot_atomic(tick, self.nondet);
501        out.ir_node.borrow_mut().op_metadata_mut().backtrace = backtrace;
502        out
503    }
504}
505
506impl<'a, K, V, L: Location<'a>> Slicable<'a, L::DropConsistency>
507    for Atomic<
508        crate::live_collections::KeyedSingleton<K, V, crate::location::Atomic<L>, BoundedValue>,
509    >
510{
511    type Slice = crate::live_collections::KeyedSingleton<K, V, Tick<L::DropConsistency>, Bounded>;
512    type Backtrace = crate::compile::ir::backtrace::Backtrace;
513    fn get_location(&self) -> L::DropConsistency {
514        self.collection.location().tick.l.drop_consistency()
515    }
516
517    fn slice(self, tick: &Tick<L::DropConsistency>, backtrace: Self::Backtrace) -> Self::Slice {
518        let out = self.collection.batch_atomic(tick, self.nondet);
519        out.ir_node.borrow_mut().op_metadata_mut().backtrace = backtrace;
520        out
521    }
522}