Skip to main content

datum/stream/
source.rs

1//! `Source<Out, Mat>` — the origin of a linear stream — plus its constructors,
2//! terminal runners (`run_with`/`run_fold`/`run_foreach`/`run_reduce`/…), and the
3//! supporting types `NotUsed`, `Keep`, `Demand`, `PushOutlet`, `MaybeHandle`, and
4//! `IntoSource`.
5//!
6//! Construction is side-effect-free: every constructor stores a factory closure
7//! that only runs at materialization. See this module's `AGENTS.md` and the crate
8//! `CLAUDE.md` blueprint-vs-materialization rule.
9
10use super::flow::{self, FlowTransform};
11use super::*;
12use crate::Attributes;
13use crate::context::SourceWithContext;
14use crate::instrument::{InstrumentedStream, StreamInstrumentationRegistry};
15
16#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
17pub struct NotUsed;
18
19type CombinedSourceFactory<Out> =
20    dyn Fn(&Materializer) -> StreamResult<BoxStream<Out>> + Send + Sync;
21
22pub struct Keep;
23
24impl Keep {
25    pub fn left<Left, Right>(left: Left, _right: Right) -> Left {
26        left
27    }
28
29    pub fn right<Left, Right>(_left: Left, right: Right) -> Right {
30        right
31    }
32
33    pub fn both<Left, Right>(left: Left, right: Right) -> (Left, Right) {
34        (left, right)
35    }
36
37    pub fn none<Left, Right>(_left: Left, _right: Right) -> NotUsed {
38        NotUsed
39    }
40}
41
42#[derive(Debug, Clone, PartialEq, Eq)]
43pub enum SourceCombineStrategy {
44    Merge {
45        eager_complete: bool,
46    },
47    Concat,
48    Prioritized {
49        priorities: Vec<usize>,
50        eager_complete: bool,
51    },
52}
53
54#[derive(Clone)]
55pub struct MaybeHandle<T> {
56    value: Arc<Mutex<Option<StreamResult<T>>>>,
57}
58
59impl<T> MaybeHandle<T> {
60    #[must_use]
61    pub fn is_completed(&self) -> bool {
62        self.value.lock().expect("maybe handle poisoned").is_some()
63    }
64
65    pub fn complete(&self, item: T) -> StreamResult<()> {
66        self.settle(Ok(item))
67    }
68
69    pub fn fail(&self, error: StreamError) -> StreamResult<()> {
70        self.settle(Err(error))
71    }
72
73    fn settle(&self, result: StreamResult<T>) -> StreamResult<()> {
74        let mut value = self.value.lock().expect("maybe handle poisoned");
75        if value.is_some() {
76            return Err(StreamError::Failed("maybe source already completed".into()));
77        }
78        *value = Some(result);
79        Ok(())
80    }
81}
82
83#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
84pub struct Demand(u64);
85
86impl Demand {
87    pub const ZERO: Self = Self(0);
88    pub const ONE: Self = Self(1);
89    pub const MAX: Self = Self(u64::MAX);
90
91    #[must_use]
92    pub const fn new(available: u64) -> Self {
93        Self(available)
94    }
95
96    #[must_use]
97    pub const fn available(self) -> u64 {
98        self.0
99    }
100
101    #[must_use]
102    pub const fn is_unbounded(self) -> bool {
103        self.0 == u64::MAX
104    }
105
106    #[must_use]
107    pub const fn is_empty(self) -> bool {
108        self.0 == 0
109    }
110
111    #[must_use]
112    pub const fn saturating_add(self, rhs: Self) -> Self {
113        Self(self.0.saturating_add(rhs.0))
114    }
115
116    pub fn consume_one(&mut self) -> bool {
117        match self.0 {
118            0 => false,
119            u64::MAX => true,
120            _ => {
121                self.0 -= 1;
122                true
123            }
124        }
125    }
126}
127
128pub trait PushOutlet<T>: Send {
129    fn push(&mut self, item: T) -> StreamResult<Demand>;
130
131    fn complete(&mut self) -> StreamResult<()> {
132        Ok(())
133    }
134
135    fn fail(&mut self, cause: StreamError) -> StreamResult<()> {
136        Err(cause)
137    }
138}
139
140#[derive(Clone)]
141pub struct Source<Out, Mat = NotUsed> {
142    pub(crate) factory: Arc<dyn SourceFactory<Out, Mat>>,
143    pub(crate) terminal_factory: Option<Arc<TerminalSourceFactory<Out, Mat>>>,
144    pub(super) hints: SourceHints,
145    pub(super) attributes: Attributes,
146    /// Set only for split-segment sources in fast mode. `None` for all other sources.
147    /// Cleared by all composition operators (via/map/to_mat etc.).
148    pub(crate) split_hook: Option<Arc<dyn SplitSegmentHookDyn>>,
149}
150
151pub(crate) type TerminalSourceFactory<Out, Mat> =
152    dyn Fn(&Materializer) -> StreamResult<(Arc<dyn TerminalSourceHookDyn<Out>>, Mat)> + Send + Sync;
153
154impl<Out: Send + 'static> Source<Out, NotUsed> {
155    pub(super) fn from_factory<F>(factory: F) -> Self
156    where
157        F: Fn() -> BoxStream<Out> + Send + Sync + 'static,
158    {
159        Self::from_factory_with_hints(factory, SourceHints::default())
160    }
161
162    fn from_factory_with_hints<F>(factory: F, hints: SourceHints) -> Self
163    where
164        F: Fn() -> BoxStream<Out> + Send + Sync + 'static,
165    {
166        Self::from_materialized_factory_with_hints(
167            move |_materializer| Ok((factory(), NotUsed)),
168            hints,
169        )
170    }
171
172    #[must_use]
173    pub fn empty() -> Self {
174        Self::from_factory_with_hints(
175            || Box::new(std::iter::empty()),
176            SourceHints::with_inline_micro(0),
177        )
178    }
179
180    #[must_use]
181    pub fn never() -> Self {
182        Self::from_materialized_factory_with_hints(
183            move |materializer| {
184                let state = Arc::clone(&materializer.inner.state);
185                Ok((
186                    Box::new(std::iter::from_fn(move || {
187                        loop {
188                            if state.shutdown.load(Ordering::SeqCst) {
189                                return Some(Err(StreamError::AbruptTermination));
190                            }
191                            if super::runtime::current_stream_cancelled()
192                                .as_ref()
193                                .is_some_and(|cancelled| cancelled.load(Ordering::SeqCst))
194                            {
195                                return Some(Err(StreamError::Cancelled));
196                            }
197                            std::thread::park_timeout(Duration::from_millis(1));
198                        }
199                    })),
200                    NotUsed,
201                ))
202            },
203            SourceHints::default(),
204        )
205    }
206
207    #[must_use]
208    pub fn failed(error: StreamError) -> Self {
209        Self::from_factory_with_hints(
210            move || Box::new(std::iter::once(Err(error.clone()))),
211            // max_success_items=0: failed() emits no Ok items before the Err.
212            SourceHints::with_inline_micro(0),
213        )
214    }
215
216    #[must_use]
217    pub fn future<F, Fut>(future: F) -> Self
218    where
219        F: Fn() -> Fut + Send + Sync + 'static,
220        Fut: Future<Output = StreamResult<Out>> + Send + 'static,
221    {
222        let future = Arc::new(future);
223        Self::from_factory(move || {
224            let future = Arc::clone(&future);
225            let mut emitted = false;
226            Box::new(std::iter::from_fn(move || {
227                if emitted {
228                    return None;
229                }
230                emitted = true;
231                Some(
232                    catch_unwind_failed("source future factory", || future())
233                        .and_then(flow::run_future_inline_or_spawn),
234                )
235            }))
236        })
237    }
238
239    #[must_use]
240    pub fn future_source<F, Fut>(future: F) -> Self
241    where
242        F: Fn() -> Fut + Send + Sync + 'static,
243        Fut: Future<Output = StreamResult<Source<Out>>> + Send + 'static,
244    {
245        let future = Arc::new(future);
246        Self::from_materialized_factory(move |materializer| {
247            let materializer = materializer.with_name_prefix(materializer.name_prefix().to_owned());
248            let future = Arc::clone(&future);
249            let mut current = None::<BoxStream<Out>>;
250            let mut initialized = false;
251            let mut terminated = false;
252            Ok((
253                Box::new(std::iter::from_fn(move || {
254                    if terminated {
255                        return None;
256                    }
257
258                    loop {
259                        if let Some(stream) = current.as_mut() {
260                            match stream.next() {
261                                Some(item) => return Some(item),
262                                None => {
263                                    terminated = true;
264                                    return None;
265                                }
266                            }
267                        }
268
269                        if initialized {
270                            terminated = true;
271                            return None;
272                        }
273                        initialized = true;
274                        let source = match catch_unwind_failed("future_source factory", || future())
275                            .and_then(flow::run_future_inline_or_spawn)
276                        {
277                            Ok(source) => source,
278                            Err(error) => {
279                                terminated = true;
280                                return Some(Err(error));
281                            }
282                        };
283                        current = Some(match Arc::clone(&source.factory).create(&materializer) {
284                            Ok((stream, _)) => stream,
285                            Err(error) => {
286                                terminated = true;
287                                return Some(Err(error));
288                            }
289                        });
290                    }
291                })) as BoxStream<Out>,
292                NotUsed,
293            ))
294        })
295    }
296
297    #[must_use]
298    pub fn cycle<F, I>(factory: F) -> Self
299    where
300        F: Fn() -> I + Send + Sync + 'static,
301        I: IntoIterator<Item = Out>,
302        I::IntoIter: Send + 'static,
303    {
304        let factory = Arc::new(factory);
305        Self::from_factory(move || {
306            let factory = Arc::clone(&factory);
307            let mut current = None::<I::IntoIter>;
308            let mut terminated = false;
309            Box::new(std::iter::from_fn(move || {
310                if terminated {
311                    return None;
312                }
313
314                if let Some(iter) = current.as_mut()
315                    && let Some(item) = iter.next()
316                {
317                    return Some(Ok(item));
318                }
319
320                let mut next = match catch_unwind_failed("cycle factory", || factory()) {
321                    Ok(iterable) => iterable.into_iter(),
322                    Err(error) => {
323                        terminated = true;
324                        return Some(Err(error));
325                    }
326                };
327                match next.next() {
328                    Some(item) => {
329                        current = Some(next);
330                        Some(Ok(item))
331                    }
332                    None => {
333                        terminated = true;
334                        Some(Err(StreamError::Failed("empty iterator".into())))
335                    }
336                }
337            }))
338        })
339    }
340
341    #[must_use]
342    pub fn unfold<State, F>(initial: State, f: F) -> Self
343    where
344        State: Clone + Send + Sync + 'static,
345        F: Fn(State) -> Option<(State, Out)> + Send + Sync + 'static,
346    {
347        let f = Arc::new(f);
348        Self::from_factory(move || {
349            let f = Arc::clone(&f);
350            let mut state = Some(initial.clone());
351            let mut terminated = false;
352            Box::new(std::iter::from_fn(move || {
353                if terminated {
354                    return None;
355                }
356                let current = state.take().expect("unfold state present");
357                match catch_unwind_failed("unfold function", || f(current)) {
358                    Ok(Some((next_state, item))) => {
359                        state = Some(next_state);
360                        Some(Ok(item))
361                    }
362                    Ok(None) => {
363                        terminated = true;
364                        None
365                    }
366                    Err(error) => {
367                        terminated = true;
368                        Some(Err(error))
369                    }
370                }
371            }))
372        })
373    }
374
375    #[must_use]
376    pub fn unfold_async<State, F, Fut>(initial: State, f: F) -> Self
377    where
378        State: Clone + Send + Sync + 'static,
379        F: Fn(State) -> Fut + Send + Sync + 'static,
380        Fut: Future<Output = StreamResult<Option<(State, Out)>>> + Send + 'static,
381    {
382        let f = Arc::new(f);
383        Self::from_factory(move || {
384            let f = Arc::clone(&f);
385            let mut state = Some(initial.clone());
386            let mut terminated = false;
387            Box::new(std::iter::from_fn(move || {
388                if terminated {
389                    return None;
390                }
391                let current = state.take().expect("unfold_async state present");
392                match catch_unwind_failed("unfold_async factory", || f(current))
393                    .and_then(flow::run_future_inline_or_spawn)
394                {
395                    Ok(Some((next_state, item))) => {
396                        state = Some(next_state);
397                        Some(Ok(item))
398                    }
399                    Ok(None) => {
400                        terminated = true;
401                        None
402                    }
403                    Err(error) => {
404                        terminated = true;
405                        Some(Err(error))
406                    }
407                }
408            }))
409        })
410    }
411
412    #[must_use]
413    pub fn unfold_resource<Resource, Create, Read, Close>(
414        create: Create,
415        read: Read,
416        close: Close,
417    ) -> Self
418    where
419        Resource: Send + 'static,
420        Create: Fn() -> StreamResult<Resource> + Send + Sync + 'static,
421        Read: Fn(&mut Resource) -> StreamResult<Option<Out>> + Send + Sync + 'static,
422        Close: Fn(Resource) -> StreamResult<()> + Send + Sync + 'static,
423    {
424        let create = Arc::new(create);
425        let read = Arc::new(read);
426        let close = Arc::new(close);
427        Self::from_factory(move || {
428            Box::new(UnfoldResourceStream {
429                create: Arc::clone(&create),
430                read: Arc::clone(&read),
431                close: Arc::clone(&close),
432                resource: None,
433                created: false,
434                terminated: false,
435                _marker: PhantomData,
436            })
437        })
438    }
439
440    #[must_use]
441    pub fn unfold_resource_async<Resource, Create, CreateFut, Read, ReadFut, Close, CloseFut>(
442        create: Create,
443        read: Read,
444        close: Close,
445    ) -> Self
446    where
447        Resource: Send + 'static,
448        Create: Fn() -> CreateFut + Send + Sync + 'static,
449        CreateFut: Future<Output = StreamResult<Resource>> + Send + 'static,
450        Read: Fn(&mut Resource) -> ReadFut + Send + Sync + 'static,
451        ReadFut: Future<Output = StreamResult<Option<Out>>> + Send + 'static,
452        Close: Fn(Resource) -> CloseFut + Send + Sync + 'static,
453        CloseFut: Future<Output = StreamResult<()>> + Send + 'static,
454    {
455        let create = Arc::new(create);
456        let read = Arc::new(read);
457        let close = Arc::new(close);
458        Self::from_factory(move || {
459            Box::new(UnfoldResourceAsyncStream {
460                create: Arc::clone(&create),
461                read: Arc::clone(&read),
462                close: Arc::clone(&close),
463                resource: None,
464                created: false,
465                terminated: false,
466                _marker: PhantomData,
467            })
468        })
469    }
470
471    #[must_use]
472    pub fn lazy_single<F>(create: F) -> Self
473    where
474        F: Fn() -> Out + Send + Sync + 'static,
475    {
476        let create = Arc::new(create);
477        Self::from_factory(move || {
478            let create = Arc::clone(&create);
479            let mut emitted = false;
480            Box::new(std::iter::from_fn(move || {
481                if emitted {
482                    return None;
483                }
484                emitted = true;
485                Some(catch_unwind_failed("lazy_single factory", || create()))
486            }))
487        })
488    }
489
490    #[must_use]
491    pub fn lazy_future<F, Fut>(create: F) -> Self
492    where
493        F: Fn() -> Fut + Send + Sync + 'static,
494        Fut: Future<Output = StreamResult<Out>> + Send + 'static,
495    {
496        let create = Arc::new(create);
497        Self::from_factory(move || {
498            let create = Arc::clone(&create);
499            let mut emitted = false;
500            Box::new(std::iter::from_fn(move || {
501                if emitted {
502                    return None;
503                }
504                emitted = true;
505                Some(
506                    catch_unwind_failed("lazy_future factory", || create())
507                        .and_then(flow::run_future_inline_or_spawn),
508                )
509            }))
510        })
511    }
512
513    #[must_use]
514    pub fn lazy_source<InnerMat, F>(create: F) -> Source<Out, StreamCompletion<InnerMat>>
515    where
516        InnerMat: Send + 'static,
517        F: Fn() -> Source<Out, InnerMat> + Send + Sync + 'static,
518    {
519        let create = Arc::new(create);
520        Source::from_materialized_factory(move |materializer| {
521            let (sender, receiver) = oneshot::channel();
522            Ok((
523                Box::new(LazySourceStream {
524                    create: Arc::clone(&create),
525                    materializer: materializer
526                        .with_name_prefix(materializer.name_prefix().to_owned()),
527                    current: None,
528                    mat_sender: Some(sender),
529                    initialized: false,
530                    terminated: false,
531                }) as BoxStream<Out>,
532                StreamCompletion::from_receiver(receiver, None),
533            ))
534        })
535    }
536
537    #[must_use]
538    pub fn lazy_future_source<InnerMat, F, Fut>(
539        create: F,
540    ) -> Source<Out, StreamCompletion<InnerMat>>
541    where
542        InnerMat: Send + 'static,
543        F: Fn() -> Fut + Send + Sync + 'static,
544        Fut: Future<Output = StreamResult<Source<Out, InnerMat>>> + Send + 'static,
545    {
546        let create = Arc::new(create);
547        Source::from_materialized_factory(move |materializer| {
548            let (sender, receiver) = oneshot::channel();
549            Ok((
550                Box::new(LazyFutureSourceStream {
551                    create: Arc::clone(&create),
552                    materializer: materializer
553                        .with_name_prefix(materializer.name_prefix().to_owned()),
554                    current: None,
555                    mat_sender: Some(sender),
556                    initialized: false,
557                    terminated: false,
558                    _marker: PhantomData,
559                }) as BoxStream<Out>,
560                StreamCompletion::from_receiver(receiver, None),
561            ))
562        })
563    }
564
565    #[must_use]
566    pub fn from_fn_iter<F, I>(factory: F) -> Self
567    where
568        F: Fn() -> I + Send + Sync + 'static,
569        I: IntoIterator<Item = Out>,
570        I::IntoIter: Send + 'static,
571    {
572        Self::from_factory(move || Box::new(factory().into_iter().map(Ok)))
573    }
574}
575
576impl<Out: Send + 'static, Mat: Send + 'static> Source<Out, Mat> {
577    fn from_materialized_factory_with_hints<F>(factory: F, hints: SourceHints) -> Self
578    where
579        F: Fn(&Materializer) -> StreamResult<(BoxStream<Out>, Mat)> + Send + Sync + 'static,
580    {
581        Self {
582            factory: Arc::new(FnSourceFactory(factory)),
583            terminal_factory: None,
584            hints,
585            attributes: Attributes::default(),
586            split_hook: None,
587        }
588    }
589
590    pub(crate) fn from_materialized_factory<F>(factory: F) -> Self
591    where
592        F: Fn(&Materializer) -> StreamResult<(BoxStream<Out>, Mat)> + Send + Sync + 'static,
593    {
594        Self::from_materialized_factory_with_hints(factory, SourceHints::default())
595    }
596
597    pub(crate) fn from_terminal_direct_materialized_factory<F, D>(
598        factory: F,
599        terminal_factory: D,
600    ) -> Self
601    where
602        F: Fn(&Materializer) -> StreamResult<(BoxStream<Out>, Mat)> + Send + Sync + 'static,
603        D: Fn(&Materializer) -> StreamResult<(Arc<dyn TerminalSourceHookDyn<Out>>, Mat)>
604            + Send
605            + Sync
606            + 'static,
607    {
608        Self {
609            factory: Arc::new(FnSourceFactory(factory)),
610            terminal_factory: Some(Arc::new(terminal_factory)),
611            hints: SourceHints::with_terminal_consumer_batch(),
612            attributes: Attributes::default(),
613            split_hook: None,
614        }
615    }
616
617    #[must_use]
618    pub fn as_source_with_context<Ctx, F>(
619        self,
620        extract_context: F,
621    ) -> SourceWithContext<Out, Ctx, Mat>
622    where
623        Ctx: Send + 'static,
624        F: Fn(&Out) -> Ctx + Send + Sync + 'static,
625    {
626        SourceWithContext::from_source(self.map(move |item| {
627            let context = extract_context(&item);
628            (item, context)
629        }))
630    }
631
632    #[must_use]
633    pub fn via<Next, FlowMat>(self, flow: Flow<Out, Next, FlowMat>) -> Source<Next, Mat>
634    where
635        Next: Send + 'static,
636        FlowMat: Send + 'static,
637    {
638        self.via_mat(flow, Keep::left)
639    }
640
641    #[must_use]
642    pub fn via_mat<Next, FlowMat, Combined, F>(
643        self,
644        flow: Flow<Out, Next, FlowMat>,
645        combine: F,
646    ) -> Source<Next, Combined>
647    where
648        Next: Send + 'static,
649        FlowMat: Send + 'static,
650        Combined: Send + 'static,
651        F: Fn(Mat, FlowMat) -> Combined + Send + Sync + 'static,
652    {
653        let source = self.factory;
654        let transform = flow.transform;
655        let materialize_flow = flow.materialize;
656        let hints = self.hints.after_flow(flow.hints);
657        let combine = Arc::new(combine);
658        Source::from_materialized_factory_with_hints(
659            move |materializer| {
660                let (stream, source_mat) = Arc::clone(&source).create(materializer)?;
661                let flow_mat = materialize_flow()?;
662                let stream = match &transform {
663                    FlowTransform::Pure(transform) => transform(stream),
664                    FlowTransform::Runtime(transform) => transform(stream, materializer)?,
665                };
666                Ok((stream, combine(source_mat, flow_mat)))
667            },
668            hints,
669        )
670    }
671
672    #[must_use]
673    pub fn via_mat_with<Next, FlowMat, Combined, F>(
674        self,
675        flow: Flow<Out, Next, FlowMat>,
676        combine: F,
677    ) -> Source<Next, Combined>
678    where
679        Next: Send + 'static,
680        FlowMat: Send + 'static,
681        Combined: Send + 'static,
682        F: Fn(Mat, FlowMat) -> Combined + Send + Sync + 'static,
683    {
684        self.via_mat(flow, combine)
685    }
686
687    #[must_use]
688    pub fn map<Next, F>(self, f: F) -> Source<Next, Mat>
689    where
690        Next: Send + 'static,
691        F: Fn(Out) -> Next + Send + Sync + 'static,
692    {
693        let hints = self.hints.without_inline_micro();
694        Source {
695            factory: Arc::new(MapSourceFactory {
696                source: self.factory,
697                stage: f,
698                _marker: PhantomData,
699            }),
700            terminal_factory: None,
701            hints,
702            attributes: self.attributes,
703            split_hook: None,
704        }
705    }
706
707    #[must_use]
708    pub fn attributes(&self) -> &Attributes {
709        &self.attributes
710    }
711
712    #[must_use]
713    pub fn with_attributes(mut self, attributes: Attributes) -> Self {
714        self.attributes = attributes;
715        self
716    }
717
718    #[must_use]
719    pub fn add_attributes(mut self, attributes: Attributes) -> Self {
720        self.attributes = self.attributes.and(attributes);
721        self
722    }
723
724    #[must_use]
725    pub fn named(self, name: impl Into<String>) -> Self {
726        self.add_attributes(Attributes::named(name))
727    }
728
729    /// Insert an opt-in instrumentation boundary after this source.
730    ///
731    /// A fresh run is registered in `registry` every time the returned source is
732    /// materialized. The boundary counts successful elements with relaxed atomics
733    /// and records running/draining/completed/failed state for that materialization.
734    ///
735    /// This is intentionally an explicit wrapper instead of an [`Attributes`] flag:
736    /// ordinary streams that do not call `instrumented` keep the exact same blueprint
737    /// and hot path.
738    #[must_use]
739    pub fn instrumented(
740        self,
741        name: impl Into<String>,
742        registry: &StreamInstrumentationRegistry,
743    ) -> Self {
744        let Source {
745            factory,
746            terminal_factory: _,
747            hints,
748            attributes,
749            split_hook: _,
750        } = self;
751        let name: Arc<str> = Arc::from(name.into());
752        let registry = registry.clone();
753        let mut source = Source::from_materialized_factory_with_hints(
754            move |materializer| {
755                let run = registry.register(name.as_ref().to_owned());
756                match Arc::clone(&factory).create(materializer) {
757                    Ok((stream, mat)) => Ok((
758                        Box::new(InstrumentedStream::new(stream, run)) as BoxStream<Out>,
759                        mat,
760                    )),
761                    Err(error) => {
762                        run.mark_failed();
763                        Err(error)
764                    }
765                }
766            },
767            hints.without_inline_micro(),
768        );
769        source.attributes = attributes;
770        source
771    }
772
773    /// Insert an async boundary after this source.
774    ///
775    /// The boundary uses the same [`crate::AsyncBoundaryExecutionConfig`] defaults
776    /// as the GraphDSL `AsyncBoundary` runner and hands elements across a bounded
777    /// Ractor-backed queue. `async_boundary` is the Rust-friendly primary name;
778    /// `Source::r#async` is provided as the Akka-mirroring alias.
779    #[must_use]
780    pub fn async_boundary(self) -> Self {
781        self.via(Flow::identity().async_boundary())
782    }
783
784    /// Akka-mirroring alias for [`Source::async_boundary`].
785    #[must_use]
786    pub fn r#async(self) -> Self {
787        self.async_boundary()
788    }
789
790    /// Insert an async boundary with an explicit bounded handoff configuration.
791    ///
792    /// `config.buffer_size` controls the number of elements that may be queued
793    /// between the upstream and downstream fused regions. A zero buffer is
794    /// rejected when the stream is materialized.
795    #[must_use]
796    pub fn async_boundary_with_config(
797        self,
798        config: crate::graph::AsyncBoundaryExecutionConfig,
799    ) -> Self {
800        self.via(Flow::identity().async_boundary_with_config(config))
801    }
802
803    /// Insert an async boundary with a custom bounded handoff size.
804    #[must_use]
805    pub fn async_boundary_with_buffer(self, buffer_size: usize) -> Self {
806        self.via(Flow::identity().async_boundary_with_buffer(buffer_size))
807    }
808
809    #[must_use]
810    pub fn try_map<Next, F>(self, f: F) -> Source<Next, Mat>
811    where
812        Next: Send + 'static,
813        F: Fn(Out) -> StreamResult<Next> + Send + Sync + 'static,
814    {
815        self.via(Flow::identity().try_map(f))
816    }
817
818    /// Deprecated alias for [`Source::try_map`].
819    #[must_use]
820    #[deprecated(
821        since = "0.9.0",
822        note = "renamed to `try_map` (idiomatic); the `_result` name still works"
823    )]
824    pub fn map_result<Next, F>(self, f: F) -> Source<Next, Mat>
825    where
826        Next: Send + 'static,
827        F: Fn(Out) -> StreamResult<Next> + Send + Sync + 'static,
828    {
829        self.try_map(f)
830    }
831
832    #[must_use]
833    pub fn map_result_with_supervision<Next, F>(
834        self,
835        f: F,
836        decider: SupervisionDecider,
837    ) -> Source<Next, Mat>
838    where
839        Next: Send + 'static,
840        F: Fn(Out) -> StreamResult<Next> + Send + Sync + 'static,
841    {
842        self.via(Flow::identity().map_result_with_supervision(f, decider))
843    }
844
845    #[must_use]
846    pub fn filter<F>(self, predicate: F) -> Source<Out, Mat>
847    where
848        F: Fn(&Out) -> bool + Send + Sync + 'static,
849    {
850        self.via(Flow::identity().filter(predicate))
851    }
852
853    #[must_use]
854    pub fn try_filter<F>(self, predicate: F) -> Source<Out, Mat>
855    where
856        F: Fn(&Out) -> StreamResult<bool> + Send + Sync + 'static,
857    {
858        self.via(Flow::identity().try_filter(predicate))
859    }
860
861    /// Deprecated alias for [`Source::try_filter`].
862    #[must_use]
863    #[deprecated(
864        since = "0.9.0",
865        note = "renamed to `try_filter` (idiomatic); the `_result` name still works"
866    )]
867    pub fn filter_result<F>(self, predicate: F) -> Source<Out, Mat>
868    where
869        F: Fn(&Out) -> StreamResult<bool> + Send + Sync + 'static,
870    {
871        self.try_filter(predicate)
872    }
873
874    #[must_use]
875    pub fn filter_result_with_supervision<F>(
876        self,
877        predicate: F,
878        decider: SupervisionDecider,
879    ) -> Source<Out, Mat>
880    where
881        F: Fn(&Out) -> StreamResult<bool> + Send + Sync + 'static,
882    {
883        self.via(Flow::identity().filter_result_with_supervision(predicate, decider))
884    }
885
886    #[must_use]
887    pub fn filter_not<F>(self, predicate: F) -> Source<Out, Mat>
888    where
889        F: Fn(&Out) -> bool + Send + Sync + 'static,
890    {
891        self.via(Flow::identity().filter_not(predicate))
892    }
893
894    #[must_use]
895    pub fn filter_map<Next, F>(self, f: F) -> Source<Next, Mat>
896    where
897        Next: Send + 'static,
898        F: Fn(Out) -> Option<Next> + Send + Sync + 'static,
899    {
900        self.via(Flow::identity().filter_map(f))
901    }
902
903    #[must_use]
904    pub fn try_filter_map<Next, F>(self, f: F) -> Source<Next, Mat>
905    where
906        Next: Send + 'static,
907        F: Fn(Out) -> StreamResult<Option<Next>> + Send + Sync + 'static,
908    {
909        self.via(Flow::identity().try_filter_map(f))
910    }
911
912    /// Deprecated alias for [`Source::try_filter_map`].
913    #[must_use]
914    #[deprecated(
915        since = "0.9.0",
916        note = "renamed to `try_filter_map` (idiomatic); the `_result` name still works"
917    )]
918    pub fn filter_map_result<Next, F>(self, f: F) -> Source<Next, Mat>
919    where
920        Next: Send + 'static,
921        F: Fn(Out) -> StreamResult<Option<Next>> + Send + Sync + 'static,
922    {
923        self.try_filter_map(f)
924    }
925
926    #[must_use]
927    pub fn filter_map_result_with_supervision<Next, F>(
928        self,
929        f: F,
930        decider: SupervisionDecider,
931    ) -> Source<Next, Mat>
932    where
933        Next: Send + 'static,
934        F: Fn(Out) -> StreamResult<Option<Next>> + Send + Sync + 'static,
935    {
936        self.via(Flow::identity().filter_map_result_with_supervision(f, decider))
937    }
938
939    #[must_use]
940    pub fn map_concat<Next, F, I>(self, f: F) -> Source<Next, Mat>
941    where
942        Next: Send + 'static,
943        F: Fn(Out) -> I + Send + Sync + 'static,
944        I: IntoIterator<Item = Next>,
945        I::IntoIter: Send + 'static,
946    {
947        self.via(Flow::identity().map_concat(f))
948    }
949
950    #[must_use]
951    pub fn try_map_concat<Next, F, I>(self, f: F) -> Source<Next, Mat>
952    where
953        Next: Send + 'static,
954        F: Fn(Out) -> StreamResult<I> + Send + Sync + 'static,
955        I: IntoIterator<Item = Next>,
956        I::IntoIter: Send + 'static,
957    {
958        self.via(Flow::identity().try_map_concat(f))
959    }
960
961    /// Deprecated alias for [`Source::try_map_concat`].
962    #[must_use]
963    #[deprecated(
964        since = "0.9.0",
965        note = "renamed to `try_map_concat` (idiomatic); the `_result` name still works"
966    )]
967    pub fn map_concat_result<Next, F, I>(self, f: F) -> Source<Next, Mat>
968    where
969        Next: Send + 'static,
970        F: Fn(Out) -> StreamResult<I> + Send + Sync + 'static,
971        I: IntoIterator<Item = Next>,
972        I::IntoIter: Send + 'static,
973    {
974        self.try_map_concat(f)
975    }
976
977    #[must_use]
978    pub fn map_concat_result_with_supervision<Next, F, I>(
979        self,
980        f: F,
981        decider: SupervisionDecider,
982    ) -> Source<Next, Mat>
983    where
984        Next: Send + 'static,
985        F: Fn(Out) -> StreamResult<I> + Send + Sync + 'static,
986        I: IntoIterator<Item = Next>,
987        I::IntoIter: Send + 'static,
988    {
989        self.via(Flow::identity().map_concat_result_with_supervision(f, decider))
990    }
991
992    #[must_use]
993    pub fn stateful_map<State, Next, F>(self, seed: State, f: F) -> Source<Next, Mat>
994    where
995        State: Clone + Send + Sync + 'static,
996        Next: Send + 'static,
997        F: Fn(&mut State, Out) -> Next + Send + Sync + 'static,
998    {
999        self.via(Flow::identity().stateful_map(seed, f))
1000    }
1001
1002    #[must_use]
1003    pub fn try_stateful_map<State, Next, F>(self, seed: State, f: F) -> Source<Next, Mat>
1004    where
1005        State: Clone + Send + Sync + 'static,
1006        Next: Send + 'static,
1007        F: Fn(&mut State, Out) -> StreamResult<Next> + Send + Sync + 'static,
1008    {
1009        self.via(Flow::identity().try_stateful_map(seed, f))
1010    }
1011
1012    /// Deprecated alias for [`Source::try_stateful_map`].
1013    #[must_use]
1014    #[deprecated(
1015        since = "0.9.0",
1016        note = "renamed to `try_stateful_map` (idiomatic); the `_result` name still works"
1017    )]
1018    pub fn stateful_map_result<State, Next, F>(self, seed: State, f: F) -> Source<Next, Mat>
1019    where
1020        State: Clone + Send + Sync + 'static,
1021        Next: Send + 'static,
1022        F: Fn(&mut State, Out) -> StreamResult<Next> + Send + Sync + 'static,
1023    {
1024        self.try_stateful_map(seed, f)
1025    }
1026
1027    #[must_use]
1028    pub fn stateful_map_result_with_supervision<State, Next, F>(
1029        self,
1030        seed: State,
1031        f: F,
1032        decider: SupervisionDecider,
1033    ) -> Source<Next, Mat>
1034    where
1035        State: Clone + Send + Sync + 'static,
1036        Next: Send + 'static,
1037        F: Fn(&mut State, Out) -> StreamResult<Next> + Send + Sync + 'static,
1038    {
1039        self.via(Flow::identity().stateful_map_result_with_supervision(seed, f, decider))
1040    }
1041
1042    #[must_use]
1043    pub fn stateful_map_concat<State, Next, F, I>(self, seed: State, f: F) -> Source<Next, Mat>
1044    where
1045        State: Clone + Send + Sync + 'static,
1046        Next: Send + 'static,
1047        F: Fn(&mut State, Out) -> I + Send + Sync + 'static,
1048        I: IntoIterator<Item = Next>,
1049        I::IntoIter: Send + 'static,
1050    {
1051        self.via(Flow::identity().stateful_map_concat(seed, f))
1052    }
1053
1054    #[must_use]
1055    pub fn try_stateful_map_concat<State, Next, F, I>(self, seed: State, f: F) -> Source<Next, Mat>
1056    where
1057        State: Clone + Send + Sync + 'static,
1058        Next: Send + 'static,
1059        F: Fn(&mut State, Out) -> StreamResult<I> + Send + Sync + 'static,
1060        I: IntoIterator<Item = Next>,
1061        I::IntoIter: Send + 'static,
1062    {
1063        self.via(Flow::identity().try_stateful_map_concat(seed, f))
1064    }
1065
1066    /// Deprecated alias for [`Source::try_stateful_map_concat`].
1067    #[must_use]
1068    #[deprecated(
1069        since = "0.9.0",
1070        note = "renamed to `try_stateful_map_concat` (idiomatic); the `_result` name still works"
1071    )]
1072    pub fn stateful_map_concat_result<State, Next, F, I>(
1073        self,
1074        seed: State,
1075        f: F,
1076    ) -> Source<Next, Mat>
1077    where
1078        State: Clone + Send + Sync + 'static,
1079        Next: Send + 'static,
1080        F: Fn(&mut State, Out) -> StreamResult<I> + Send + Sync + 'static,
1081        I: IntoIterator<Item = Next>,
1082        I::IntoIter: Send + 'static,
1083    {
1084        self.try_stateful_map_concat(seed, f)
1085    }
1086
1087    #[must_use]
1088    pub fn stateful_map_concat_result_with_supervision<State, Next, F, I>(
1089        self,
1090        seed: State,
1091        f: F,
1092        decider: SupervisionDecider,
1093    ) -> Source<Next, Mat>
1094    where
1095        State: Clone + Send + Sync + 'static,
1096        Next: Send + 'static,
1097        F: Fn(&mut State, Out) -> StreamResult<I> + Send + Sync + 'static,
1098        I: IntoIterator<Item = Next>,
1099        I::IntoIter: Send + 'static,
1100    {
1101        self.via(Flow::identity().stateful_map_concat_result_with_supervision(seed, f, decider))
1102    }
1103
1104    #[must_use]
1105    pub fn map_async<Next, F, Fut>(self, parallelism: usize, f: F) -> Source<Next, Mat>
1106    where
1107        Next: Send + 'static,
1108        F: Fn(Out) -> Fut + Send + Sync + 'static,
1109        Fut: Future<Output = StreamResult<Next>> + Send + 'static,
1110    {
1111        self.via(Flow::identity().map_async(parallelism, f))
1112    }
1113
1114    #[must_use]
1115    pub fn map_async_with_supervision<Next, F, Fut>(
1116        self,
1117        parallelism: usize,
1118        f: F,
1119        decider: SupervisionDecider,
1120    ) -> Source<Next, Mat>
1121    where
1122        Next: Send + 'static,
1123        F: Fn(Out) -> Fut + Send + Sync + 'static,
1124        Fut: Future<Output = StreamResult<Next>> + Send + 'static,
1125    {
1126        self.via(Flow::identity().map_async_with_supervision(parallelism, f, decider))
1127    }
1128
1129    #[must_use]
1130    pub fn map_async_unordered<Next, F, Fut>(self, parallelism: usize, f: F) -> Source<Next, Mat>
1131    where
1132        Next: Send + 'static,
1133        F: Fn(Out) -> Fut + Send + Sync + 'static,
1134        Fut: Future<Output = StreamResult<Next>> + Send + 'static,
1135    {
1136        self.via(Flow::identity().map_async_unordered(parallelism, f))
1137    }
1138
1139    #[must_use]
1140    pub fn map_async_unordered_with_supervision<Next, F, Fut>(
1141        self,
1142        parallelism: usize,
1143        f: F,
1144        decider: SupervisionDecider,
1145    ) -> Source<Next, Mat>
1146    where
1147        Next: Send + 'static,
1148        F: Fn(Out) -> Fut + Send + Sync + 'static,
1149        Fut: Future<Output = StreamResult<Next>> + Send + 'static,
1150    {
1151        self.via(Flow::identity().map_async_unordered_with_supervision(parallelism, f, decider))
1152    }
1153
1154    #[must_use]
1155    pub fn map_async_partitioned<Key, Next, Partition, F, Fut>(
1156        self,
1157        parallelism: usize,
1158        per_partition: usize,
1159        partition: Partition,
1160        f: F,
1161    ) -> Source<Next, Mat>
1162    where
1163        Key: Clone + Eq + Hash + Send + 'static,
1164        Next: Send + 'static,
1165        Partition: Fn(&Out) -> Key + Send + Sync + 'static,
1166        F: Fn(Out) -> Fut + Send + Sync + 'static,
1167        Fut: Future<Output = StreamResult<Next>> + Send + 'static,
1168    {
1169        self.via(Flow::identity().map_async_partitioned(parallelism, per_partition, partition, f))
1170    }
1171
1172    #[must_use]
1173    pub fn prefix_and_tail(self, n: usize) -> Source<(Vec<Out>, Source<Out>), Mat> {
1174        self.via(Flow::identity().prefix_and_tail(n))
1175    }
1176
1177    #[must_use]
1178    pub fn flat_map_prefix<Next, FlowMat, F>(self, n: usize, f: F) -> Source<Next, Mat>
1179    where
1180        Next: Send + 'static,
1181        FlowMat: Send + 'static,
1182        F: Fn(Vec<Out>) -> Flow<Out, Next, FlowMat> + Send + Sync + 'static,
1183        Out: Clone,
1184    {
1185        self.via(Flow::identity().flat_map_prefix(n, f))
1186    }
1187
1188    #[must_use]
1189    pub fn group_by<Key, F>(
1190        self,
1191        max_substreams: usize,
1192        f: F,
1193        allow_closed_substream_recreation: bool,
1194    ) -> Source<Source<Out>, Mat>
1195    where
1196        Key: Clone + Eq + Hash + Send + 'static,
1197        F: Fn(&Out) -> Key + Send + Sync + 'static,
1198        Out: Clone,
1199    {
1200        let batch_mode = if self.hints.inline_micro.is_some() && !allow_closed_substream_recreation
1201        {
1202            flow::GroupByBatchMode::FiniteEagerNoRecreate
1203        } else {
1204            flow::GroupByBatchMode::Immediate
1205        };
1206        self.via(Flow::identity().group_by_with_batching(
1207            max_substreams,
1208            f,
1209            allow_closed_substream_recreation,
1210            batch_mode,
1211        ))
1212    }
1213
1214    #[must_use]
1215    pub fn split_when<F>(self, predicate: F) -> Source<Source<Out>, Mat>
1216    where
1217        F: Fn(&Out) -> bool + Send + Sync + 'static,
1218        Out: Clone,
1219    {
1220        self.via(Flow::identity().split_when(predicate))
1221    }
1222
1223    #[must_use]
1224    pub fn split_after<F>(self, predicate: F) -> Source<Source<Out>, Mat>
1225    where
1226        F: Fn(&Out) -> bool + Send + Sync + 'static,
1227        Out: Clone,
1228    {
1229        self.via(Flow::identity().split_after(predicate))
1230    }
1231
1232    #[must_use]
1233    pub fn flat_map_concat<Next, NextMat, F>(self, f: F) -> Source<Next, Mat>
1234    where
1235        Next: Send + 'static,
1236        NextMat: Send + 'static,
1237        F: Fn(Out) -> Source<Next, NextMat> + Send + Sync + 'static,
1238    {
1239        self.via(Flow::identity().flat_map_concat(f))
1240    }
1241
1242    #[must_use]
1243    pub fn flat_map_merge<Next, NextMat, F>(self, breadth: usize, f: F) -> Source<Next, Mat>
1244    where
1245        Next: Send + 'static,
1246        NextMat: Send + 'static,
1247        F: Fn(Out) -> Source<Next, NextMat> + Send + Sync + 'static,
1248    {
1249        self.via(Flow::identity().flat_map_merge(breadth, f))
1250    }
1251
1252    #[must_use]
1253    pub fn take(self, n: usize) -> Source<Out, Mat> {
1254        self.via(Flow::identity().take(n))
1255    }
1256
1257    #[must_use]
1258    pub fn drop(self, n: usize) -> Source<Out, Mat> {
1259        self.via(Flow::identity().drop(n))
1260    }
1261
1262    #[must_use]
1263    pub fn take_while<F>(self, predicate: F) -> Source<Out, Mat>
1264    where
1265        F: Fn(&Out) -> bool + Send + Sync + 'static,
1266    {
1267        self.via(Flow::identity().take_while(predicate))
1268    }
1269
1270    #[must_use]
1271    pub fn drop_while<F>(self, predicate: F) -> Source<Out, Mat>
1272    where
1273        F: Fn(&Out) -> bool + Send + Sync + 'static,
1274    {
1275        self.via(Flow::identity().drop_while(predicate))
1276    }
1277
1278    #[must_use]
1279    pub fn limit(self, max: u64) -> Source<Out, Mat> {
1280        self.via(Flow::identity().limit(max))
1281    }
1282
1283    #[must_use]
1284    pub fn grouped(self, size: usize) -> Source<Vec<Out>, Mat> {
1285        self.via(Flow::identity().grouped(size))
1286    }
1287
1288    #[must_use]
1289    pub fn scan<State, F>(self, seed: State, f: F) -> Source<State, Mat>
1290    where
1291        State: Clone + Send + Sync + 'static,
1292        F: Fn(State, Out) -> State + Send + Sync + 'static,
1293    {
1294        self.via(Flow::identity().scan(seed, f))
1295    }
1296
1297    #[must_use]
1298    pub fn scan_async<State, F, Fut>(self, seed: State, f: F) -> Source<State, Mat>
1299    where
1300        State: Clone + Send + Sync + 'static,
1301        F: Fn(State, Out) -> Fut + Send + Sync + 'static,
1302        Fut: Future<Output = StreamResult<State>> + Send + 'static,
1303    {
1304        self.via(Flow::identity().scan_async(seed, f))
1305    }
1306
1307    #[must_use]
1308    pub fn try_scan<State, F>(self, seed: State, f: F) -> Source<State, Mat>
1309    where
1310        State: Clone + Send + Sync + 'static,
1311        F: Fn(State, Out) -> StreamResult<State> + Send + Sync + 'static,
1312    {
1313        self.via(Flow::identity().try_scan(seed, f))
1314    }
1315
1316    /// Deprecated alias for [`Source::try_scan`].
1317    #[must_use]
1318    #[deprecated(
1319        since = "0.9.0",
1320        note = "renamed to `try_scan` (idiomatic); the `_result` name still works"
1321    )]
1322    pub fn scan_result<State, F>(self, seed: State, f: F) -> Source<State, Mat>
1323    where
1324        State: Clone + Send + Sync + 'static,
1325        F: Fn(State, Out) -> StreamResult<State> + Send + Sync + 'static,
1326    {
1327        self.try_scan(seed, f)
1328    }
1329
1330    #[must_use]
1331    pub fn scan_result_with_supervision<State, F>(
1332        self,
1333        seed: State,
1334        f: F,
1335        decider: SupervisionDecider,
1336    ) -> Source<State, Mat>
1337    where
1338        State: Clone + Send + Sync + 'static,
1339        F: Fn(State, Out) -> StreamResult<State> + Send + Sync + 'static,
1340    {
1341        self.via(Flow::identity().scan_result_with_supervision(seed, f, decider))
1342    }
1343
1344    #[must_use]
1345    pub fn sliding(self, size: usize, step: usize) -> Source<Vec<Out>, Mat>
1346    where
1347        Out: Clone,
1348    {
1349        self.via(Flow::identity().sliding(size, step))
1350    }
1351
1352    #[must_use]
1353    pub fn fold<Acc, F>(self, zero: Acc, f: F) -> Source<Acc, Mat>
1354    where
1355        Acc: Clone + Send + Sync + 'static,
1356        F: Fn(Acc, Out) -> Acc + Send + Sync + 'static,
1357    {
1358        self.via(Flow::identity().fold(zero, f))
1359    }
1360
1361    #[must_use]
1362    pub fn fold_async<Acc, F, Fut>(self, zero: Acc, f: F) -> Source<Acc, Mat>
1363    where
1364        Acc: Clone + Send + Sync + 'static,
1365        F: Fn(Acc, Out) -> Fut + Send + Sync + 'static,
1366        Fut: Future<Output = StreamResult<Acc>> + Send + 'static,
1367    {
1368        self.via(Flow::identity().fold_async(zero, f))
1369    }
1370
1371    #[must_use]
1372    pub fn map_with_resource<Resource, Next, Create, F, Close>(
1373        self,
1374        create: Create,
1375        f: F,
1376        close: Close,
1377    ) -> Source<Next, Mat>
1378    where
1379        Resource: Send + 'static,
1380        Next: Send + 'static,
1381        Create: Fn() -> StreamResult<Resource> + Send + Sync + 'static,
1382        F: Fn(&mut Resource, Out) -> StreamResult<Next> + Send + Sync + 'static,
1383        Close: Fn(Resource) -> StreamResult<Option<Next>> + Send + Sync + 'static,
1384    {
1385        self.via(Flow::identity().map_with_resource(create, f, close))
1386    }
1387
1388    #[must_use]
1389    pub fn try_fold<Acc, F>(self, zero: Acc, f: F) -> Source<Acc, Mat>
1390    where
1391        Acc: Clone + Send + Sync + 'static,
1392        F: Fn(Acc, Out) -> StreamResult<Acc> + Send + Sync + 'static,
1393    {
1394        self.via(Flow::identity().try_fold(zero, f))
1395    }
1396
1397    /// Deprecated alias for [`Source::try_fold`].
1398    #[must_use]
1399    #[deprecated(
1400        since = "0.9.0",
1401        note = "renamed to `try_fold` (idiomatic); the `_result` name still works"
1402    )]
1403    pub fn fold_result<Acc, F>(self, zero: Acc, f: F) -> Source<Acc, Mat>
1404    where
1405        Acc: Clone + Send + Sync + 'static,
1406        F: Fn(Acc, Out) -> StreamResult<Acc> + Send + Sync + 'static,
1407    {
1408        self.try_fold(zero, f)
1409    }
1410
1411    #[must_use]
1412    pub fn fold_result_with_supervision<Acc, F>(
1413        self,
1414        zero: Acc,
1415        f: F,
1416        decider: SupervisionDecider,
1417    ) -> Source<Acc, Mat>
1418    where
1419        Acc: Clone + Send + Sync + 'static,
1420        F: Fn(Acc, Out) -> StreamResult<Acc> + Send + Sync + 'static,
1421    {
1422        self.via(Flow::identity().fold_result_with_supervision(zero, f, decider))
1423    }
1424
1425    #[must_use]
1426    pub fn reduce<F>(self, f: F) -> Source<Out, Mat>
1427    where
1428        F: Fn(Out, Out) -> Out + Send + Sync + 'static,
1429    {
1430        self.via(Flow::identity().reduce(f))
1431    }
1432
1433    #[must_use]
1434    pub fn try_reduce<F>(self, f: F) -> Source<Out, Mat>
1435    where
1436        Out: Clone,
1437        F: Fn(Out, Out) -> StreamResult<Out> + Send + Sync + 'static,
1438    {
1439        self.via(Flow::identity().try_reduce(f))
1440    }
1441
1442    /// Deprecated alias for [`Source::try_reduce`].
1443    #[must_use]
1444    #[deprecated(
1445        since = "0.9.0",
1446        note = "renamed to `try_reduce` (idiomatic); the `_result` name still works"
1447    )]
1448    pub fn reduce_result<F>(self, f: F) -> Source<Out, Mat>
1449    where
1450        Out: Clone,
1451        F: Fn(Out, Out) -> StreamResult<Out> + Send + Sync + 'static,
1452    {
1453        self.try_reduce(f)
1454    }
1455
1456    #[must_use]
1457    pub fn reduce_result_with_supervision<F>(
1458        self,
1459        f: F,
1460        decider: SupervisionDecider,
1461    ) -> Source<Out, Mat>
1462    where
1463        Out: Clone,
1464        F: Fn(Out, Out) -> StreamResult<Out> + Send + Sync + 'static,
1465    {
1466        self.via(Flow::identity().reduce_result_with_supervision(f, decider))
1467    }
1468
1469    #[must_use]
1470    pub fn map_error<F>(self, f: F) -> Source<Out, Mat>
1471    where
1472        F: Fn(StreamError) -> StreamError + Send + Sync + 'static,
1473    {
1474        self.via(Flow::identity().map_error(f))
1475    }
1476
1477    #[must_use]
1478    pub fn recover<F>(self, f: F) -> Source<Out, Mat>
1479    where
1480        F: Fn(StreamError) -> Option<Out> + Send + Sync + 'static,
1481    {
1482        self.via(Flow::identity().recover(f))
1483    }
1484
1485    #[must_use]
1486    pub fn recover_with<F>(self, f: F) -> Source<Out, Mat>
1487    where
1488        F: Fn(StreamError) -> Option<Source<Out>> + Send + Sync + 'static,
1489    {
1490        self.via(Flow::identity().recover_with(f))
1491    }
1492
1493    #[must_use]
1494    pub fn recover_with_retries<F>(self, retries: usize, f: F) -> Source<Out, Mat>
1495    where
1496        F: Fn(StreamError) -> Option<Source<Out>> + Send + Sync + 'static,
1497    {
1498        self.via(Flow::identity().recover_with_retries(retries, f))
1499    }
1500
1501    #[must_use]
1502    pub fn on_error_complete(self) -> Source<Out, Mat> {
1503        self.via(Flow::identity().on_error_complete())
1504    }
1505
1506    #[must_use]
1507    pub fn concat<Mat2>(self, that: Source<Out, Mat2>) -> Source<Out, Mat>
1508    where
1509        Mat2: Send + 'static,
1510    {
1511        let factory = self.factory;
1512        let hints = self.hints;
1513        let that_factory = that.factory;
1514        Source::from_materialized_factory_with_hints(
1515            move |materializer| {
1516                let (primary, mat) = Arc::clone(&factory).create(materializer)?;
1517                let secondary = match Arc::clone(&that_factory).create(materializer) {
1518                    Ok((stream, _)) => stream,
1519                    Err(error) => Box::new(std::iter::once(Err(error))) as BoxStream<Out>,
1520                };
1521                Ok((concat_source_streams(vec![primary, secondary]), mat))
1522            },
1523            hints,
1524        )
1525    }
1526
1527    #[must_use]
1528    pub fn concat_lazy<Mat2>(self, that: Source<Out, Mat2>) -> Source<Out, Mat>
1529    where
1530        Mat2: Send + 'static,
1531    {
1532        let factory = self.factory;
1533        let hints = self.hints;
1534        let that_factory = that.factory;
1535        Source::from_materialized_factory_with_hints(
1536            move |materializer| {
1537                let (primary, mat) = Arc::clone(&factory).create(materializer)?;
1538                Ok((
1539                    concat_source_streams_lazy(
1540                        primary,
1541                        vec![Arc::clone(&that_factory)],
1542                        materializer,
1543                    ),
1544                    mat,
1545                ))
1546            },
1547            hints,
1548        )
1549    }
1550
1551    #[must_use]
1552    pub fn concat_all_lazy<Mat2, I>(self, those: I) -> Source<Out, Mat>
1553    where
1554        Mat2: Send + 'static,
1555        I: IntoIterator<Item = Source<Out, Mat2>>,
1556    {
1557        let factory = self.factory;
1558        let hints = self.hints;
1559        let other_factories: Vec<_> = those.into_iter().map(|source| source.factory).collect();
1560        Source::from_materialized_factory_with_hints(
1561            move |materializer| {
1562                let (primary, mat) = Arc::clone(&factory).create(materializer)?;
1563                Ok((
1564                    concat_source_streams_lazy(primary, other_factories.clone(), materializer),
1565                    mat,
1566                ))
1567            },
1568            hints,
1569        )
1570    }
1571
1572    #[must_use]
1573    pub fn prepend<Mat2>(self, that: Source<Out, Mat2>) -> Source<Out, Mat>
1574    where
1575        Mat2: Send + 'static,
1576    {
1577        let factory = self.factory;
1578        let hints = self.hints;
1579        let that_factory = that.factory;
1580        Source::from_materialized_factory_with_hints(
1581            move |materializer| {
1582                let (primary, mat) = Arc::clone(&factory).create(materializer)?;
1583                let secondary = match Arc::clone(&that_factory).create(materializer) {
1584                    Ok((stream, _)) => stream,
1585                    Err(error) => Box::new(std::iter::once(Err(error))) as BoxStream<Out>,
1586                };
1587                Ok((concat_source_streams(vec![secondary, primary]), mat))
1588            },
1589            hints,
1590        )
1591    }
1592
1593    #[must_use]
1594    pub fn prepend_lazy<Mat2>(self, that: Source<Out, Mat2>) -> Source<Out, Mat>
1595    where
1596        Mat2: Send + 'static,
1597    {
1598        self.prepend(that)
1599    }
1600
1601    #[must_use]
1602    pub fn or_else<Mat2>(self, secondary: Source<Out, Mat2>) -> Source<Out, Mat>
1603    where
1604        Mat2: Send + 'static,
1605    {
1606        let factory = self.factory;
1607        let hints = self.hints;
1608        let secondary_factory = secondary.factory;
1609        Source::from_materialized_factory_with_hints(
1610            move |materializer| {
1611                let (primary, mat) = Arc::clone(&factory).create(materializer)?;
1612                let secondary = match Arc::clone(&secondary_factory).create(materializer) {
1613                    Ok((stream, _)) => stream,
1614                    Err(error) => Box::new(std::iter::once(Err(error))) as BoxStream<Out>,
1615                };
1616                Ok((or_else_source_stream(primary, secondary), mat))
1617            },
1618            hints,
1619        )
1620    }
1621
1622    #[must_use]
1623    pub fn interleave<Mat2>(self, that: Source<Out, Mat2>, segment_size: usize) -> Source<Out, Mat>
1624    where
1625        Mat2: Send + 'static,
1626    {
1627        self.interleave_all([that], segment_size, false)
1628    }
1629
1630    #[must_use]
1631    pub fn interleave_all<Mat2, I>(
1632        self,
1633        those: I,
1634        segment_size: usize,
1635        eager_close: bool,
1636    ) -> Source<Out, Mat>
1637    where
1638        Mat2: Send + 'static,
1639        I: IntoIterator<Item = Source<Out, Mat2>>,
1640    {
1641        let factory = self.factory;
1642        let hints = self.hints;
1643        let other_factories: Vec<_> = those.into_iter().map(|source| source.factory).collect();
1644        Source::from_materialized_factory_with_hints(
1645            move |materializer| {
1646                let (primary, mat) = Arc::clone(&factory).create(materializer)?;
1647                let mut streams = Vec::with_capacity(other_factories.len() + 1);
1648                streams.push(primary);
1649                for other in &other_factories {
1650                    let stream = match Arc::clone(other).create(materializer) {
1651                        Ok((stream, _)) => stream,
1652                        Err(error) => Box::new(std::iter::once(Err(error))) as BoxStream<Out>,
1653                    };
1654                    streams.push(stream);
1655                }
1656                Ok((
1657                    interleave_source_streams(streams, segment_size, eager_close),
1658                    mat,
1659                ))
1660            },
1661            hints,
1662        )
1663    }
1664
1665    #[must_use]
1666    pub fn merge_sorted<Mat2>(self, that: Source<Out, Mat2>) -> Source<Out, Mat>
1667    where
1668        Out: Ord,
1669        Mat2: Send + 'static,
1670    {
1671        self.via(Flow::identity().merge_sorted(that))
1672    }
1673
1674    #[must_use]
1675    pub fn merge_latest<Mat2>(
1676        self,
1677        that: Source<Out, Mat2>,
1678        eager_complete: bool,
1679    ) -> Source<Vec<Out>, Mat>
1680    where
1681        Out: Clone,
1682        Mat2: Send + 'static,
1683    {
1684        let factory = self.factory;
1685        let hints = self.hints;
1686        let that_factory = that.factory;
1687        Source::from_materialized_factory_with_hints(
1688            move |materializer| {
1689                let (left, mat) = Arc::clone(&factory).create(materializer)?;
1690                let right = match Arc::clone(&that_factory).create(materializer) {
1691                    Ok((stream, _)) => stream,
1692                    Err(error) => {
1693                        return Ok((
1694                            Box::new(std::iter::once(Err(error))) as BoxStream<Vec<Out>>,
1695                            mat,
1696                        ));
1697                    }
1698                };
1699                Ok((merge_latest_streams(vec![left, right], eager_complete), mat))
1700            },
1701            hints,
1702        )
1703    }
1704
1705    #[must_use]
1706    pub fn merge_all<Mat2, I>(self, those: I, eager_complete: bool) -> Source<Out, Mat>
1707    where
1708        Mat2: Send + 'static,
1709        I: IntoIterator<Item = Source<Out, Mat2>>,
1710    {
1711        let factory = self.factory;
1712        let hints = self.hints;
1713        let other_factories: Vec<_> = those.into_iter().map(|source| source.factory).collect();
1714        Source::from_materialized_factory_with_hints(
1715            move |materializer| {
1716                let (primary, mat) = Arc::clone(&factory).create(materializer)?;
1717                let mut streams = Vec::with_capacity(other_factories.len() + 1);
1718                streams.push(primary);
1719                for other in &other_factories {
1720                    let stream = match Arc::clone(other).create(materializer) {
1721                        Ok((stream, _)) => stream,
1722                        Err(error) => {
1723                            return Ok((
1724                                Box::new(std::iter::once(Err(error))) as BoxStream<Out>,
1725                                mat,
1726                            ));
1727                        }
1728                    };
1729                    streams.push(stream);
1730                }
1731                Ok((merge_streams(streams, eager_complete), mat))
1732            },
1733            hints,
1734        )
1735    }
1736
1737    #[must_use]
1738    pub fn zip_with<Mat2, Out2, Next, F>(
1739        self,
1740        that: Source<Out2, Mat2>,
1741        combine: F,
1742    ) -> Source<Next, Mat>
1743    where
1744        Out2: Send + 'static,
1745        Next: Send + 'static,
1746        Mat2: Send + 'static,
1747        F: Fn(Out, Out2) -> Next + Send + Sync + 'static,
1748    {
1749        self.via(Flow::identity().zip_with(that, combine))
1750    }
1751
1752    #[must_use]
1753    pub fn zip_latest<Mat2, Out2>(self, that: Source<Out2, Mat2>) -> Source<(Out, Out2), Mat>
1754    where
1755        Out: Clone,
1756        Out2: Clone + Send + 'static,
1757        Mat2: Send + 'static,
1758    {
1759        self.zip_latest_with(that, true, |left, right| (left, right))
1760    }
1761
1762    #[must_use]
1763    pub fn zip_latest_with<Mat2, Out2, Next, F>(
1764        self,
1765        that: Source<Out2, Mat2>,
1766        eager_complete: bool,
1767        combine: F,
1768    ) -> Source<Next, Mat>
1769    where
1770        Out: Clone,
1771        Out2: Clone + Send + 'static,
1772        Next: Send + 'static,
1773        Mat2: Send + 'static,
1774        F: Fn(Out, Out2) -> Next + Send + Sync + 'static,
1775    {
1776        let factory = self.factory;
1777        let hints = self.hints;
1778        let that_factory = that.factory;
1779        let combine = Arc::new(combine);
1780        Source::from_materialized_factory_with_hints(
1781            move |materializer| {
1782                let (left, mat) = Arc::clone(&factory).create(materializer)?;
1783                let right = match Arc::clone(&that_factory).create(materializer) {
1784                    Ok((stream, _)) => stream,
1785                    Err(error) => {
1786                        return Ok((
1787                            Box::new(std::iter::once(Err(error))) as BoxStream<Next>,
1788                            mat,
1789                        ));
1790                    }
1791                };
1792                Ok((
1793                    zip_latest_with_stream(left, right, eager_complete, Arc::clone(&combine)),
1794                    mat,
1795                ))
1796            },
1797            hints,
1798        )
1799    }
1800
1801    #[must_use]
1802    pub fn zip_with_index(self) -> Source<(Out, u64), Mat> {
1803        let factory = self.factory;
1804        let hints = self.hints;
1805        Source::from_materialized_factory_with_hints(
1806            move |materializer| {
1807                let (mut stream, mat) = Arc::clone(&factory).create(materializer)?;
1808                let mut index = 0_u64;
1809                Ok((
1810                    Box::new(std::iter::from_fn(move || {
1811                        stream.next().map(|item| {
1812                            item.map(|value| {
1813                                let pair = (value, index);
1814                                index = index.wrapping_add(1);
1815                                pair
1816                            })
1817                        })
1818                    })) as BoxStream<(Out, u64)>,
1819                    mat,
1820                ))
1821            },
1822            hints,
1823        )
1824    }
1825
1826    #[must_use]
1827    pub fn zip_all<Mat2, Out2>(
1828        self,
1829        that: Source<Out2, Mat2>,
1830        this_elem: Out,
1831        that_elem: Out2,
1832    ) -> Source<(Out, Out2), Mat>
1833    where
1834        Out: Clone + Sync,
1835        Out2: Clone + Send + Sync + 'static,
1836        Mat2: Send + 'static,
1837    {
1838        let factory = self.factory;
1839        let hints = self.hints;
1840        let that_factory = that.factory;
1841        Source::from_materialized_factory_with_hints(
1842            move |materializer| {
1843                let (left, mat) = Arc::clone(&factory).create(materializer)?;
1844                let right = match Arc::clone(&that_factory).create(materializer) {
1845                    Ok((stream, _)) => stream,
1846                    Err(error) => {
1847                        return Ok((
1848                            Box::new(std::iter::once(Err(error))) as BoxStream<(Out, Out2)>,
1849                            mat,
1850                        ));
1851                    }
1852                };
1853                Ok((
1854                    zip_all_stream(left, right, this_elem.clone(), that_elem.clone()),
1855                    mat,
1856                ))
1857            },
1858            hints,
1859        )
1860    }
1861
1862    #[must_use]
1863    pub fn also_to<SinkMat>(self, sink: Sink<Out, SinkMat>) -> Source<Out, Mat>
1864    where
1865        Out: Clone,
1866        SinkMat: Send + 'static,
1867    {
1868        self.via(Flow::identity().also_to(sink))
1869    }
1870
1871    #[must_use]
1872    pub fn also_to_all<SinkMat, I>(self, sinks: I) -> Source<Out, Mat>
1873    where
1874        Out: Clone,
1875        SinkMat: Send + 'static,
1876        I: IntoIterator<Item = Sink<Out, SinkMat>>,
1877    {
1878        self.via(Flow::identity().also_to_all(sinks))
1879    }
1880
1881    #[must_use]
1882    pub fn divert_to<SinkMat, F>(self, sink: Sink<Out, SinkMat>, predicate: F) -> Source<Out, Mat>
1883    where
1884        SinkMat: Send + 'static,
1885        F: Fn(&Out) -> bool + Send + Sync + 'static,
1886    {
1887        self.via(Flow::identity().divert_to(sink, predicate))
1888    }
1889
1890    #[must_use]
1891    pub fn wire_tap<SinkMat>(self, sink: Sink<Out, SinkMat>) -> Source<Out, Mat>
1892    where
1893        Out: Clone,
1894        SinkMat: Send + 'static,
1895    {
1896        self.via(Flow::identity().wire_tap(sink))
1897    }
1898
1899    pub fn run_with<SinkMat: Send + 'static>(
1900        self,
1901        sink: Sink<Out, SinkMat>,
1902    ) -> StreamResult<SinkMat> {
1903        // Split-segment fast path: if source has a segment hook and sink has a
1904        // fold fast-path descriptor, drive the fold inline without spawning a task.
1905        let fast_result = self
1906            .split_hook
1907            .as_ref()
1908            .zip(sink.fold_fp.as_deref())
1909            .and_then(|(hook, fp)| fp.try_register(Arc::clone(hook)));
1910        if let Some(result) = fast_result {
1911            return result?.downcast::<SinkMat>().map(|b| *b).map_err(|_| {
1912                StreamError::Failed("split fast path: unexpected mat type (internal error)".into())
1913            });
1914        }
1915        self.to_mat(sink, Keep::right).run()
1916    }
1917
1918    pub fn run_with_materializer<SinkMat: Send + 'static>(
1919        self,
1920        sink: Sink<Out, SinkMat>,
1921        materializer: &Materializer,
1922    ) -> StreamResult<SinkMat> {
1923        self.to_mat(sink, Keep::right)
1924            .run_with_materializer(materializer)
1925    }
1926
1927    #[must_use]
1928    pub fn to<SinkMat>(self, sink: Sink<Out, SinkMat>) -> RunnableGraph<Mat>
1929    where
1930        SinkMat: Send + 'static,
1931    {
1932        self.to_mat(sink, Keep::left)
1933    }
1934
1935    #[must_use]
1936    pub fn to_mat<SinkMat, Combined, F>(
1937        self,
1938        sink: Sink<Out, SinkMat>,
1939        combine: F,
1940    ) -> RunnableGraph<Combined>
1941    where
1942        SinkMat: Send + 'static,
1943        Combined: Send + 'static,
1944        F: Fn(Mat, SinkMat) -> Combined + Send + Sync + 'static,
1945    {
1946        let factory = self.factory;
1947        let terminal_factory = self.terminal_factory;
1948        let hints = self.hints;
1949        let combine = Arc::new(combine);
1950        RunnableGraph::from_runner(move |materializer| {
1951            if let Some(terminal_factory) = &terminal_factory
1952                && let Some(fold_fp) = sink.fold_fp.as_deref()
1953            {
1954                let (hook, source_mat) = terminal_factory(materializer)?;
1955                if hook.supports_direct_terminal()
1956                    && let Some(sink_mat) =
1957                        fold_fp.try_register_direct_terminal(Arc::clone(&hook), materializer)
1958                {
1959                    let sink_mat = sink_mat.and_then(|mat| {
1960                        mat.downcast::<SinkMat>().map(|boxed| *boxed).map_err(|_| {
1961                            StreamError::Failed(
1962                                "terminal fast path: unexpected mat type (internal error)".into(),
1963                            )
1964                        })
1965                    })?;
1966                    return Ok(combine(source_mat, sink_mat));
1967                }
1968                if !fold_fp.supports_terminal_drain() {
1969                    let (stream, source_mat) = Arc::clone(&factory).create(materializer)?;
1970                    let sink_mat = sink.run_from_source(stream, materializer, hints.runtime())?;
1971                    return Ok(combine(source_mat, sink_mat));
1972                }
1973                let sink_mat = fold_fp
1974                    .try_register_terminal_drain(hook, materializer)
1975                    .expect("terminal drain support advertised")
1976                    .and_then(|mat| {
1977                        mat.downcast::<SinkMat>().map(|boxed| *boxed).map_err(|_| {
1978                            StreamError::Failed(
1979                                "terminal fast path: unexpected mat type (internal error)".into(),
1980                            )
1981                        })
1982                    })?;
1983                return Ok(combine(source_mat, sink_mat));
1984            }
1985            let (stream, source_mat) = Arc::clone(&factory).create(materializer)?;
1986            let sink_mat = if hints.inline_head_terminal && sink.can_inline() {
1987                let stream =
1988                    runtime_checked_stream(stream, Arc::clone(&materializer.inner.state), None);
1989                sink.run_inline(stream, materializer)?
1990            } else {
1991                sink.run_from_source(stream, materializer, hints.runtime())?
1992            };
1993            Ok(combine(source_mat, sink_mat))
1994        })
1995    }
1996
1997    pub fn run_collect(self) -> StreamResult<Vec<Out>> {
1998        self.run_with(Sink::collect())?.wait()
1999    }
2000
2001    pub fn run_fold<Acc, F>(self, zero: Acc, f: F) -> StreamResult<StreamCompletion<Acc>>
2002    where
2003        Acc: Clone + Send + Sync + 'static,
2004        F: Fn(Acc, Out) -> Acc + Send + Sync + 'static,
2005    {
2006        self.run_with(Sink::fold(zero, f))
2007    }
2008
2009    pub fn run_foreach<F>(self, f: F) -> StreamResult<StreamCompletion<NotUsed>>
2010    where
2011        F: Fn(Out) + Send + Sync + 'static,
2012    {
2013        self.run_with(Sink::foreach(f))
2014    }
2015
2016    pub fn run_for_each<F>(self, f: F) -> StreamResult<StreamCompletion<NotUsed>>
2017    where
2018        F: Fn(Out) + Send + Sync + 'static,
2019    {
2020        self.run_with(Sink::foreach(f))
2021    }
2022
2023    pub fn run_reduce<F>(self, f: F) -> StreamResult<StreamCompletion<Out>>
2024    where
2025        F: Fn(Out, Out) -> Out + Send + Sync + 'static,
2026    {
2027        self.run_with(Sink::reduce(f))
2028    }
2029
2030    #[must_use]
2031    pub fn map_materialized_value<NextMat, F>(self, f: F) -> Source<Out, NextMat>
2032    where
2033        NextMat: Send + 'static,
2034        F: Fn(Mat) -> NextMat + Send + Sync + 'static,
2035    {
2036        let factory = self.factory;
2037        let terminal_factory = self.terminal_factory;
2038        let hints = self.hints;
2039        let f = Arc::new(f);
2040        let factory_f = Arc::clone(&f);
2041        let mapped_terminal_factory = terminal_factory.map(|terminal_factory| {
2042            let f = Arc::clone(&f);
2043            Arc::new(move |materializer: &Materializer| {
2044                let (hook, mat) = terminal_factory(materializer)?;
2045                Ok((hook, f(mat)))
2046            }) as Arc<TerminalSourceFactory<Out, NextMat>>
2047        });
2048        Source {
2049            factory: Arc::new(FnSourceFactory(move |materializer: &Materializer| {
2050                let (stream, mat) = Arc::clone(&factory).create(materializer)?;
2051                Ok((stream, factory_f(mat)))
2052            })),
2053            terminal_factory: mapped_terminal_factory,
2054            hints,
2055            attributes: Attributes::default(),
2056            split_hook: None,
2057        }
2058    }
2059}
2060
2061impl<Out: Clone + Send + Sync + 'static> Source<Out, NotUsed> {
2062    #[must_use]
2063    pub fn combine<Mat1, Mat2, MatRest, I>(
2064        first: Source<Out, Mat1>,
2065        second: Source<Out, Mat2>,
2066        rest: I,
2067        strategy: SourceCombineStrategy,
2068    ) -> Source<Out, NotUsed>
2069    where
2070        Mat1: Send + 'static,
2071        Mat2: Send + 'static,
2072        MatRest: Send + 'static,
2073        I: IntoIterator<Item = Source<Out, MatRest>>,
2074    {
2075        let mut factories: Vec<Arc<CombinedSourceFactory<Out>>> = vec![
2076            Arc::new(move |materializer| {
2077                Arc::clone(&first.factory)
2078                    .create(materializer)
2079                    .map(|(stream, _)| stream)
2080            }),
2081            Arc::new(move |materializer| {
2082                Arc::clone(&second.factory)
2083                    .create(materializer)
2084                    .map(|(stream, _)| stream)
2085            }),
2086        ];
2087        factories.extend(rest.into_iter().map(|source| {
2088            Arc::new(move |materializer: &Materializer| {
2089                Arc::clone(&source.factory)
2090                    .create(materializer)
2091                    .map(|(stream, _)| stream)
2092            }) as Arc<CombinedSourceFactory<Out>>
2093        }));
2094        Source::from_materialized_factory(move |materializer| {
2095            let mut streams = Vec::with_capacity(factories.len());
2096            for factory in &factories {
2097                let stream = match factory(materializer) {
2098                    Ok(stream) => stream,
2099                    Err(error) => {
2100                        return Ok((
2101                            Box::new(std::iter::once(Err(error))) as BoxStream<Out>,
2102                            NotUsed,
2103                        ));
2104                    }
2105                };
2106                streams.push(stream);
2107            }
2108            let stream = match &strategy {
2109                SourceCombineStrategy::Merge { eager_complete } => {
2110                    merge_streams(streams, *eager_complete)
2111                }
2112                SourceCombineStrategy::Concat => concat_source_streams(streams),
2113                SourceCombineStrategy::Prioritized {
2114                    priorities,
2115                    eager_complete,
2116                } => {
2117                    if priorities.len() != streams.len() {
2118                        return Err(StreamError::GraphValidation(format!(
2119                            "combine priorities length {} did not match source count {}",
2120                            priorities.len(),
2121                            streams.len()
2122                        )));
2123                    }
2124                    merge_prioritized_streams(streams, priorities.clone(), *eager_complete)
2125                }
2126            };
2127            Ok((stream, NotUsed))
2128        })
2129    }
2130
2131    #[must_use]
2132    pub fn zip_n<Mat2, I>(sources: I) -> Source<Vec<Out>, NotUsed>
2133    where
2134        I: IntoIterator<Item = Source<Out, Mat2>>,
2135        Mat2: Send + 'static,
2136        Out: Clone,
2137    {
2138        Self::zip_with_n(sources, |values| values)
2139    }
2140
2141    #[must_use]
2142    pub fn zip_with_n<Mat2, I, Next, F>(sources: I, zipper: F) -> Source<Next, NotUsed>
2143    where
2144        I: IntoIterator<Item = Source<Out, Mat2>>,
2145        Mat2: Send + 'static,
2146        Next: Send + 'static,
2147        F: Fn(Vec<Out>) -> Next + Send + Sync + 'static,
2148    {
2149        let factories: Vec<_> = sources.into_iter().map(|source| source.factory).collect();
2150        let zipper = Arc::new(zipper);
2151        Source::from_materialized_factory(move |materializer| {
2152            let mut streams = Vec::with_capacity(factories.len());
2153            for factory in &factories {
2154                let stream = match Arc::clone(factory).create(materializer) {
2155                    Ok((stream, _)) => stream,
2156                    Err(error) => {
2157                        return Ok((
2158                            Box::new(std::iter::once(Err(error))) as BoxStream<Next>,
2159                            NotUsed,
2160                        ));
2161                    }
2162                };
2163                streams.push(stream);
2164            }
2165            Ok((zip_n_streams(streams, Arc::clone(&zipper)), NotUsed))
2166        })
2167    }
2168
2169    #[must_use]
2170    pub fn merge_prioritized_n<Mat2, I>(
2171        sources_and_priorities: I,
2172        eager_complete: bool,
2173    ) -> Source<Out, NotUsed>
2174    where
2175        I: IntoIterator<Item = (Source<Out, Mat2>, usize)>,
2176        Mat2: Send + 'static,
2177    {
2178        let sources_and_priorities: Vec<_> = sources_and_priorities.into_iter().collect();
2179        if sources_and_priorities.is_empty() {
2180            return Source::empty();
2181        }
2182        let (factories, priorities): (Vec<_>, Vec<_>) = sources_and_priorities
2183            .into_iter()
2184            .map(|(source, priority)| (source.factory, priority))
2185            .unzip();
2186        Source::from_materialized_factory(move |materializer| {
2187            let mut streams = Vec::with_capacity(factories.len());
2188            for factory in &factories {
2189                let stream = match Arc::clone(factory).create(materializer) {
2190                    Ok((stream, _)) => stream,
2191                    Err(error) => {
2192                        return Ok((
2193                            Box::new(std::iter::once(Err(error))) as BoxStream<Out>,
2194                            NotUsed,
2195                        ));
2196                    }
2197                };
2198                streams.push(stream);
2199            }
2200            Ok((
2201                merge_prioritized_streams(streams, priorities.clone(), eager_complete),
2202                NotUsed,
2203            ))
2204        })
2205    }
2206
2207    #[must_use]
2208    pub fn maybe() -> (MaybeHandle<Out>, Self) {
2209        let value = Arc::new(Mutex::new(None));
2210        let handle = MaybeHandle {
2211            value: Arc::clone(&value),
2212        };
2213        let source = Self::from_factory(move || {
2214            let result = value
2215                .lock()
2216                .expect("maybe source poisoned")
2217                .clone()
2218                .unwrap_or(Err(StreamError::MaybeIncomplete));
2219            Box::new(std::iter::once(result))
2220        });
2221        (handle, source)
2222    }
2223
2224    #[must_use]
2225    pub fn single(item: Out) -> Self {
2226        Self::from_factory_with_hints(
2227            move || Box::new(std::iter::once(Ok(item.clone()))),
2228            SourceHints::with_inline_micro(1),
2229        )
2230    }
2231
2232    #[must_use]
2233    pub fn repeat(item: Out) -> Self {
2234        Self::from_factory(move || {
2235            let item = item.clone();
2236            Box::new(std::iter::repeat_with(move || Ok(item.clone())))
2237        })
2238    }
2239
2240    #[must_use]
2241    pub fn from_iterable<I>(items: I) -> Self
2242    where
2243        I: IntoIterator<Item = Out>,
2244    {
2245        items.into_iter().collect()
2246    }
2247}
2248
2249impl<Out: Clone + Send + Sync + 'static> FromIterator<Out> for Source<Out, NotUsed> {
2250    fn from_iter<T: IntoIterator<Item = Out>>(iter: T) -> Self {
2251        // Share the backing storage across materializations and clone elements
2252        // lazily on demand, instead of cloning the whole container each run.
2253        let items: Arc<[Out]> = iter.into_iter().collect();
2254        let len = items.len();
2255        Self::from_factory_with_hints(
2256            move || {
2257                let items = Arc::clone(&items);
2258                let mut index = 0;
2259                Box::new(std::iter::from_fn(move || {
2260                    let item = items.get(index)?.clone();
2261                    index += 1;
2262                    Some(Ok(item))
2263                }))
2264            },
2265            // Exact success item count: enables the coordinator inline drain path.
2266            SourceHints::with_inline_micro(len),
2267        )
2268    }
2269}
2270
2271impl<Out: Clone + Send + Sync + 'static> From<Vec<Out>> for Source<Out, NotUsed> {
2272    fn from(items: Vec<Out>) -> Self {
2273        Source::from_iter(items)
2274    }
2275}
2276
2277impl<Out: Clone + Send + Sync + 'static, const N: usize> From<[Out; N]> for Source<Out, NotUsed> {
2278    fn from(items: [Out; N]) -> Self {
2279        Source::from_iter(items)
2280    }
2281}
2282
2283/// Converts any [`IntoIterator`] into a [`Source`] explicitly.
2284///
2285/// `Vec<T>` and `[T; N]` also implement `From` for `Source<T>`, while broader
2286/// iterables such as ranges intentionally use this trait to avoid a blanket
2287/// `From<I: IntoIterator>` inference trap.
2288///
2289/// ```
2290/// use datum::{IntoSource, Source};
2291///
2292/// let via_vec_into: Source<u64> = vec![1, 2, 3].into();
2293/// let via_array_into: Source<u64> = [1, 2, 3].into();
2294/// let via_range = (0_u64..3).into_source();
2295/// let via_from_iter = Source::from_iter(0_u64..3);
2296/// let via_from_iterator: Source<u64> = (0_u64..3).collect();
2297///
2298/// let _ = (
2299///     via_vec_into,
2300///     via_array_into,
2301///     via_range,
2302///     via_from_iter,
2303///     via_from_iterator,
2304/// );
2305/// ```
2306///
2307/// ```compile_fail
2308/// use datum::Source;
2309///
2310/// let _source: Source<u64> = (0_u64..3).into();
2311/// ```
2312pub trait IntoSource: IntoIterator + Sized {
2313    #[must_use]
2314    fn into_source(self) -> Source<Self::Item, NotUsed>
2315    where
2316        Self::Item: Clone + Send + Sync + 'static,
2317    {
2318        Source::from_iter(self)
2319    }
2320}
2321
2322impl<I> IntoSource for I where I: IntoIterator {}
2323
2324/// Test-only helper: create a Source whose factory calls the given closure but
2325/// marks it as inline-micro eligible. Used to test the inline drain path with
2326/// custom (possibly blocking) iterators without broadening production
2327/// eligibility.
2328#[cfg(test)]
2329pub(in crate::stream) fn test_source_with_inline_micro_hint<Out: Send + 'static>(
2330    factory: impl Fn() -> BoxStream<Out> + Send + Sync + 'static,
2331    max_success_items: usize,
2332) -> Source<Out, NotUsed> {
2333    Source::from_factory_with_hints(factory, SourceHints::with_inline_micro(max_success_items))
2334}
2335
2336struct UnfoldResourceStream<Resource, Out, Create, Read, Close>
2337where
2338    Create: Fn() -> StreamResult<Resource>,
2339    Read: Fn(&mut Resource) -> StreamResult<Option<Out>>,
2340    Close: Fn(Resource) -> StreamResult<()>,
2341{
2342    create: Arc<Create>,
2343    read: Arc<Read>,
2344    close: Arc<Close>,
2345    resource: Option<Resource>,
2346    created: bool,
2347    terminated: bool,
2348    _marker: PhantomData<fn() -> Out>,
2349}
2350
2351impl<Resource, Out, Create, Read, Close> UnfoldResourceStream<Resource, Out, Create, Read, Close>
2352where
2353    Create: Fn() -> StreamResult<Resource>,
2354    Read: Fn(&mut Resource) -> StreamResult<Option<Out>>,
2355    Close: Fn(Resource) -> StreamResult<()>,
2356{
2357    fn ensure_created(&mut self) -> StreamResult<()> {
2358        if self.created {
2359            return Ok(());
2360        }
2361        self.created = true;
2362        let resource = catch_unwind_failed("unfold_resource create", || (self.create)())
2363            .and_then(|result| result)?;
2364        self.resource = Some(resource);
2365        Ok(())
2366    }
2367
2368    fn close_resource(&mut self) -> StreamResult<()> {
2369        match self.resource.take() {
2370            Some(resource) => {
2371                catch_unwind_failed("unfold_resource close", || (self.close)(resource))
2372                    .and_then(|result| result)
2373            }
2374            None => Ok(()),
2375        }
2376    }
2377}
2378
2379impl<Resource, Out, Create, Read, Close> Iterator
2380    for UnfoldResourceStream<Resource, Out, Create, Read, Close>
2381where
2382    Create: Fn() -> StreamResult<Resource>,
2383    Read: Fn(&mut Resource) -> StreamResult<Option<Out>>,
2384    Close: Fn(Resource) -> StreamResult<()>,
2385{
2386    type Item = StreamResult<Out>;
2387
2388    fn next(&mut self) -> Option<Self::Item> {
2389        if self.terminated {
2390            return None;
2391        }
2392        if let Err(error) = self.ensure_created() {
2393            self.terminated = true;
2394            return Some(Err(error));
2395        }
2396
2397        let result = {
2398            let resource = self
2399                .resource
2400                .as_mut()
2401                .expect("unfold_resource resource is open");
2402            catch_unwind_failed("unfold_resource read", || (self.read)(resource))
2403                .and_then(|result| result)
2404        };
2405
2406        match result {
2407            Ok(Some(item)) => Some(Ok(item)),
2408            Ok(None) => {
2409                self.terminated = true;
2410                match self.close_resource() {
2411                    Ok(()) => None,
2412                    Err(error) => Some(Err(error)),
2413                }
2414            }
2415            Err(read_error) => {
2416                self.terminated = true;
2417                let _ = self.close_resource();
2418                Some(Err(read_error))
2419            }
2420        }
2421    }
2422}
2423
2424impl<Resource, Out, Create, Read, Close> Drop
2425    for UnfoldResourceStream<Resource, Out, Create, Read, Close>
2426where
2427    Create: Fn() -> StreamResult<Resource>,
2428    Read: Fn(&mut Resource) -> StreamResult<Option<Out>>,
2429    Close: Fn(Resource) -> StreamResult<()>,
2430{
2431    fn drop(&mut self) {
2432        let _ = self.close_resource();
2433    }
2434}
2435
2436type UnfoldResourceAsyncMarker<Out, CreateFut, ReadFut, CloseFut> =
2437    fn() -> (Out, CreateFut, ReadFut, CloseFut);
2438
2439struct UnfoldResourceAsyncStream<Resource, Out, Create, CreateFut, Read, ReadFut, Close, CloseFut>
2440where
2441    Resource: Send + 'static,
2442    Create: Fn() -> CreateFut,
2443    CreateFut: Future<Output = StreamResult<Resource>> + Send + 'static,
2444    Read: Fn(&mut Resource) -> ReadFut,
2445    ReadFut: Future<Output = StreamResult<Option<Out>>> + Send + 'static,
2446    Close: Fn(Resource) -> CloseFut,
2447    CloseFut: Future<Output = StreamResult<()>> + Send + 'static,
2448{
2449    create: Arc<Create>,
2450    read: Arc<Read>,
2451    close: Arc<Close>,
2452    resource: Option<Resource>,
2453    created: bool,
2454    terminated: bool,
2455    _marker: PhantomData<UnfoldResourceAsyncMarker<Out, CreateFut, ReadFut, CloseFut>>,
2456}
2457
2458impl<Resource, Out, Create, CreateFut, Read, ReadFut, Close, CloseFut>
2459    UnfoldResourceAsyncStream<Resource, Out, Create, CreateFut, Read, ReadFut, Close, CloseFut>
2460where
2461    Resource: Send + 'static,
2462    Create: Fn() -> CreateFut,
2463    CreateFut: Future<Output = StreamResult<Resource>> + Send + 'static,
2464    Read: Fn(&mut Resource) -> ReadFut,
2465    ReadFut: Future<Output = StreamResult<Option<Out>>> + Send + 'static,
2466    Close: Fn(Resource) -> CloseFut,
2467    CloseFut: Future<Output = StreamResult<()>> + Send + 'static,
2468{
2469    fn ensure_created(&mut self) -> StreamResult<()> {
2470        if self.created {
2471            return Ok(());
2472        }
2473        self.created = true;
2474        let resource = catch_unwind_failed("unfold_resource_async create", || (self.create)())
2475            .and_then(flow::run_future_inline_or_spawn)?;
2476        self.resource = Some(resource);
2477        Ok(())
2478    }
2479
2480    fn close_resource(&mut self) -> StreamResult<()> {
2481        match self.resource.take() {
2482            Some(resource) => {
2483                catch_unwind_failed("unfold_resource_async close", || (self.close)(resource))
2484                    .and_then(flow::run_future_inline_or_spawn)
2485            }
2486            None => Ok(()),
2487        }
2488    }
2489}
2490
2491impl<Resource, Out, Create, CreateFut, Read, ReadFut, Close, CloseFut> Iterator
2492    for UnfoldResourceAsyncStream<Resource, Out, Create, CreateFut, Read, ReadFut, Close, CloseFut>
2493where
2494    Resource: Send + 'static,
2495    Out: Send + 'static,
2496    Create: Fn() -> CreateFut,
2497    CreateFut: Future<Output = StreamResult<Resource>> + Send + 'static,
2498    Read: Fn(&mut Resource) -> ReadFut,
2499    ReadFut: Future<Output = StreamResult<Option<Out>>> + Send + 'static,
2500    Close: Fn(Resource) -> CloseFut,
2501    CloseFut: Future<Output = StreamResult<()>> + Send + 'static,
2502{
2503    type Item = StreamResult<Out>;
2504
2505    fn next(&mut self) -> Option<Self::Item> {
2506        if self.terminated {
2507            return None;
2508        }
2509        if let Err(error) = self.ensure_created() {
2510            self.terminated = true;
2511            return Some(Err(error));
2512        }
2513
2514        let result = {
2515            let resource = self
2516                .resource
2517                .as_mut()
2518                .expect("unfold_resource_async resource is open");
2519            catch_unwind_failed("unfold_resource_async read", || (self.read)(resource))
2520                .and_then(flow::run_future_inline_or_spawn)
2521        };
2522
2523        match result {
2524            Ok(Some(item)) => Some(Ok(item)),
2525            Ok(None) => {
2526                self.terminated = true;
2527                match self.close_resource() {
2528                    Ok(()) => None,
2529                    Err(error) => Some(Err(error)),
2530                }
2531            }
2532            Err(read_error) => {
2533                self.terminated = true;
2534                let _ = self.close_resource();
2535                Some(Err(read_error))
2536            }
2537        }
2538    }
2539}
2540
2541impl<Resource, Out, Create, CreateFut, Read, ReadFut, Close, CloseFut> Drop
2542    for UnfoldResourceAsyncStream<Resource, Out, Create, CreateFut, Read, ReadFut, Close, CloseFut>
2543where
2544    Resource: Send + 'static,
2545    Create: Fn() -> CreateFut,
2546    CreateFut: Future<Output = StreamResult<Resource>> + Send + 'static,
2547    Read: Fn(&mut Resource) -> ReadFut,
2548    ReadFut: Future<Output = StreamResult<Option<Out>>> + Send + 'static,
2549    Close: Fn(Resource) -> CloseFut,
2550    CloseFut: Future<Output = StreamResult<()>> + Send + 'static,
2551{
2552    fn drop(&mut self) {
2553        let _ = self.close_resource();
2554    }
2555}
2556
2557struct LazySourceStream<Out, InnerMat, F> {
2558    create: Arc<F>,
2559    materializer: Materializer,
2560    current: Option<BoxStream<Out>>,
2561    mat_sender: Option<oneshot::Sender<StreamResult<InnerMat>>>,
2562    initialized: bool,
2563    terminated: bool,
2564}
2565
2566impl<Out, InnerMat, F> LazySourceStream<Out, InnerMat, F>
2567where
2568    Out: Send + 'static,
2569    InnerMat: Send + 'static,
2570    F: Fn() -> Source<Out, InnerMat>,
2571{
2572    fn complete_mat(&mut self, result: StreamResult<InnerMat>) {
2573        if let Some(sender) = self.mat_sender.take() {
2574            let _ = sender.send(result);
2575        }
2576    }
2577
2578    fn initialize(&mut self) -> StreamResult<()> {
2579        if self.initialized {
2580            return Ok(());
2581        }
2582        self.initialized = true;
2583        let source = match catch_unwind_failed("lazy_source factory", || (self.create)()) {
2584            Ok(source) => source,
2585            Err(error) => {
2586                self.complete_mat(Err(error.clone()));
2587                return Err(error);
2588            }
2589        };
2590        match Arc::clone(&source.factory).create(&self.materializer) {
2591            Ok((stream, mat)) => {
2592                self.current = Some(stream);
2593                self.complete_mat(Ok(mat));
2594                Ok(())
2595            }
2596            Err(error) => {
2597                self.complete_mat(Err(error.clone()));
2598                Err(error)
2599            }
2600        }
2601    }
2602}
2603
2604impl<Out, InnerMat, F> Iterator for LazySourceStream<Out, InnerMat, F>
2605where
2606    Out: Send + 'static,
2607    InnerMat: Send + 'static,
2608    F: Fn() -> Source<Out, InnerMat>,
2609{
2610    type Item = StreamResult<Out>;
2611
2612    fn next(&mut self) -> Option<Self::Item> {
2613        if self.terminated {
2614            return None;
2615        }
2616        if let Err(error) = self.initialize() {
2617            self.terminated = true;
2618            return Some(Err(error));
2619        }
2620        match self
2621            .current
2622            .as_mut()
2623            .expect("lazy_source current stream initialized")
2624            .next()
2625        {
2626            Some(Ok(item)) => Some(Ok(item)),
2627            Some(Err(error)) => {
2628                self.terminated = true;
2629                Some(Err(error))
2630            }
2631            None => {
2632                self.terminated = true;
2633                None
2634            }
2635        }
2636    }
2637}
2638
2639impl<Out, InnerMat, F> Drop for LazySourceStream<Out, InnerMat, F> {
2640    fn drop(&mut self) {
2641        if !self.initialized
2642            && let Some(sender) = self.mat_sender.take()
2643        {
2644            let _ = sender.send(Err(StreamError::Failed(
2645                "lazy source was never materialized".into(),
2646            )));
2647        }
2648    }
2649}
2650
2651struct LazyFutureSourceStream<Out, InnerMat, F, Fut> {
2652    create: Arc<F>,
2653    materializer: Materializer,
2654    current: Option<BoxStream<Out>>,
2655    mat_sender: Option<oneshot::Sender<StreamResult<InnerMat>>>,
2656    initialized: bool,
2657    terminated: bool,
2658    _marker: PhantomData<fn() -> Fut>,
2659}
2660
2661impl<Out, InnerMat, F, Fut> LazyFutureSourceStream<Out, InnerMat, F, Fut>
2662where
2663    Out: Send + 'static,
2664    InnerMat: Send + 'static,
2665    F: Fn() -> Fut,
2666    Fut: Future<Output = StreamResult<Source<Out, InnerMat>>> + Send + 'static,
2667{
2668    fn complete_mat(&mut self, result: StreamResult<InnerMat>) {
2669        if let Some(sender) = self.mat_sender.take() {
2670            let _ = sender.send(result);
2671        }
2672    }
2673
2674    fn initialize(&mut self) -> StreamResult<()> {
2675        if self.initialized {
2676            return Ok(());
2677        }
2678        self.initialized = true;
2679        let source = match catch_unwind_failed("lazy_future_source factory", || (self.create)())
2680            .and_then(flow::run_future_inline_or_spawn)
2681        {
2682            Ok(source) => source,
2683            Err(error) => {
2684                self.complete_mat(Err(error.clone()));
2685                return Err(error);
2686            }
2687        };
2688        match Arc::clone(&source.factory).create(&self.materializer) {
2689            Ok((stream, mat)) => {
2690                self.current = Some(stream);
2691                self.complete_mat(Ok(mat));
2692                Ok(())
2693            }
2694            Err(error) => {
2695                self.complete_mat(Err(error.clone()));
2696                Err(error)
2697            }
2698        }
2699    }
2700}
2701
2702impl<Out, InnerMat, F, Fut> Iterator for LazyFutureSourceStream<Out, InnerMat, F, Fut>
2703where
2704    Out: Send + 'static,
2705    InnerMat: Send + 'static,
2706    F: Fn() -> Fut,
2707    Fut: Future<Output = StreamResult<Source<Out, InnerMat>>> + Send + 'static,
2708{
2709    type Item = StreamResult<Out>;
2710
2711    fn next(&mut self) -> Option<Self::Item> {
2712        if self.terminated {
2713            return None;
2714        }
2715        if let Err(error) = self.initialize() {
2716            self.terminated = true;
2717            return Some(Err(error));
2718        }
2719        match self
2720            .current
2721            .as_mut()
2722            .expect("lazy_future_source current stream initialized")
2723            .next()
2724        {
2725            Some(Ok(item)) => Some(Ok(item)),
2726            Some(Err(error)) => {
2727                self.terminated = true;
2728                Some(Err(error))
2729            }
2730            None => {
2731                self.terminated = true;
2732                None
2733            }
2734        }
2735    }
2736}
2737
2738impl<Out, InnerMat, F, Fut> Drop for LazyFutureSourceStream<Out, InnerMat, F, Fut> {
2739    fn drop(&mut self) {
2740        if !self.initialized
2741            && let Some(sender) = self.mat_sender.take()
2742        {
2743            let _ = sender.send(Err(StreamError::Failed(
2744                "lazy future source was never materialized".into(),
2745            )));
2746        }
2747    }
2748}
2749
2750fn concat_source_streams<Out>(streams: Vec<BoxStream<Out>>) -> BoxStream<Out>
2751where
2752    Out: Send + 'static,
2753{
2754    let mut streams: VecDeque<_> = streams.into();
2755    let mut current = streams.pop_front();
2756    Box::new(std::iter::from_fn(move || {
2757        loop {
2758            match current.as_mut() {
2759                Some(stream) => match stream.next() {
2760                    Some(item) => return Some(item),
2761                    None => current = streams.pop_front(),
2762                },
2763                None => return None,
2764            }
2765        }
2766    }))
2767}
2768
2769fn concat_source_streams_lazy<Out, Mat>(
2770    initial: BoxStream<Out>,
2771    factories: Vec<Arc<dyn SourceFactory<Out, Mat>>>,
2772    materializer: &Materializer,
2773) -> BoxStream<Out>
2774where
2775    Out: Send + 'static,
2776    Mat: Send + 'static,
2777{
2778    let mut current = Some(initial);
2779    let mut remaining: VecDeque<_> = factories.into();
2780    let materializer = materializer.with_name_prefix(materializer.name_prefix().to_owned());
2781    Box::new(std::iter::from_fn(move || {
2782        loop {
2783            match current.as_mut() {
2784                Some(stream) => match stream.next() {
2785                    Some(item) => return Some(item),
2786                    None => {
2787                        current = remaining.pop_front().map(|factory| {
2788                            match factory.create(&materializer) {
2789                                Ok((stream, _)) => stream,
2790                                Err(error) => {
2791                                    Box::new(std::iter::once(Err(error))) as BoxStream<Out>
2792                                }
2793                            }
2794                        });
2795                    }
2796                },
2797                None => return None,
2798            }
2799        }
2800    }))
2801}
2802
2803fn or_else_source_stream<Out>(
2804    mut primary: BoxStream<Out>,
2805    mut secondary: BoxStream<Out>,
2806) -> BoxStream<Out>
2807where
2808    Out: Send + 'static,
2809{
2810    let mut primary_emitted = false;
2811    let mut using_secondary = false;
2812    Box::new(std::iter::from_fn(move || {
2813        loop {
2814            if using_secondary {
2815                return secondary.next();
2816            }
2817
2818            match primary.next() {
2819                Some(Ok(item)) => {
2820                    primary_emitted = true;
2821                    return Some(Ok(item));
2822                }
2823                Some(Err(error)) => return Some(Err(error)),
2824                None if primary_emitted => return None,
2825                None => using_secondary = true,
2826            }
2827        }
2828    }))
2829}
2830
2831fn interleave_source_streams<Out>(
2832    streams: Vec<BoxStream<Out>>,
2833    segment_size: usize,
2834    eager_close: bool,
2835) -> BoxStream<Out>
2836where
2837    Out: Send + 'static,
2838{
2839    if segment_size == 0 {
2840        return Box::new(std::iter::once(Err(StreamError::GraphValidation(
2841            "interleave segment size must be greater than zero".into(),
2842        ))));
2843    }
2844
2845    let mut streams: Vec<Option<BoxStream<Out>>> = streams.into_iter().map(Some).collect();
2846    let mut pending: Vec<Option<StreamResult<Out>>> = (0..streams.len()).map(|_| None).collect();
2847    let mut current = 0usize;
2848    let mut emitted = 0usize;
2849    Box::new(std::iter::from_fn(move || {
2850        loop {
2851            if streams.iter().all(Option::is_none) {
2852                return None;
2853            }
2854            if streams[current].is_none() {
2855                match next_active_source_stream(&streams, current) {
2856                    Some(next) => {
2857                        current = next;
2858                        emitted = 0;
2859                    }
2860                    None => return None,
2861                }
2862            }
2863
2864            let Some(stream) = streams[current].as_mut() else {
2865                continue;
2866            };
2867            let next_item = pending[current].take().or_else(|| stream.next());
2868            match next_item {
2869                Some(Ok(item)) => {
2870                    emitted += 1;
2871                    if emitted == segment_size {
2872                        emitted = 0;
2873                        if let Some(next) = next_active_source_stream(&streams, current) {
2874                            current = next;
2875                        }
2876                    }
2877                    return Some(Ok(item));
2878                }
2879                Some(Err(error)) => return Some(Err(error)),
2880                None => {
2881                    streams[current] = None;
2882                    emitted = 0;
2883                    if eager_close {
2884                        return None;
2885                    }
2886                    match next_active_source_stream(&streams, current) {
2887                        Some(next) => current = next,
2888                        None => return None,
2889                    }
2890                }
2891            }
2892        }
2893    }))
2894}
2895
2896fn next_active_source_stream<Out>(
2897    streams: &[Option<BoxStream<Out>>],
2898    current: usize,
2899) -> Option<usize>
2900where
2901    Out: Send + 'static,
2902{
2903    if streams.is_empty() {
2904        return None;
2905    }
2906    for offset in 1..=streams.len() {
2907        let index = (current + offset) % streams.len();
2908        if streams[index].is_some() {
2909            return Some(index);
2910        }
2911    }
2912    None
2913}