Skip to main content

scirs2_core/reactive/
mod.rs

1//! Reactive stream primitives — push-based, iterator-compatible streams.
2//!
3//! This module provides composable stream abstractions that model data flows as
4//! sequences of values with standard combinators (`map`, `filter`, `take`,
5//! `skip`, `flatten`, `zip`, `merge`).  Everything is built on top of
6//! `std::sync` — no async runtime is required.
7//!
8//! # Overview
9//!
10//! | Type | Description |
11//! |------|-------------|
12//! | [`StreamSource`] trait | Dyn-compatible core trait (next only) |
13//! | [`Stream`] trait | Full combinators trait (requires `Sized`) |
14//! | [`InfiniteStream<T>`] | Iterator-backed stream |
15//! | [`Subject<T>`] | Broadcast subject (push-based observable) |
16//! | [`WindowedStream<T>`] | Tumbling / sliding window |
17//! | [`ZipStream<A,B>`] | Element-wise zip of two streams |
18//! | [`MergeStream<T>`] | Round-robin merge of multiple streams |
19//! | [`BackpressureBuffer<T>`] | Bounded buffer with backpressure signaling |
20//!
21//! # Example
22//!
23//! ```rust
24//! use scirs2_core::reactive::{InfiniteStream, Stream};
25//!
26//! let s = InfiniteStream::from_iter(0..10);
27//! let evens: Vec<i32> = Stream::filter(s, |x| x % 2 == 0)
28//!     .take(3)
29//!     .collect_stream();
30//! assert_eq!(evens, vec![0, 2, 4]);
31//! ```
32
33use std::collections::VecDeque;
34use std::sync::{Arc, Condvar, Mutex};
35use std::time::Duration;
36
37use crate::error::{CoreError, CoreResult, ErrorContext};
38
39// ============================================================================
40// StreamSource — dyn-compatible core trait
41// ============================================================================
42
43/// The dyn-compatible core trait for streams.
44///
45/// Implement this for any type that produces a sequence of values.  Unlike
46/// [`Stream`] (which requires `Self: Sized`), `StreamSource` can be used as a
47/// trait object (`Box<dyn StreamSource<Item = T>>`).
48pub trait StreamSource {
49    /// The type of items produced.
50    type Item;
51
52    /// Advance the stream and return the next item, or `None` when exhausted.
53    fn next_item(&mut self) -> Option<Self::Item>;
54}
55
56// ============================================================================
57// Stream — rich combinator trait
58// ============================================================================
59
60/// Full stream combinator trait.
61///
62/// Automatically implemented for any `T: StreamSource + Sized`.  Provides the
63/// ergonomic adaptor API (`map`, `filter`, `take`, etc.).
64///
65/// For boxed, type-erased streams use [`StreamSource`] directly or wrap in
66/// [`BoxedStream`].
67pub trait Stream: StreamSource + Sized {
68    /// Apply `f` to each item, producing a new stream.
69    fn map<U, F>(self, f: F) -> MapStream<Self, F>
70    where
71        F: FnMut(Self::Item) -> U,
72    {
73        MapStream { inner: self, f }
74    }
75
76    /// Keep only items for which `pred` returns `true`.
77    fn filter<P>(self, pred: P) -> FilterStream<Self, P>
78    where
79        P: FnMut(&Self::Item) -> bool,
80    {
81        FilterStream { inner: self, pred }
82    }
83
84    /// Take at most `n` items then stop.
85    fn take(self, n: usize) -> TakeStream<Self> {
86        TakeStream {
87            inner: self,
88            remaining: n,
89        }
90    }
91
92    /// Skip the first `n` items then yield the rest.
93    fn skip(self, n: usize) -> SkipStream<Self> {
94        SkipStream {
95            inner: self,
96            remaining: n,
97        }
98    }
99
100    /// Flatten a stream of iterables into a flat stream.
101    fn flatten(self) -> FlattenStream<Self>
102    where
103        Self::Item: IntoIterator,
104    {
105        FlattenStream {
106            outer: self,
107            current: None,
108        }
109    }
110
111    /// Borrow this stream mutably, producing an adaptor that forwards to it.
112    fn by_ref(&mut self) -> ByRefStream<'_, Self> {
113        ByRefStream { inner: self }
114    }
115
116    /// Collect all remaining items into a `Vec`.
117    fn collect_stream(mut self) -> Vec<Self::Item> {
118        let mut out = Vec::new();
119        while let Some(item) = self.next_item() {
120            out.push(item);
121        }
122        out
123    }
124
125    /// Count remaining items (exhausts the stream).
126    fn count_stream(mut self) -> usize {
127        let mut n = 0;
128        while self.next_item().is_some() {
129            n += 1;
130        }
131        n
132    }
133
134    /// Apply `f` to each item for its side-effects.
135    fn for_each_stream<F: FnMut(Self::Item)>(mut self, mut f: F) {
136        while let Some(item) = self.next_item() {
137            f(item);
138        }
139    }
140}
141
142// Blanket implementation: everything that is StreamSource + Sized gets Stream.
143impl<S: StreamSource + Sized> Stream for S {}
144
145// ============================================================================
146// Adaptor types
147// ============================================================================
148
149/// Stream returned by [`Stream::map`].
150pub struct MapStream<S, F> {
151    inner: S,
152    f: F,
153}
154
155impl<S: StreamSource, U, F: FnMut(S::Item) -> U> StreamSource for MapStream<S, F> {
156    type Item = U;
157
158    fn next_item(&mut self) -> Option<U> {
159        self.inner.next_item().map(|item| (self.f)(item))
160    }
161}
162
163/// Stream returned by [`Stream::filter`].
164pub struct FilterStream<S, P> {
165    inner: S,
166    pred: P,
167}
168
169impl<S: StreamSource, P: FnMut(&S::Item) -> bool> StreamSource for FilterStream<S, P> {
170    type Item = S::Item;
171
172    fn next_item(&mut self) -> Option<S::Item> {
173        loop {
174            let item = self.inner.next_item()?;
175            if (self.pred)(&item) {
176                return Some(item);
177            }
178        }
179    }
180}
181
182/// Stream returned by [`Stream::take`].
183pub struct TakeStream<S> {
184    inner: S,
185    remaining: usize,
186}
187
188impl<S: StreamSource> StreamSource for TakeStream<S> {
189    type Item = S::Item;
190
191    fn next_item(&mut self) -> Option<S::Item> {
192        if self.remaining == 0 {
193            return None;
194        }
195        self.remaining -= 1;
196        self.inner.next_item()
197    }
198}
199
200/// Stream returned by [`Stream::skip`].
201pub struct SkipStream<S> {
202    inner: S,
203    remaining: usize,
204}
205
206impl<S: StreamSource> StreamSource for SkipStream<S> {
207    type Item = S::Item;
208
209    fn next_item(&mut self) -> Option<S::Item> {
210        while self.remaining > 0 {
211            self.inner.next_item()?;
212            self.remaining -= 1;
213        }
214        self.inner.next_item()
215    }
216}
217
218/// Stream returned by [`Stream::flatten`].
219pub struct FlattenStream<S: StreamSource>
220where
221    S::Item: IntoIterator,
222{
223    outer: S,
224    current: Option<<S::Item as IntoIterator>::IntoIter>,
225}
226
227impl<S: StreamSource> StreamSource for FlattenStream<S>
228where
229    S::Item: IntoIterator,
230{
231    type Item = <S::Item as IntoIterator>::Item;
232
233    fn next_item(&mut self) -> Option<Self::Item> {
234        loop {
235            if let Some(ref mut iter) = self.current {
236                if let Some(item) = iter.next() {
237                    return Some(item);
238                }
239            }
240            let next_outer = self.outer.next_item()?;
241            self.current = Some(next_outer.into_iter());
242        }
243    }
244}
245
246/// Mutable reference adaptor returned by [`Stream::by_ref`].
247pub struct ByRefStream<'a, S> {
248    inner: &'a mut S,
249}
250
251impl<'a, S: StreamSource> StreamSource for ByRefStream<'a, S> {
252    type Item = S::Item;
253
254    fn next_item(&mut self) -> Option<S::Item> {
255        self.inner.next_item()
256    }
257}
258
259// ============================================================================
260// BoxedStream — type-erased stream wrapper
261// ============================================================================
262
263/// A heap-allocated, type-erased stream.
264///
265/// Useful when you need to store heterogeneous streams in a collection or
266/// return a stream from a function without exposing the concrete type.
267pub struct BoxedStream<T> {
268    inner: Box<dyn StreamSource<Item = T> + Send>,
269}
270
271impl<T> BoxedStream<T> {
272    /// Wrap any `StreamSource` in a heap allocation.
273    pub fn new<S>(s: S) -> Self
274    where
275        S: StreamSource<Item = T> + Send + 'static,
276    {
277        Self { inner: Box::new(s) }
278    }
279}
280
281impl<T> StreamSource for BoxedStream<T> {
282    type Item = T;
283
284    fn next_item(&mut self) -> Option<T> {
285        self.inner.next_item()
286    }
287}
288
289// ============================================================================
290// InfiniteStream<T>
291// ============================================================================
292
293/// A stream backed by any Rust `Iterator`.
294///
295/// The stream ends when the underlying iterator is exhausted.
296pub struct InfiniteStream<I: Iterator> {
297    iter: I,
298}
299
300impl<I: Iterator> InfiniteStream<I> {
301    /// Wrap an `Iterator` as a `Stream`.
302    #[allow(clippy::should_implement_trait)]
303    pub fn from_iter(iter: I) -> Self {
304        Self { iter }
305    }
306
307    /// Consume the stream and return the underlying iterator.
308    pub fn into_inner(self) -> I {
309        self.iter
310    }
311}
312
313impl<I: Iterator> StreamSource for InfiniteStream<I> {
314    type Item = I::Item;
315
316    fn next_item(&mut self) -> Option<I::Item> {
317        self.iter.next()
318    }
319}
320
321/// Also implement standard `Iterator` so callers can use `for` loops and
322/// standard adapters interchangeably.
323impl<I: Iterator> Iterator for InfiniteStream<I> {
324    type Item = I::Item;
325
326    fn next(&mut self) -> Option<I::Item> {
327        StreamSource::next_item(self)
328    }
329}
330
331// ============================================================================
332// Subject<T> — broadcast push-based observable
333// ============================================================================
334
335/// Internal subscriber slot.
336struct Subscriber<T: Clone> {
337    buf: VecDeque<T>,
338    closed: bool,
339}
340
341/// Shared state for a [`Subject`].
342struct SubjectInner<T: Clone> {
343    subscribers: Vec<Subscriber<T>>,
344    completed: bool,
345}
346
347/// A broadcast subject: push values from any thread; any number of subscriber
348/// handles can independently pull values.
349///
350/// # Example
351///
352/// ```rust
353/// use scirs2_core::reactive::Subject;
354///
355/// let mut subject = Subject::<i32>::new();
356/// let rx1 = subject.subscribe();
357/// let rx2 = subject.subscribe();
358///
359/// subject.emit(1);
360/// subject.emit(2);
361/// subject.complete();
362///
363/// assert_eq!(rx1.collect_all(), vec![1, 2]);
364/// assert_eq!(rx2.collect_all(), vec![1, 2]);
365/// ```
366pub struct Subject<T: Clone + Send + 'static> {
367    inner: Arc<(Mutex<SubjectInner<T>>, Condvar)>,
368}
369
370impl<T: Clone + Send + 'static> Subject<T> {
371    /// Create a new, empty `Subject`.
372    pub fn new() -> Self {
373        Self {
374            inner: Arc::new((
375                Mutex::new(SubjectInner {
376                    subscribers: Vec::new(),
377                    completed: false,
378                }),
379                Condvar::new(),
380            )),
381        }
382    }
383
384    /// Subscribe, returning a [`SubjectReceiver`] that receives all future
385    /// emitted values.
386    pub fn subscribe(&self) -> SubjectReceiver<T> {
387        let (lock, _) = &*self.inner;
388        let slot_idx = lock
389            .lock()
390            .map(|mut g| {
391                let idx = g.subscribers.len();
392                g.subscribers.push(Subscriber {
393                    buf: VecDeque::new(),
394                    closed: false,
395                });
396                idx
397            })
398            .unwrap_or(0);
399
400        SubjectReceiver {
401            inner: Arc::clone(&self.inner),
402            slot: slot_idx,
403        }
404    }
405
406    /// Emit one value to all current subscribers.
407    pub fn emit(&self, value: T) {
408        let (lock, cv) = &*self.inner;
409        if let Ok(mut g) = lock.lock() {
410            for sub in g.subscribers.iter_mut() {
411                if !sub.closed {
412                    sub.buf.push_back(value.clone());
413                }
414            }
415        }
416        cv.notify_all();
417    }
418
419    /// Complete the subject — no further values can be emitted.
420    pub fn complete(&self) {
421        let (lock, cv) = &*self.inner;
422        if let Ok(mut g) = lock.lock() {
423            g.completed = true;
424            for sub in g.subscribers.iter_mut() {
425                sub.closed = true;
426            }
427        }
428        cv.notify_all();
429    }
430
431    /// Number of current subscribers.
432    pub fn subscriber_count(&self) -> usize {
433        let (lock, _) = &*self.inner;
434        lock.lock().map(|g| g.subscribers.len()).unwrap_or(0)
435    }
436
437    /// `true` if [`complete`](Subject::complete) has been called.
438    pub fn is_completed(&self) -> bool {
439        let (lock, _) = &*self.inner;
440        lock.lock().map(|g| g.completed).unwrap_or(false)
441    }
442}
443
444/// A subscriber handle for a [`Subject`].
445pub struct SubjectReceiver<T: Clone + Send + 'static> {
446    inner: Arc<(Mutex<SubjectInner<T>>, Condvar)>,
447    slot: usize,
448}
449
450impl<T: Clone + Send + 'static> SubjectReceiver<T> {
451    /// Block until the next value arrives or the subject is completed.
452    pub fn recv(&self) -> Option<T> {
453        let (lock, cv) = &*self.inner;
454        let mut g = lock.lock().ok()?;
455        loop {
456            if let Some(v) = g.subscribers.get_mut(self.slot)?.buf.pop_front() {
457                return Some(v);
458            }
459            if g.completed
460                || g.subscribers
461                    .get(self.slot)
462                    .map(|s| s.closed)
463                    .unwrap_or(true)
464            {
465                return None;
466            }
467            g = cv.wait(g).ok()?;
468        }
469    }
470
471    /// Try to receive without blocking.
472    pub fn try_recv(&self) -> Option<T> {
473        let (lock, _) = &*self.inner;
474        let mut g = lock.lock().ok()?;
475        g.subscribers.get_mut(self.slot)?.buf.pop_front()
476    }
477
478    /// Drain all buffered and future values until the subject is completed.
479    pub fn collect_all(self) -> Vec<T> {
480        let mut result = Vec::new();
481        while let Some(v) = self.recv() {
482            result.push(v);
483        }
484        result
485    }
486}
487
488// ============================================================================
489// WindowedStream<T>
490// ============================================================================
491
492/// Window mode for [`WindowedStream`].
493#[derive(Debug, Clone, Copy, PartialEq, Eq)]
494pub enum WindowMode {
495    /// Non-overlapping windows of exactly `size` items.
496    Tumbling,
497    /// Overlapping windows: advance by `step`, window has `size` items.
498    Sliding { step: usize },
499}
500
501/// A stream that groups items into fixed-size windows.
502///
503/// - **Tumbling** windows are non-overlapping (`step = size`).
504/// - **Sliding** windows advance by a configurable `step`.
505///
506/// Each call to `next_item` returns `Some(Vec<T>)` with exactly `size`
507/// items (or `None` when the source is exhausted).
508pub struct WindowedStream<S: StreamSource>
509where
510    S::Item: Clone,
511{
512    inner: S,
513    window_size: usize,
514    mode: WindowMode,
515    buffer: VecDeque<S::Item>,
516    exhausted: bool,
517}
518
519impl<S: StreamSource> WindowedStream<S>
520where
521    S::Item: Clone,
522{
523    /// Create a tumbling window of `size`.
524    pub fn tumbling(inner: S, size: usize) -> Self {
525        let size = size.max(1);
526        Self {
527            inner,
528            window_size: size,
529            mode: WindowMode::Tumbling,
530            buffer: VecDeque::new(),
531            exhausted: false,
532        }
533    }
534
535    /// Create a sliding window of `size` that advances by `step`.
536    pub fn sliding(inner: S, size: usize, step: usize) -> Self {
537        let size = size.max(1);
538        let step = step.max(1);
539        Self {
540            inner,
541            window_size: size,
542            mode: WindowMode::Sliding { step },
543            buffer: VecDeque::new(),
544            exhausted: false,
545        }
546    }
547
548    /// Fill the internal buffer up to `target` items.
549    fn fill_to(&mut self, target: usize) {
550        while !self.exhausted && self.buffer.len() < target {
551            match self.inner.next_item() {
552                Some(item) => self.buffer.push_back(item),
553                None => {
554                    self.exhausted = true;
555                    break;
556                }
557            }
558        }
559    }
560}
561
562impl<S: StreamSource> StreamSource for WindowedStream<S>
563where
564    S::Item: Clone,
565{
566    type Item = Vec<S::Item>;
567
568    fn next_item(&mut self) -> Option<Vec<S::Item>> {
569        self.fill_to(self.window_size);
570        if self.buffer.len() < self.window_size {
571            return None;
572        }
573        let window: Vec<S::Item> = self.buffer.iter().take(self.window_size).cloned().collect();
574        match self.mode {
575            WindowMode::Tumbling => {
576                for _ in 0..self.window_size {
577                    self.buffer.pop_front();
578                }
579            }
580            WindowMode::Sliding { step } => {
581                for _ in 0..step {
582                    self.buffer.pop_front();
583                }
584            }
585        }
586        Some(window)
587    }
588}
589
590// ============================================================================
591// ZipStream<A, B>
592// ============================================================================
593
594/// A stream that zips two streams element-wise.
595///
596/// Stops as soon as either stream is exhausted.
597pub struct ZipStream<A: StreamSource, B: StreamSource> {
598    left: A,
599    right: B,
600}
601
602impl<A: StreamSource, B: StreamSource> ZipStream<A, B> {
603    /// Create a new `ZipStream`.
604    pub fn new(left: A, right: B) -> Self {
605        Self { left, right }
606    }
607}
608
609impl<A: StreamSource, B: StreamSource> StreamSource for ZipStream<A, B> {
610    type Item = (A::Item, B::Item);
611
612    fn next_item(&mut self) -> Option<(A::Item, B::Item)> {
613        let a = self.left.next_item()?;
614        let b = self.right.next_item()?;
615        Some((a, b))
616    }
617}
618
619// ============================================================================
620// MergeStream<T>
621// ============================================================================
622
623/// A stream that merges multiple source streams in round-robin order.
624///
625/// Each call to `next_item` tries each sub-stream in rotation, returning the
626/// first non-`None` value.  Exhausted sub-streams are removed.  Returns
627/// `None` when all sub-streams are exhausted.
628pub struct MergeStream<T> {
629    sources: Vec<BoxedStream<T>>,
630    cursor: usize,
631}
632
633impl<T: Send + 'static> MergeStream<T> {
634    /// Create a `MergeStream` from a vector of `BoxedStream`s.
635    pub fn new(sources: Vec<BoxedStream<T>>) -> Self {
636        Self { sources, cursor: 0 }
637    }
638
639    /// Convenience: build from a vector of concrete stream types.
640    pub fn from_streams<S>(streams: Vec<S>) -> Self
641    where
642        S: StreamSource<Item = T> + Send + 'static,
643    {
644        let boxed = streams.into_iter().map(BoxedStream::new).collect();
645        Self::new(boxed)
646    }
647}
648
649impl<T: Send + 'static> StreamSource for MergeStream<T> {
650    type Item = T;
651
652    fn next_item(&mut self) -> Option<T> {
653        let n = self.sources.len();
654        if n == 0 {
655            return None;
656        }
657        for attempt in 0..n {
658            let idx = (self.cursor + attempt) % n;
659            if let Some(item) = self.sources[idx].next_item() {
660                self.cursor = (idx + 1) % self.sources.len();
661                return Some(item);
662            }
663        }
664        None
665    }
666}
667
668// ============================================================================
669// BackpressureBuffer<T>
670// ============================================================================
671
672/// Backpressure state.
673#[derive(Debug, Clone, Copy, PartialEq, Eq)]
674pub enum BackpressureSignal {
675    /// Buffer has room; producers can continue.
676    Normal,
677    /// Buffer is above the high-water mark; producers should slow down.
678    Throttle,
679    /// Buffer is full; producers must block or drop.
680    Full,
681}
682
683/// A bounded buffer with backpressure signaling.
684pub struct BackpressureBuffer<T: Send> {
685    inner: Mutex<VecDeque<T>>,
686    not_empty: Condvar,
687    not_full: Condvar,
688    capacity: usize,
689    high_water: usize,
690}
691
692impl<T: Send> BackpressureBuffer<T> {
693    /// Create a new buffer with `capacity` and `high_water_fraction` ∈ (0, 1].
694    pub fn new(capacity: usize, high_water_fraction: f64) -> Self {
695        let capacity = capacity.max(1);
696        let high_water = ((capacity as f64) * high_water_fraction.clamp(0.0, 1.0)) as usize;
697        let high_water = high_water.max(1).min(capacity);
698        Self {
699            inner: Mutex::new(VecDeque::with_capacity(capacity)),
700            not_empty: Condvar::new(),
701            not_full: Condvar::new(),
702            capacity,
703            high_water,
704        }
705    }
706
707    /// Try to push without blocking.
708    pub fn try_push(&self, item: T) -> Result<BackpressureSignal, T> {
709        // If the mutex is poisoned we return the item back to the caller.
710        let mut g = match self.inner.lock() {
711            Ok(guard) => guard,
712            Err(_) => return Err(item),
713        };
714        if g.len() >= self.capacity {
715            return Err(item);
716        }
717        g.push_back(item);
718        self.not_empty.notify_one();
719        let signal = if g.len() >= self.high_water {
720            BackpressureSignal::Throttle
721        } else {
722            BackpressureSignal::Normal
723        };
724        Ok(signal)
725    }
726
727    /// Blocking push — waits until space is available.
728    pub fn push(&self, item: T) -> CoreResult<BackpressureSignal> {
729        let mut g = self.inner.lock().map_err(|_| {
730            CoreError::InvalidInput(ErrorContext::new("BackpressureBuffer: mutex poisoned"))
731        })?;
732
733        while g.len() >= self.capacity {
734            g = self.not_full.wait(g).map_err(|_| {
735                CoreError::InvalidInput(ErrorContext::new("BackpressureBuffer: condvar poisoned"))
736            })?;
737        }
738        g.push_back(item);
739        self.not_empty.notify_one();
740        let signal = if g.len() >= self.high_water {
741            BackpressureSignal::Throttle
742        } else {
743            BackpressureSignal::Normal
744        };
745        Ok(signal)
746    }
747
748    /// Non-blocking pop.
749    pub fn try_pop(&self) -> Option<T> {
750        let mut g = self.inner.lock().ok()?;
751        let item = g.pop_front()?;
752        self.not_full.notify_one();
753        Some(item)
754    }
755
756    /// Blocking pop — waits until an item is available.
757    pub fn pop(&self) -> Option<T> {
758        let mut g = self.inner.lock().ok()?;
759        loop {
760            if let Some(item) = g.pop_front() {
761                self.not_full.notify_one();
762                return Some(item);
763            }
764            g = self.not_empty.wait(g).ok()?;
765        }
766    }
767
768    /// Pop with a timeout.
769    pub fn pop_timeout(&self, timeout: Duration) -> Option<T> {
770        let mut g = self.inner.lock().ok()?;
771        loop {
772            if let Some(item) = g.pop_front() {
773                self.not_full.notify_one();
774                return Some(item);
775            }
776            let (ng, result) = self.not_empty.wait_timeout(g, timeout).ok()?;
777            g = ng;
778            if result.timed_out() {
779                return None;
780            }
781        }
782    }
783
784    /// Current number of buffered items.
785    pub fn len(&self) -> usize {
786        self.inner.lock().map(|g| g.len()).unwrap_or(0)
787    }
788
789    /// `true` if the buffer is empty.
790    pub fn is_empty(&self) -> bool {
791        self.len() == 0
792    }
793
794    /// Maximum buffer capacity.
795    pub fn capacity(&self) -> usize {
796        self.capacity
797    }
798
799    /// Current backpressure signal (without pushing anything).
800    pub fn signal(&self) -> BackpressureSignal {
801        let len = self.len();
802        if len >= self.capacity {
803            BackpressureSignal::Full
804        } else if len >= self.high_water {
805            BackpressureSignal::Throttle
806        } else {
807            BackpressureSignal::Normal
808        }
809    }
810}
811
812// Push-pull dataflow (Map/Filter/Zip/Buffer)
813pub mod dataflow;
814// Reactive signal/slot pattern
815pub mod signal;
816
817// ============================================================================
818// Tests
819// ============================================================================
820
821#[cfg(test)]
822mod tests {
823    use super::*;
824
825    #[test]
826    fn infinite_stream_map_filter_take() {
827        let mut s = InfiniteStream::from_iter(0..100i32);
828        let result: Vec<i32> = Stream::by_ref(&mut s)
829            .map(|x| x * 2)
830            .filter(|x| x % 4 == 0)
831            .take(5)
832            .collect_stream();
833        assert_eq!(result, vec![0, 4, 8, 12, 16]);
834    }
835
836    #[test]
837    fn infinite_stream_skip_take() {
838        let s = InfiniteStream::from_iter(0..20i32);
839        let result: Vec<i32> = Stream::skip(s, 5).take(5).collect_stream();
840        assert_eq!(result, vec![5, 6, 7, 8, 9]);
841    }
842
843    #[test]
844    fn flatten_stream() {
845        let nested = vec![vec![1, 2, 3], vec![4, 5], vec![6]];
846        let s = InfiniteStream::from_iter(nested.into_iter());
847        let result: Vec<i32> = Stream::flatten(s).collect_stream();
848        assert_eq!(result, vec![1, 2, 3, 4, 5, 6]);
849    }
850
851    #[test]
852    fn zip_stream() {
853        let a = InfiniteStream::from_iter(0..5i32);
854        let b = InfiniteStream::from_iter(10..15i32);
855        let result: Vec<(i32, i32)> = ZipStream::new(a, b).collect_stream();
856        assert_eq!(result, vec![(0, 10), (1, 11), (2, 12), (3, 13), (4, 14)]);
857    }
858
859    #[test]
860    fn tumbling_window() {
861        let s = InfiniteStream::from_iter(0..9i32);
862        let windows: Vec<Vec<i32>> = WindowedStream::tumbling(s, 3).collect_stream();
863        assert_eq!(windows, vec![vec![0, 1, 2], vec![3, 4, 5], vec![6, 7, 8]]);
864    }
865
866    #[test]
867    fn sliding_window() {
868        let s = InfiniteStream::from_iter(0..6i32);
869        let windows: Vec<Vec<i32>> = WindowedStream::sliding(s, 3, 1).collect_stream();
870        assert_eq!(
871            windows,
872            vec![vec![0, 1, 2], vec![1, 2, 3], vec![2, 3, 4], vec![3, 4, 5],]
873        );
874    }
875
876    #[test]
877    fn merge_stream_round_robin() {
878        let s1 = InfiniteStream::from_iter(vec![1, 3, 5].into_iter());
879        let s2 = InfiniteStream::from_iter(vec![2, 4, 6].into_iter());
880        let result: Vec<i32> = MergeStream::from_streams(vec![s1, s2]).collect_stream();
881        assert_eq!(result, vec![1, 2, 3, 4, 5, 6]);
882    }
883
884    #[test]
885    fn subject_broadcast() {
886        let subject = Subject::<i32>::new();
887        let rx1 = subject.subscribe();
888        let rx2 = subject.subscribe();
889
890        subject.emit(10);
891        subject.emit(20);
892        subject.complete();
893
894        assert_eq!(rx1.collect_all(), vec![10, 20]);
895        assert_eq!(rx2.collect_all(), vec![10, 20]);
896    }
897
898    #[test]
899    fn backpressure_buffer_basic() {
900        let buf = BackpressureBuffer::<i32>::new(4, 0.75);
901        assert_eq!(buf.try_push(1), Ok(BackpressureSignal::Normal));
902        assert_eq!(buf.try_push(2), Ok(BackpressureSignal::Normal));
903        assert_eq!(buf.try_push(3), Ok(BackpressureSignal::Throttle));
904        assert_eq!(buf.try_push(4), Ok(BackpressureSignal::Throttle));
905        assert!(buf.try_push(5).is_err());
906        assert_eq!(buf.try_pop(), Some(1));
907        assert_eq!(buf.len(), 3);
908    }
909}