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 scalar_chunks_allowed = self.hints.inline_micro.is_some();
654        let source = self.factory;
655        let transform = flow.transform;
656        let scalar_i64_fallback = flow.scalar_i64_fallback;
657        let materialize_flow = flow.materialize;
658        let hints = self.hints.after_flow(flow.hints);
659        let combine = Arc::new(combine);
660        Source::from_materialized_factory_with_hints(
661            move |materializer| {
662                let (stream, source_mat) = Arc::clone(&source).create(materializer)?;
663                let flow_mat = materialize_flow()?;
664                let stream = match &transform {
665                    FlowTransform::Pure(transform) => {
666                        if scalar_chunks_allowed {
667                            transform(stream)
668                        } else if let Some(fallback) = &scalar_i64_fallback {
669                            fallback(stream)
670                        } else {
671                            transform(stream)
672                        }
673                    }
674                    FlowTransform::Runtime(transform) => transform(stream, materializer)?,
675                };
676                Ok((stream, combine(source_mat, flow_mat)))
677            },
678            hints,
679        )
680    }
681
682    #[must_use]
683    pub fn via_mat_with<Next, FlowMat, Combined, F>(
684        self,
685        flow: Flow<Out, Next, FlowMat>,
686        combine: F,
687    ) -> Source<Next, Combined>
688    where
689        Next: Send + 'static,
690        FlowMat: Send + 'static,
691        Combined: Send + 'static,
692        F: Fn(Mat, FlowMat) -> Combined + Send + Sync + 'static,
693    {
694        self.via_mat(flow, combine)
695    }
696
697    #[must_use]
698    pub fn map<Next, F>(self, f: F) -> Source<Next, Mat>
699    where
700        Next: Send + 'static,
701        F: Fn(Out) -> Next + Send + Sync + 'static,
702    {
703        let hints = self.hints.without_inline_micro();
704        Source {
705            factory: Arc::new(MapSourceFactory {
706                source: self.factory,
707                stage: f,
708                _marker: PhantomData,
709            }),
710            terminal_factory: None,
711            hints,
712            attributes: self.attributes,
713            split_hook: None,
714        }
715    }
716
717    #[must_use]
718    pub fn attributes(&self) -> &Attributes {
719        &self.attributes
720    }
721
722    #[must_use]
723    pub fn with_attributes(mut self, attributes: Attributes) -> Self {
724        self.attributes = attributes;
725        self
726    }
727
728    #[must_use]
729    pub fn add_attributes(mut self, attributes: Attributes) -> Self {
730        self.attributes = self.attributes.and(attributes);
731        self
732    }
733
734    #[must_use]
735    pub fn named(self, name: impl Into<String>) -> Self {
736        self.add_attributes(Attributes::named(name))
737    }
738
739    /// Insert an opt-in instrumentation boundary after this source.
740    ///
741    /// A fresh run is registered in `registry` every time the returned source is
742    /// materialized. The boundary counts successful elements with relaxed atomics
743    /// and records running/draining/completed/failed state for that materialization.
744    ///
745    /// This is intentionally an explicit wrapper instead of an [`Attributes`] flag:
746    /// ordinary streams that do not call `instrumented` keep the exact same blueprint
747    /// and hot path.
748    #[must_use]
749    pub fn instrumented(
750        self,
751        name: impl Into<String>,
752        registry: &StreamInstrumentationRegistry,
753    ) -> Self {
754        let Source {
755            factory,
756            terminal_factory: _,
757            hints,
758            attributes,
759            split_hook: _,
760        } = self;
761        let name: Arc<str> = Arc::from(name.into());
762        let registry = registry.clone();
763        let mut source = Source::from_materialized_factory_with_hints(
764            move |materializer| {
765                let run = registry.register(name.as_ref().to_owned());
766                match Arc::clone(&factory).create(materializer) {
767                    Ok((stream, mat)) => Ok((
768                        Box::new(InstrumentedStream::new(stream, run)) as BoxStream<Out>,
769                        mat,
770                    )),
771                    Err(error) => {
772                        run.mark_failed();
773                        Err(error)
774                    }
775                }
776            },
777            hints.without_inline_micro(),
778        );
779        source.attributes = attributes;
780        source
781    }
782
783    /// Insert an async boundary after this source.
784    ///
785    /// The boundary uses the same [`crate::AsyncBoundaryExecutionConfig`] defaults
786    /// as the GraphDSL `AsyncBoundary` runner and hands elements across a bounded
787    /// Ractor-backed queue. `async_boundary` is the Rust-friendly primary name;
788    /// `Source::r#async` is provided as the Akka-mirroring alias.
789    #[must_use]
790    pub fn async_boundary(self) -> Self {
791        self.via(Flow::identity().async_boundary())
792    }
793
794    /// Akka-mirroring alias for [`Source::async_boundary`].
795    #[must_use]
796    pub fn r#async(self) -> Self {
797        self.async_boundary()
798    }
799
800    /// Insert an async boundary with an explicit bounded handoff configuration.
801    ///
802    /// `config.buffer_size` controls the number of elements that may be queued
803    /// between the upstream and downstream fused regions. A zero buffer is
804    /// rejected when the stream is materialized.
805    #[must_use]
806    pub fn async_boundary_with_config(
807        self,
808        config: crate::graph::AsyncBoundaryExecutionConfig,
809    ) -> Self {
810        self.via(Flow::identity().async_boundary_with_config(config))
811    }
812
813    /// Insert an async boundary with a custom bounded handoff size.
814    #[must_use]
815    pub fn async_boundary_with_buffer(self, buffer_size: usize) -> Self {
816        self.via(Flow::identity().async_boundary_with_buffer(buffer_size))
817    }
818
819    #[must_use]
820    pub fn try_map<Next, F>(self, f: F) -> Source<Next, Mat>
821    where
822        Next: Send + 'static,
823        F: Fn(Out) -> StreamResult<Next> + Send + Sync + 'static,
824    {
825        self.via(Flow::identity().try_map(f))
826    }
827
828    /// Deprecated alias for [`Source::try_map`].
829    #[must_use]
830    #[deprecated(
831        since = "0.9.0",
832        note = "renamed to `try_map` (idiomatic); the `_result` name still works"
833    )]
834    pub fn map_result<Next, F>(self, f: F) -> Source<Next, Mat>
835    where
836        Next: Send + 'static,
837        F: Fn(Out) -> StreamResult<Next> + Send + Sync + 'static,
838    {
839        self.try_map(f)
840    }
841
842    #[must_use]
843    pub fn map_result_with_supervision<Next, F>(
844        self,
845        f: F,
846        decider: SupervisionDecider,
847    ) -> Source<Next, Mat>
848    where
849        Next: Send + 'static,
850        F: Fn(Out) -> StreamResult<Next> + Send + Sync + 'static,
851    {
852        self.via(Flow::identity().map_result_with_supervision(f, decider))
853    }
854
855    #[must_use]
856    pub fn filter<F>(self, predicate: F) -> Source<Out, Mat>
857    where
858        F: Fn(&Out) -> bool + Send + Sync + 'static,
859    {
860        self.via(Flow::identity().filter(predicate))
861    }
862
863    #[must_use]
864    pub fn try_filter<F>(self, predicate: F) -> Source<Out, Mat>
865    where
866        F: Fn(&Out) -> StreamResult<bool> + Send + Sync + 'static,
867    {
868        self.via(Flow::identity().try_filter(predicate))
869    }
870
871    /// Deprecated alias for [`Source::try_filter`].
872    #[must_use]
873    #[deprecated(
874        since = "0.9.0",
875        note = "renamed to `try_filter` (idiomatic); the `_result` name still works"
876    )]
877    pub fn filter_result<F>(self, predicate: F) -> Source<Out, Mat>
878    where
879        F: Fn(&Out) -> StreamResult<bool> + Send + Sync + 'static,
880    {
881        self.try_filter(predicate)
882    }
883
884    #[must_use]
885    pub fn filter_result_with_supervision<F>(
886        self,
887        predicate: F,
888        decider: SupervisionDecider,
889    ) -> Source<Out, Mat>
890    where
891        F: Fn(&Out) -> StreamResult<bool> + Send + Sync + 'static,
892    {
893        self.via(Flow::identity().filter_result_with_supervision(predicate, decider))
894    }
895
896    #[must_use]
897    pub fn filter_not<F>(self, predicate: F) -> Source<Out, Mat>
898    where
899        F: Fn(&Out) -> bool + Send + Sync + 'static,
900    {
901        self.via(Flow::identity().filter_not(predicate))
902    }
903
904    #[must_use]
905    pub fn filter_map<Next, F>(self, f: F) -> Source<Next, Mat>
906    where
907        Next: Send + 'static,
908        F: Fn(Out) -> Option<Next> + Send + Sync + 'static,
909    {
910        self.via(Flow::identity().filter_map(f))
911    }
912
913    #[must_use]
914    pub fn try_filter_map<Next, F>(self, f: F) -> Source<Next, Mat>
915    where
916        Next: Send + 'static,
917        F: Fn(Out) -> StreamResult<Option<Next>> + Send + Sync + 'static,
918    {
919        self.via(Flow::identity().try_filter_map(f))
920    }
921
922    /// Deprecated alias for [`Source::try_filter_map`].
923    #[must_use]
924    #[deprecated(
925        since = "0.9.0",
926        note = "renamed to `try_filter_map` (idiomatic); the `_result` name still works"
927    )]
928    pub fn filter_map_result<Next, F>(self, f: F) -> Source<Next, Mat>
929    where
930        Next: Send + 'static,
931        F: Fn(Out) -> StreamResult<Option<Next>> + Send + Sync + 'static,
932    {
933        self.try_filter_map(f)
934    }
935
936    #[must_use]
937    pub fn filter_map_result_with_supervision<Next, F>(
938        self,
939        f: F,
940        decider: SupervisionDecider,
941    ) -> Source<Next, Mat>
942    where
943        Next: Send + 'static,
944        F: Fn(Out) -> StreamResult<Option<Next>> + Send + Sync + 'static,
945    {
946        self.via(Flow::identity().filter_map_result_with_supervision(f, decider))
947    }
948
949    #[must_use]
950    pub fn map_concat<Next, F, I>(self, f: F) -> Source<Next, Mat>
951    where
952        Next: Send + 'static,
953        F: Fn(Out) -> I + Send + Sync + 'static,
954        I: IntoIterator<Item = Next>,
955        I::IntoIter: Send + 'static,
956    {
957        self.via(Flow::identity().map_concat(f))
958    }
959
960    #[must_use]
961    pub fn try_map_concat<Next, F, I>(self, f: F) -> Source<Next, Mat>
962    where
963        Next: Send + 'static,
964        F: Fn(Out) -> StreamResult<I> + Send + Sync + 'static,
965        I: IntoIterator<Item = Next>,
966        I::IntoIter: Send + 'static,
967    {
968        self.via(Flow::identity().try_map_concat(f))
969    }
970
971    /// Deprecated alias for [`Source::try_map_concat`].
972    #[must_use]
973    #[deprecated(
974        since = "0.9.0",
975        note = "renamed to `try_map_concat` (idiomatic); the `_result` name still works"
976    )]
977    pub fn map_concat_result<Next, F, I>(self, f: F) -> Source<Next, Mat>
978    where
979        Next: Send + 'static,
980        F: Fn(Out) -> StreamResult<I> + Send + Sync + 'static,
981        I: IntoIterator<Item = Next>,
982        I::IntoIter: Send + 'static,
983    {
984        self.try_map_concat(f)
985    }
986
987    #[must_use]
988    pub fn map_concat_result_with_supervision<Next, F, I>(
989        self,
990        f: F,
991        decider: SupervisionDecider,
992    ) -> Source<Next, Mat>
993    where
994        Next: Send + 'static,
995        F: Fn(Out) -> StreamResult<I> + Send + Sync + 'static,
996        I: IntoIterator<Item = Next>,
997        I::IntoIter: Send + 'static,
998    {
999        self.via(Flow::identity().map_concat_result_with_supervision(f, decider))
1000    }
1001
1002    #[must_use]
1003    pub fn 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) -> Next + Send + Sync + 'static,
1008    {
1009        self.via(Flow::identity().stateful_map(seed, f))
1010    }
1011
1012    #[must_use]
1013    pub fn try_stateful_map<State, Next, F>(self, seed: State, f: F) -> Source<Next, Mat>
1014    where
1015        State: Clone + Send + Sync + 'static,
1016        Next: Send + 'static,
1017        F: Fn(&mut State, Out) -> StreamResult<Next> + Send + Sync + 'static,
1018    {
1019        self.via(Flow::identity().try_stateful_map(seed, f))
1020    }
1021
1022    /// Deprecated alias for [`Source::try_stateful_map`].
1023    #[must_use]
1024    #[deprecated(
1025        since = "0.9.0",
1026        note = "renamed to `try_stateful_map` (idiomatic); the `_result` name still works"
1027    )]
1028    pub fn stateful_map_result<State, Next, F>(self, seed: State, f: F) -> Source<Next, Mat>
1029    where
1030        State: Clone + Send + Sync + 'static,
1031        Next: Send + 'static,
1032        F: Fn(&mut State, Out) -> StreamResult<Next> + Send + Sync + 'static,
1033    {
1034        self.try_stateful_map(seed, f)
1035    }
1036
1037    #[must_use]
1038    pub fn stateful_map_result_with_supervision<State, Next, F>(
1039        self,
1040        seed: State,
1041        f: F,
1042        decider: SupervisionDecider,
1043    ) -> Source<Next, Mat>
1044    where
1045        State: Clone + Send + Sync + 'static,
1046        Next: Send + 'static,
1047        F: Fn(&mut State, Out) -> StreamResult<Next> + Send + Sync + 'static,
1048    {
1049        self.via(Flow::identity().stateful_map_result_with_supervision(seed, f, decider))
1050    }
1051
1052    #[must_use]
1053    pub fn stateful_map_concat<State, Next, F, I>(self, seed: State, f: F) -> Source<Next, Mat>
1054    where
1055        State: Clone + Send + Sync + 'static,
1056        Next: Send + 'static,
1057        F: Fn(&mut State, Out) -> I + Send + Sync + 'static,
1058        I: IntoIterator<Item = Next>,
1059        I::IntoIter: Send + 'static,
1060    {
1061        self.via(Flow::identity().stateful_map_concat(seed, f))
1062    }
1063
1064    #[must_use]
1065    pub fn try_stateful_map_concat<State, Next, F, I>(self, seed: State, f: F) -> Source<Next, Mat>
1066    where
1067        State: Clone + Send + Sync + 'static,
1068        Next: Send + 'static,
1069        F: Fn(&mut State, Out) -> StreamResult<I> + Send + Sync + 'static,
1070        I: IntoIterator<Item = Next>,
1071        I::IntoIter: Send + 'static,
1072    {
1073        self.via(Flow::identity().try_stateful_map_concat(seed, f))
1074    }
1075
1076    /// Deprecated alias for [`Source::try_stateful_map_concat`].
1077    #[must_use]
1078    #[deprecated(
1079        since = "0.9.0",
1080        note = "renamed to `try_stateful_map_concat` (idiomatic); the `_result` name still works"
1081    )]
1082    pub fn stateful_map_concat_result<State, Next, F, I>(
1083        self,
1084        seed: State,
1085        f: F,
1086    ) -> Source<Next, Mat>
1087    where
1088        State: Clone + Send + Sync + 'static,
1089        Next: Send + 'static,
1090        F: Fn(&mut State, Out) -> StreamResult<I> + Send + Sync + 'static,
1091        I: IntoIterator<Item = Next>,
1092        I::IntoIter: Send + 'static,
1093    {
1094        self.try_stateful_map_concat(seed, f)
1095    }
1096
1097    #[must_use]
1098    pub fn stateful_map_concat_result_with_supervision<State, Next, F, I>(
1099        self,
1100        seed: State,
1101        f: F,
1102        decider: SupervisionDecider,
1103    ) -> Source<Next, Mat>
1104    where
1105        State: Clone + Send + Sync + 'static,
1106        Next: Send + 'static,
1107        F: Fn(&mut State, Out) -> StreamResult<I> + Send + Sync + 'static,
1108        I: IntoIterator<Item = Next>,
1109        I::IntoIter: Send + 'static,
1110    {
1111        self.via(Flow::identity().stateful_map_concat_result_with_supervision(seed, f, decider))
1112    }
1113
1114    #[must_use]
1115    pub fn map_async<Next, F, Fut>(self, parallelism: usize, f: F) -> Source<Next, Mat>
1116    where
1117        Next: Send + 'static,
1118        F: Fn(Out) -> Fut + Send + Sync + 'static,
1119        Fut: Future<Output = StreamResult<Next>> + Send + 'static,
1120    {
1121        self.via(Flow::identity().map_async(parallelism, f))
1122    }
1123
1124    #[must_use]
1125    pub fn map_async_with_supervision<Next, F, Fut>(
1126        self,
1127        parallelism: usize,
1128        f: F,
1129        decider: SupervisionDecider,
1130    ) -> 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_with_supervision(parallelism, f, decider))
1137    }
1138
1139    #[must_use]
1140    pub fn map_async_unordered<Next, F, Fut>(self, parallelism: usize, f: F) -> Source<Next, Mat>
1141    where
1142        Next: Send + 'static,
1143        F: Fn(Out) -> Fut + Send + Sync + 'static,
1144        Fut: Future<Output = StreamResult<Next>> + Send + 'static,
1145    {
1146        self.via(Flow::identity().map_async_unordered(parallelism, f))
1147    }
1148
1149    #[must_use]
1150    pub fn map_async_unordered_with_supervision<Next, F, Fut>(
1151        self,
1152        parallelism: usize,
1153        f: F,
1154        decider: SupervisionDecider,
1155    ) -> Source<Next, Mat>
1156    where
1157        Next: Send + 'static,
1158        F: Fn(Out) -> Fut + Send + Sync + 'static,
1159        Fut: Future<Output = StreamResult<Next>> + Send + 'static,
1160    {
1161        self.via(Flow::identity().map_async_unordered_with_supervision(parallelism, f, decider))
1162    }
1163
1164    #[must_use]
1165    pub fn map_async_partitioned<Key, Next, Partition, F, Fut>(
1166        self,
1167        parallelism: usize,
1168        per_partition: usize,
1169        partition: Partition,
1170        f: F,
1171    ) -> Source<Next, Mat>
1172    where
1173        Key: Clone + Eq + Hash + Send + 'static,
1174        Next: Send + 'static,
1175        Partition: Fn(&Out) -> Key + Send + Sync + 'static,
1176        F: Fn(Out) -> Fut + Send + Sync + 'static,
1177        Fut: Future<Output = StreamResult<Next>> + Send + 'static,
1178    {
1179        self.via(Flow::identity().map_async_partitioned(parallelism, per_partition, partition, f))
1180    }
1181
1182    #[must_use]
1183    pub fn prefix_and_tail(self, n: usize) -> Source<(Vec<Out>, Source<Out>), Mat> {
1184        self.via(Flow::identity().prefix_and_tail(n))
1185    }
1186
1187    #[must_use]
1188    pub fn flat_map_prefix<Next, FlowMat, F>(self, n: usize, f: F) -> Source<Next, Mat>
1189    where
1190        Next: Send + 'static,
1191        FlowMat: Send + 'static,
1192        F: Fn(Vec<Out>) -> Flow<Out, Next, FlowMat> + Send + Sync + 'static,
1193        Out: Clone,
1194    {
1195        self.via(Flow::identity().flat_map_prefix(n, f))
1196    }
1197
1198    #[must_use]
1199    pub fn group_by<Key, F>(
1200        self,
1201        max_substreams: usize,
1202        f: F,
1203        allow_closed_substream_recreation: bool,
1204    ) -> Source<Source<Out>, Mat>
1205    where
1206        Key: Clone + Eq + Hash + Send + 'static,
1207        F: Fn(&Out) -> Key + Send + Sync + 'static,
1208        Out: Clone,
1209    {
1210        let batch_mode = if self.hints.inline_micro.is_some() && !allow_closed_substream_recreation
1211        {
1212            flow::GroupByBatchMode::FiniteEagerNoRecreate
1213        } else {
1214            flow::GroupByBatchMode::Immediate
1215        };
1216        self.via(Flow::identity().group_by_with_batching(
1217            max_substreams,
1218            f,
1219            allow_closed_substream_recreation,
1220            batch_mode,
1221        ))
1222    }
1223
1224    #[must_use]
1225    pub fn split_when<F>(self, predicate: F) -> Source<Source<Out>, Mat>
1226    where
1227        F: Fn(&Out) -> bool + Send + Sync + 'static,
1228        Out: Clone,
1229    {
1230        self.via(Flow::identity().split_when(predicate))
1231    }
1232
1233    #[must_use]
1234    pub fn split_after<F>(self, predicate: F) -> Source<Source<Out>, Mat>
1235    where
1236        F: Fn(&Out) -> bool + Send + Sync + 'static,
1237        Out: Clone,
1238    {
1239        self.via(Flow::identity().split_after(predicate))
1240    }
1241
1242    #[must_use]
1243    pub fn flat_map_concat<Next, NextMat, F>(self, 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_concat(f))
1250    }
1251
1252    #[must_use]
1253    pub fn flat_map_merge<Next, NextMat, F>(self, breadth: usize, f: F) -> Source<Next, Mat>
1254    where
1255        Next: Send + 'static,
1256        NextMat: Send + 'static,
1257        F: Fn(Out) -> Source<Next, NextMat> + Send + Sync + 'static,
1258    {
1259        self.via(Flow::identity().flat_map_merge(breadth, f))
1260    }
1261
1262    #[must_use]
1263    pub fn take(self, n: usize) -> Source<Out, Mat> {
1264        self.via(Flow::identity().take(n))
1265    }
1266
1267    #[must_use]
1268    pub fn drop(self, n: usize) -> Source<Out, Mat> {
1269        self.via(Flow::identity().drop(n))
1270    }
1271
1272    #[must_use]
1273    pub fn take_while<F>(self, predicate: F) -> Source<Out, Mat>
1274    where
1275        F: Fn(&Out) -> bool + Send + Sync + 'static,
1276    {
1277        self.via(Flow::identity().take_while(predicate))
1278    }
1279
1280    #[must_use]
1281    pub fn drop_while<F>(self, predicate: F) -> Source<Out, Mat>
1282    where
1283        F: Fn(&Out) -> bool + Send + Sync + 'static,
1284    {
1285        self.via(Flow::identity().drop_while(predicate))
1286    }
1287
1288    #[must_use]
1289    pub fn limit(self, max: u64) -> Source<Out, Mat> {
1290        self.via(Flow::identity().limit(max))
1291    }
1292
1293    #[must_use]
1294    pub fn grouped(self, size: usize) -> Source<Vec<Out>, Mat> {
1295        self.via(Flow::identity().grouped(size))
1296    }
1297
1298    #[must_use]
1299    pub fn scan<State, F>(self, seed: State, f: F) -> Source<State, Mat>
1300    where
1301        State: Clone + Send + Sync + 'static,
1302        F: Fn(State, Out) -> State + Send + Sync + 'static,
1303    {
1304        self.via(Flow::identity().scan(seed, f))
1305    }
1306
1307    #[must_use]
1308    pub fn scan_async<State, F, Fut>(self, seed: State, f: F) -> Source<State, Mat>
1309    where
1310        State: Clone + Send + Sync + 'static,
1311        F: Fn(State, Out) -> Fut + Send + Sync + 'static,
1312        Fut: Future<Output = StreamResult<State>> + Send + 'static,
1313    {
1314        self.via(Flow::identity().scan_async(seed, f))
1315    }
1316
1317    #[must_use]
1318    pub fn try_scan<State, F>(self, seed: State, f: F) -> Source<State, Mat>
1319    where
1320        State: Clone + Send + Sync + 'static,
1321        F: Fn(State, Out) -> StreamResult<State> + Send + Sync + 'static,
1322    {
1323        self.via(Flow::identity().try_scan(seed, f))
1324    }
1325
1326    /// Deprecated alias for [`Source::try_scan`].
1327    #[must_use]
1328    #[deprecated(
1329        since = "0.9.0",
1330        note = "renamed to `try_scan` (idiomatic); the `_result` name still works"
1331    )]
1332    pub fn scan_result<State, F>(self, seed: State, f: F) -> Source<State, Mat>
1333    where
1334        State: Clone + Send + Sync + 'static,
1335        F: Fn(State, Out) -> StreamResult<State> + Send + Sync + 'static,
1336    {
1337        self.try_scan(seed, f)
1338    }
1339
1340    #[must_use]
1341    pub fn scan_result_with_supervision<State, F>(
1342        self,
1343        seed: State,
1344        f: F,
1345        decider: SupervisionDecider,
1346    ) -> Source<State, Mat>
1347    where
1348        State: Clone + Send + Sync + 'static,
1349        F: Fn(State, Out) -> StreamResult<State> + Send + Sync + 'static,
1350    {
1351        self.via(Flow::identity().scan_result_with_supervision(seed, f, decider))
1352    }
1353
1354    #[must_use]
1355    pub fn sliding(self, size: usize, step: usize) -> Source<Vec<Out>, Mat>
1356    where
1357        Out: Clone,
1358    {
1359        self.via(Flow::identity().sliding(size, step))
1360    }
1361
1362    #[must_use]
1363    pub fn fold<Acc, F>(self, zero: Acc, f: F) -> Source<Acc, Mat>
1364    where
1365        Acc: Clone + Send + Sync + 'static,
1366        F: Fn(Acc, Out) -> Acc + Send + Sync + 'static,
1367    {
1368        self.via(Flow::identity().fold(zero, f))
1369    }
1370
1371    #[must_use]
1372    pub fn fold_async<Acc, F, Fut>(self, zero: Acc, f: F) -> Source<Acc, Mat>
1373    where
1374        Acc: Clone + Send + Sync + 'static,
1375        F: Fn(Acc, Out) -> Fut + Send + Sync + 'static,
1376        Fut: Future<Output = StreamResult<Acc>> + Send + 'static,
1377    {
1378        self.via(Flow::identity().fold_async(zero, f))
1379    }
1380
1381    #[must_use]
1382    pub fn map_with_resource<Resource, Next, Create, F, Close>(
1383        self,
1384        create: Create,
1385        f: F,
1386        close: Close,
1387    ) -> Source<Next, Mat>
1388    where
1389        Resource: Send + 'static,
1390        Next: Send + 'static,
1391        Create: Fn() -> StreamResult<Resource> + Send + Sync + 'static,
1392        F: Fn(&mut Resource, Out) -> StreamResult<Next> + Send + Sync + 'static,
1393        Close: Fn(Resource) -> StreamResult<Option<Next>> + Send + Sync + 'static,
1394    {
1395        self.via(Flow::identity().map_with_resource(create, f, close))
1396    }
1397
1398    #[must_use]
1399    pub fn try_fold<Acc, F>(self, zero: Acc, f: F) -> Source<Acc, Mat>
1400    where
1401        Acc: Clone + Send + Sync + 'static,
1402        F: Fn(Acc, Out) -> StreamResult<Acc> + Send + Sync + 'static,
1403    {
1404        self.via(Flow::identity().try_fold(zero, f))
1405    }
1406
1407    /// Deprecated alias for [`Source::try_fold`].
1408    #[must_use]
1409    #[deprecated(
1410        since = "0.9.0",
1411        note = "renamed to `try_fold` (idiomatic); the `_result` name still works"
1412    )]
1413    pub fn fold_result<Acc, F>(self, zero: Acc, f: F) -> Source<Acc, Mat>
1414    where
1415        Acc: Clone + Send + Sync + 'static,
1416        F: Fn(Acc, Out) -> StreamResult<Acc> + Send + Sync + 'static,
1417    {
1418        self.try_fold(zero, f)
1419    }
1420
1421    #[must_use]
1422    pub fn fold_result_with_supervision<Acc, F>(
1423        self,
1424        zero: Acc,
1425        f: F,
1426        decider: SupervisionDecider,
1427    ) -> Source<Acc, Mat>
1428    where
1429        Acc: Clone + Send + Sync + 'static,
1430        F: Fn(Acc, Out) -> StreamResult<Acc> + Send + Sync + 'static,
1431    {
1432        self.via(Flow::identity().fold_result_with_supervision(zero, f, decider))
1433    }
1434
1435    #[must_use]
1436    pub fn reduce<F>(self, f: F) -> Source<Out, Mat>
1437    where
1438        F: Fn(Out, Out) -> Out + Send + Sync + 'static,
1439    {
1440        self.via(Flow::identity().reduce(f))
1441    }
1442
1443    #[must_use]
1444    pub fn try_reduce<F>(self, f: F) -> Source<Out, Mat>
1445    where
1446        Out: Clone,
1447        F: Fn(Out, Out) -> StreamResult<Out> + Send + Sync + 'static,
1448    {
1449        self.via(Flow::identity().try_reduce(f))
1450    }
1451
1452    /// Deprecated alias for [`Source::try_reduce`].
1453    #[must_use]
1454    #[deprecated(
1455        since = "0.9.0",
1456        note = "renamed to `try_reduce` (idiomatic); the `_result` name still works"
1457    )]
1458    pub fn reduce_result<F>(self, f: F) -> Source<Out, Mat>
1459    where
1460        Out: Clone,
1461        F: Fn(Out, Out) -> StreamResult<Out> + Send + Sync + 'static,
1462    {
1463        self.try_reduce(f)
1464    }
1465
1466    #[must_use]
1467    pub fn reduce_result_with_supervision<F>(
1468        self,
1469        f: F,
1470        decider: SupervisionDecider,
1471    ) -> Source<Out, Mat>
1472    where
1473        Out: Clone,
1474        F: Fn(Out, Out) -> StreamResult<Out> + Send + Sync + 'static,
1475    {
1476        self.via(Flow::identity().reduce_result_with_supervision(f, decider))
1477    }
1478
1479    #[must_use]
1480    pub fn map_error<F>(self, f: F) -> Source<Out, Mat>
1481    where
1482        F: Fn(StreamError) -> StreamError + Send + Sync + 'static,
1483    {
1484        self.via(Flow::identity().map_error(f))
1485    }
1486
1487    #[must_use]
1488    pub fn recover<F>(self, f: F) -> Source<Out, Mat>
1489    where
1490        F: Fn(StreamError) -> Option<Out> + Send + Sync + 'static,
1491    {
1492        self.via(Flow::identity().recover(f))
1493    }
1494
1495    #[must_use]
1496    pub fn recover_with<F>(self, f: F) -> Source<Out, Mat>
1497    where
1498        F: Fn(StreamError) -> Option<Source<Out>> + Send + Sync + 'static,
1499    {
1500        self.via(Flow::identity().recover_with(f))
1501    }
1502
1503    #[must_use]
1504    pub fn recover_with_retries<F>(self, retries: usize, f: F) -> Source<Out, Mat>
1505    where
1506        F: Fn(StreamError) -> Option<Source<Out>> + Send + Sync + 'static,
1507    {
1508        self.via(Flow::identity().recover_with_retries(retries, f))
1509    }
1510
1511    #[must_use]
1512    pub fn on_error_complete(self) -> Source<Out, Mat> {
1513        self.via(Flow::identity().on_error_complete())
1514    }
1515
1516    #[must_use]
1517    pub fn concat<Mat2>(self, that: Source<Out, Mat2>) -> Source<Out, Mat>
1518    where
1519        Mat2: Send + 'static,
1520    {
1521        let factory = self.factory;
1522        let hints = self.hints;
1523        let that_factory = that.factory;
1524        Source::from_materialized_factory_with_hints(
1525            move |materializer| {
1526                let (primary, mat) = Arc::clone(&factory).create(materializer)?;
1527                let secondary = match Arc::clone(&that_factory).create(materializer) {
1528                    Ok((stream, _)) => stream,
1529                    Err(error) => Box::new(std::iter::once(Err(error))) as BoxStream<Out>,
1530                };
1531                Ok((concat_source_streams(vec![primary, secondary]), mat))
1532            },
1533            hints,
1534        )
1535    }
1536
1537    #[must_use]
1538    pub fn concat_lazy<Mat2>(self, that: Source<Out, Mat2>) -> Source<Out, Mat>
1539    where
1540        Mat2: Send + 'static,
1541    {
1542        let factory = self.factory;
1543        let hints = self.hints;
1544        let that_factory = that.factory;
1545        Source::from_materialized_factory_with_hints(
1546            move |materializer| {
1547                let (primary, mat) = Arc::clone(&factory).create(materializer)?;
1548                Ok((
1549                    concat_source_streams_lazy(
1550                        primary,
1551                        vec![Arc::clone(&that_factory)],
1552                        materializer,
1553                    ),
1554                    mat,
1555                ))
1556            },
1557            hints,
1558        )
1559    }
1560
1561    #[must_use]
1562    pub fn concat_all_lazy<Mat2, I>(self, those: I) -> Source<Out, Mat>
1563    where
1564        Mat2: Send + 'static,
1565        I: IntoIterator<Item = Source<Out, Mat2>>,
1566    {
1567        let factory = self.factory;
1568        let hints = self.hints;
1569        let other_factories: Vec<_> = those.into_iter().map(|source| source.factory).collect();
1570        Source::from_materialized_factory_with_hints(
1571            move |materializer| {
1572                let (primary, mat) = Arc::clone(&factory).create(materializer)?;
1573                Ok((
1574                    concat_source_streams_lazy(primary, other_factories.clone(), materializer),
1575                    mat,
1576                ))
1577            },
1578            hints,
1579        )
1580    }
1581
1582    #[must_use]
1583    pub fn prepend<Mat2>(self, that: Source<Out, Mat2>) -> Source<Out, Mat>
1584    where
1585        Mat2: Send + 'static,
1586    {
1587        let factory = self.factory;
1588        let hints = self.hints;
1589        let that_factory = that.factory;
1590        Source::from_materialized_factory_with_hints(
1591            move |materializer| {
1592                let (primary, mat) = Arc::clone(&factory).create(materializer)?;
1593                let secondary = match Arc::clone(&that_factory).create(materializer) {
1594                    Ok((stream, _)) => stream,
1595                    Err(error) => Box::new(std::iter::once(Err(error))) as BoxStream<Out>,
1596                };
1597                Ok((concat_source_streams(vec![secondary, primary]), mat))
1598            },
1599            hints,
1600        )
1601    }
1602
1603    #[must_use]
1604    pub fn prepend_lazy<Mat2>(self, that: Source<Out, Mat2>) -> Source<Out, Mat>
1605    where
1606        Mat2: Send + 'static,
1607    {
1608        self.prepend(that)
1609    }
1610
1611    #[must_use]
1612    pub fn or_else<Mat2>(self, secondary: Source<Out, Mat2>) -> Source<Out, Mat>
1613    where
1614        Mat2: Send + 'static,
1615    {
1616        let factory = self.factory;
1617        let hints = self.hints;
1618        let secondary_factory = secondary.factory;
1619        Source::from_materialized_factory_with_hints(
1620            move |materializer| {
1621                let (primary, mat) = Arc::clone(&factory).create(materializer)?;
1622                let secondary = match Arc::clone(&secondary_factory).create(materializer) {
1623                    Ok((stream, _)) => stream,
1624                    Err(error) => Box::new(std::iter::once(Err(error))) as BoxStream<Out>,
1625                };
1626                Ok((or_else_source_stream(primary, secondary), mat))
1627            },
1628            hints,
1629        )
1630    }
1631
1632    #[must_use]
1633    pub fn interleave<Mat2>(self, that: Source<Out, Mat2>, segment_size: usize) -> Source<Out, Mat>
1634    where
1635        Mat2: Send + 'static,
1636    {
1637        self.interleave_all([that], segment_size, false)
1638    }
1639
1640    #[must_use]
1641    pub fn interleave_all<Mat2, I>(
1642        self,
1643        those: I,
1644        segment_size: usize,
1645        eager_close: bool,
1646    ) -> Source<Out, Mat>
1647    where
1648        Mat2: Send + 'static,
1649        I: IntoIterator<Item = Source<Out, Mat2>>,
1650    {
1651        let factory = self.factory;
1652        let hints = self.hints;
1653        let other_factories: Vec<_> = those.into_iter().map(|source| source.factory).collect();
1654        Source::from_materialized_factory_with_hints(
1655            move |materializer| {
1656                let (primary, mat) = Arc::clone(&factory).create(materializer)?;
1657                let mut streams = Vec::with_capacity(other_factories.len() + 1);
1658                streams.push(primary);
1659                for other in &other_factories {
1660                    let stream = match Arc::clone(other).create(materializer) {
1661                        Ok((stream, _)) => stream,
1662                        Err(error) => Box::new(std::iter::once(Err(error))) as BoxStream<Out>,
1663                    };
1664                    streams.push(stream);
1665                }
1666                Ok((
1667                    interleave_source_streams(streams, segment_size, eager_close),
1668                    mat,
1669                ))
1670            },
1671            hints,
1672        )
1673    }
1674
1675    #[must_use]
1676    pub fn merge_sorted<Mat2>(self, that: Source<Out, Mat2>) -> Source<Out, Mat>
1677    where
1678        Out: Ord,
1679        Mat2: Send + 'static,
1680    {
1681        self.via(Flow::identity().merge_sorted(that))
1682    }
1683
1684    #[must_use]
1685    pub fn merge_latest<Mat2>(
1686        self,
1687        that: Source<Out, Mat2>,
1688        eager_complete: bool,
1689    ) -> Source<Vec<Out>, Mat>
1690    where
1691        Out: Clone,
1692        Mat2: Send + 'static,
1693    {
1694        let factory = self.factory;
1695        let hints = self.hints;
1696        let that_factory = that.factory;
1697        Source::from_materialized_factory_with_hints(
1698            move |materializer| {
1699                let (left, mat) = Arc::clone(&factory).create(materializer)?;
1700                let right = match Arc::clone(&that_factory).create(materializer) {
1701                    Ok((stream, _)) => stream,
1702                    Err(error) => {
1703                        return Ok((
1704                            Box::new(std::iter::once(Err(error))) as BoxStream<Vec<Out>>,
1705                            mat,
1706                        ));
1707                    }
1708                };
1709                Ok((merge_latest_streams(vec![left, right], eager_complete), mat))
1710            },
1711            hints,
1712        )
1713    }
1714
1715    #[must_use]
1716    pub fn merge_all<Mat2, I>(self, those: I, eager_complete: bool) -> Source<Out, Mat>
1717    where
1718        Mat2: Send + 'static,
1719        I: IntoIterator<Item = Source<Out, Mat2>>,
1720    {
1721        let factory = self.factory;
1722        let hints = self.hints;
1723        let other_factories: Vec<_> = those.into_iter().map(|source| source.factory).collect();
1724        Source::from_materialized_factory_with_hints(
1725            move |materializer| {
1726                let (primary, mat) = Arc::clone(&factory).create(materializer)?;
1727                let mut streams = Vec::with_capacity(other_factories.len() + 1);
1728                streams.push(primary);
1729                for other in &other_factories {
1730                    let stream = match Arc::clone(other).create(materializer) {
1731                        Ok((stream, _)) => stream,
1732                        Err(error) => {
1733                            return Ok((
1734                                Box::new(std::iter::once(Err(error))) as BoxStream<Out>,
1735                                mat,
1736                            ));
1737                        }
1738                    };
1739                    streams.push(stream);
1740                }
1741                Ok((merge_streams(streams, eager_complete), mat))
1742            },
1743            hints,
1744        )
1745    }
1746
1747    #[must_use]
1748    pub fn zip_with<Mat2, Out2, Next, F>(
1749        self,
1750        that: Source<Out2, Mat2>,
1751        combine: F,
1752    ) -> Source<Next, Mat>
1753    where
1754        Out2: Send + 'static,
1755        Next: Send + 'static,
1756        Mat2: Send + 'static,
1757        F: Fn(Out, Out2) -> Next + Send + Sync + 'static,
1758    {
1759        self.via(Flow::identity().zip_with(that, combine))
1760    }
1761
1762    #[must_use]
1763    pub fn zip_latest<Mat2, Out2>(self, that: Source<Out2, Mat2>) -> Source<(Out, Out2), Mat>
1764    where
1765        Out: Clone,
1766        Out2: Clone + Send + 'static,
1767        Mat2: Send + 'static,
1768    {
1769        self.zip_latest_with(that, true, |left, right| (left, right))
1770    }
1771
1772    #[must_use]
1773    pub fn zip_latest_with<Mat2, Out2, Next, F>(
1774        self,
1775        that: Source<Out2, Mat2>,
1776        eager_complete: bool,
1777        combine: F,
1778    ) -> Source<Next, Mat>
1779    where
1780        Out: Clone,
1781        Out2: Clone + Send + 'static,
1782        Next: Send + 'static,
1783        Mat2: Send + 'static,
1784        F: Fn(Out, Out2) -> Next + Send + Sync + 'static,
1785    {
1786        let factory = self.factory;
1787        let hints = self.hints;
1788        let that_factory = that.factory;
1789        let combine = Arc::new(combine);
1790        Source::from_materialized_factory_with_hints(
1791            move |materializer| {
1792                let (left, mat) = Arc::clone(&factory).create(materializer)?;
1793                let right = match Arc::clone(&that_factory).create(materializer) {
1794                    Ok((stream, _)) => stream,
1795                    Err(error) => {
1796                        return Ok((
1797                            Box::new(std::iter::once(Err(error))) as BoxStream<Next>,
1798                            mat,
1799                        ));
1800                    }
1801                };
1802                Ok((
1803                    zip_latest_with_stream(left, right, eager_complete, Arc::clone(&combine)),
1804                    mat,
1805                ))
1806            },
1807            hints,
1808        )
1809    }
1810
1811    #[must_use]
1812    pub fn zip_with_index(self) -> Source<(Out, u64), Mat> {
1813        let factory = self.factory;
1814        let hints = self.hints;
1815        Source::from_materialized_factory_with_hints(
1816            move |materializer| {
1817                let (mut stream, mat) = Arc::clone(&factory).create(materializer)?;
1818                let mut index = 0_u64;
1819                Ok((
1820                    Box::new(std::iter::from_fn(move || {
1821                        stream.next().map(|item| {
1822                            item.map(|value| {
1823                                let pair = (value, index);
1824                                index = index.wrapping_add(1);
1825                                pair
1826                            })
1827                        })
1828                    })) as BoxStream<(Out, u64)>,
1829                    mat,
1830                ))
1831            },
1832            hints,
1833        )
1834    }
1835
1836    #[must_use]
1837    pub fn zip_all<Mat2, Out2>(
1838        self,
1839        that: Source<Out2, Mat2>,
1840        this_elem: Out,
1841        that_elem: Out2,
1842    ) -> Source<(Out, Out2), Mat>
1843    where
1844        Out: Clone + Sync,
1845        Out2: Clone + Send + Sync + 'static,
1846        Mat2: Send + 'static,
1847    {
1848        let factory = self.factory;
1849        let hints = self.hints;
1850        let that_factory = that.factory;
1851        Source::from_materialized_factory_with_hints(
1852            move |materializer| {
1853                let (left, mat) = Arc::clone(&factory).create(materializer)?;
1854                let right = match Arc::clone(&that_factory).create(materializer) {
1855                    Ok((stream, _)) => stream,
1856                    Err(error) => {
1857                        return Ok((
1858                            Box::new(std::iter::once(Err(error))) as BoxStream<(Out, Out2)>,
1859                            mat,
1860                        ));
1861                    }
1862                };
1863                Ok((
1864                    zip_all_stream(left, right, this_elem.clone(), that_elem.clone()),
1865                    mat,
1866                ))
1867            },
1868            hints,
1869        )
1870    }
1871
1872    #[must_use]
1873    pub fn also_to<SinkMat>(self, sink: Sink<Out, SinkMat>) -> Source<Out, Mat>
1874    where
1875        Out: Clone,
1876        SinkMat: Send + 'static,
1877    {
1878        self.via(Flow::identity().also_to(sink))
1879    }
1880
1881    #[must_use]
1882    pub fn also_to_all<SinkMat, I>(self, sinks: I) -> Source<Out, Mat>
1883    where
1884        Out: Clone,
1885        SinkMat: Send + 'static,
1886        I: IntoIterator<Item = Sink<Out, SinkMat>>,
1887    {
1888        self.via(Flow::identity().also_to_all(sinks))
1889    }
1890
1891    #[must_use]
1892    pub fn divert_to<SinkMat, F>(self, sink: Sink<Out, SinkMat>, predicate: F) -> Source<Out, Mat>
1893    where
1894        SinkMat: Send + 'static,
1895        F: Fn(&Out) -> bool + Send + Sync + 'static,
1896    {
1897        self.via(Flow::identity().divert_to(sink, predicate))
1898    }
1899
1900    #[must_use]
1901    pub fn wire_tap<SinkMat>(self, sink: Sink<Out, SinkMat>) -> Source<Out, Mat>
1902    where
1903        Out: Clone,
1904        SinkMat: Send + 'static,
1905    {
1906        self.via(Flow::identity().wire_tap(sink))
1907    }
1908
1909    pub fn run_with<SinkMat: Send + 'static>(
1910        self,
1911        sink: Sink<Out, SinkMat>,
1912    ) -> StreamResult<SinkMat> {
1913        // Split-segment fast path: if source has a segment hook and sink has a
1914        // fold fast-path descriptor, drive the fold inline without spawning a task.
1915        let fast_result = self
1916            .split_hook
1917            .as_ref()
1918            .zip(sink.fold_fp.as_deref())
1919            .and_then(|(hook, fp)| fp.try_register(Arc::clone(hook)));
1920        if let Some(result) = fast_result {
1921            return result?.downcast::<SinkMat>().map(|b| *b).map_err(|_| {
1922                StreamError::Failed("split fast path: unexpected mat type (internal error)".into())
1923            });
1924        }
1925        self.to_mat(sink, Keep::right).run()
1926    }
1927
1928    pub fn run_with_materializer<SinkMat: Send + 'static>(
1929        self,
1930        sink: Sink<Out, SinkMat>,
1931        materializer: &Materializer,
1932    ) -> StreamResult<SinkMat> {
1933        self.to_mat(sink, Keep::right)
1934            .run_with_materializer(materializer)
1935    }
1936
1937    #[must_use]
1938    pub fn to<SinkMat>(self, sink: Sink<Out, SinkMat>) -> RunnableGraph<Mat>
1939    where
1940        SinkMat: Send + 'static,
1941    {
1942        self.to_mat(sink, Keep::left)
1943    }
1944
1945    #[must_use]
1946    pub fn to_mat<SinkMat, Combined, F>(
1947        self,
1948        sink: Sink<Out, SinkMat>,
1949        combine: F,
1950    ) -> RunnableGraph<Combined>
1951    where
1952        SinkMat: Send + 'static,
1953        Combined: Send + 'static,
1954        F: Fn(Mat, SinkMat) -> Combined + Send + Sync + 'static,
1955    {
1956        let factory = self.factory;
1957        let terminal_factory = self.terminal_factory;
1958        let hints = self.hints;
1959        let combine = Arc::new(combine);
1960        RunnableGraph::from_runner(move |materializer| {
1961            if let Some(terminal_factory) = &terminal_factory
1962                && let Some(fold_fp) = sink.fold_fp.as_deref()
1963            {
1964                let (hook, source_mat) = terminal_factory(materializer)?;
1965                if hook.supports_direct_terminal()
1966                    && let Some(sink_mat) =
1967                        fold_fp.try_register_direct_terminal(Arc::clone(&hook), materializer)
1968                {
1969                    let sink_mat = sink_mat.and_then(|mat| {
1970                        mat.downcast::<SinkMat>().map(|boxed| *boxed).map_err(|_| {
1971                            StreamError::Failed(
1972                                "terminal fast path: unexpected mat type (internal error)".into(),
1973                            )
1974                        })
1975                    })?;
1976                    return Ok(combine(source_mat, sink_mat));
1977                }
1978                if !fold_fp.supports_terminal_drain() {
1979                    let (stream, source_mat) = Arc::clone(&factory).create(materializer)?;
1980                    let sink_mat = sink.run_from_source(stream, materializer, hints.runtime())?;
1981                    return Ok(combine(source_mat, sink_mat));
1982                }
1983                let sink_mat = fold_fp
1984                    .try_register_terminal_drain(hook, materializer)
1985                    .expect("terminal drain support advertised")
1986                    .and_then(|mat| {
1987                        mat.downcast::<SinkMat>().map(|boxed| *boxed).map_err(|_| {
1988                            StreamError::Failed(
1989                                "terminal fast path: unexpected mat type (internal error)".into(),
1990                            )
1991                        })
1992                    })?;
1993                return Ok(combine(source_mat, sink_mat));
1994            }
1995            let (stream, source_mat) = Arc::clone(&factory).create(materializer)?;
1996            let sink_mat = if hints.inline_head_terminal && sink.can_inline() {
1997                let stream =
1998                    runtime_checked_stream(stream, Arc::clone(&materializer.inner.state), None);
1999                sink.run_inline(stream, materializer)?
2000            } else {
2001                sink.run_from_source(stream, materializer, hints.runtime())?
2002            };
2003            Ok(combine(source_mat, sink_mat))
2004        })
2005    }
2006
2007    pub fn run_collect(self) -> StreamResult<Vec<Out>> {
2008        self.run_with(Sink::collect())?.wait()
2009    }
2010
2011    pub fn run_fold<Acc, F>(self, zero: Acc, f: F) -> StreamResult<StreamCompletion<Acc>>
2012    where
2013        Acc: Clone + Send + Sync + 'static,
2014        F: Fn(Acc, Out) -> Acc + Send + Sync + 'static,
2015    {
2016        self.run_with(Sink::fold(zero, f))
2017    }
2018
2019    pub fn run_foreach<F>(self, f: F) -> StreamResult<StreamCompletion<NotUsed>>
2020    where
2021        F: Fn(Out) + Send + Sync + 'static,
2022    {
2023        self.run_with(Sink::foreach(f))
2024    }
2025
2026    pub fn run_for_each<F>(self, f: F) -> StreamResult<StreamCompletion<NotUsed>>
2027    where
2028        F: Fn(Out) + Send + Sync + 'static,
2029    {
2030        self.run_with(Sink::foreach(f))
2031    }
2032
2033    pub fn run_reduce<F>(self, f: F) -> StreamResult<StreamCompletion<Out>>
2034    where
2035        F: Fn(Out, Out) -> Out + Send + Sync + 'static,
2036    {
2037        self.run_with(Sink::reduce(f))
2038    }
2039
2040    #[must_use]
2041    pub fn map_materialized_value<NextMat, F>(self, f: F) -> Source<Out, NextMat>
2042    where
2043        NextMat: Send + 'static,
2044        F: Fn(Mat) -> NextMat + Send + Sync + 'static,
2045    {
2046        let factory = self.factory;
2047        let terminal_factory = self.terminal_factory;
2048        let hints = self.hints;
2049        let f = Arc::new(f);
2050        let factory_f = Arc::clone(&f);
2051        let mapped_terminal_factory = terminal_factory.map(|terminal_factory| {
2052            let f = Arc::clone(&f);
2053            Arc::new(move |materializer: &Materializer| {
2054                let (hook, mat) = terminal_factory(materializer)?;
2055                Ok((hook, f(mat)))
2056            }) as Arc<TerminalSourceFactory<Out, NextMat>>
2057        });
2058        Source {
2059            factory: Arc::new(FnSourceFactory(move |materializer: &Materializer| {
2060                let (stream, mat) = Arc::clone(&factory).create(materializer)?;
2061                Ok((stream, factory_f(mat)))
2062            })),
2063            terminal_factory: mapped_terminal_factory,
2064            hints,
2065            attributes: Attributes::default(),
2066            split_hook: None,
2067        }
2068    }
2069}
2070
2071impl<Out: Clone + Send + Sync + 'static> Source<Out, NotUsed> {
2072    #[must_use]
2073    pub fn combine<Mat1, Mat2, MatRest, I>(
2074        first: Source<Out, Mat1>,
2075        second: Source<Out, Mat2>,
2076        rest: I,
2077        strategy: SourceCombineStrategy,
2078    ) -> Source<Out, NotUsed>
2079    where
2080        Mat1: Send + 'static,
2081        Mat2: Send + 'static,
2082        MatRest: Send + 'static,
2083        I: IntoIterator<Item = Source<Out, MatRest>>,
2084    {
2085        let mut factories: Vec<Arc<CombinedSourceFactory<Out>>> = vec![
2086            Arc::new(move |materializer| {
2087                Arc::clone(&first.factory)
2088                    .create(materializer)
2089                    .map(|(stream, _)| stream)
2090            }),
2091            Arc::new(move |materializer| {
2092                Arc::clone(&second.factory)
2093                    .create(materializer)
2094                    .map(|(stream, _)| stream)
2095            }),
2096        ];
2097        factories.extend(rest.into_iter().map(|source| {
2098            Arc::new(move |materializer: &Materializer| {
2099                Arc::clone(&source.factory)
2100                    .create(materializer)
2101                    .map(|(stream, _)| stream)
2102            }) as Arc<CombinedSourceFactory<Out>>
2103        }));
2104        Source::from_materialized_factory(move |materializer| {
2105            let mut streams = Vec::with_capacity(factories.len());
2106            for factory in &factories {
2107                let stream = match factory(materializer) {
2108                    Ok(stream) => stream,
2109                    Err(error) => {
2110                        return Ok((
2111                            Box::new(std::iter::once(Err(error))) as BoxStream<Out>,
2112                            NotUsed,
2113                        ));
2114                    }
2115                };
2116                streams.push(stream);
2117            }
2118            let stream = match &strategy {
2119                SourceCombineStrategy::Merge { eager_complete } => {
2120                    merge_streams(streams, *eager_complete)
2121                }
2122                SourceCombineStrategy::Concat => concat_source_streams(streams),
2123                SourceCombineStrategy::Prioritized {
2124                    priorities,
2125                    eager_complete,
2126                } => {
2127                    if priorities.len() != streams.len() {
2128                        return Err(StreamError::GraphValidation(format!(
2129                            "combine priorities length {} did not match source count {}",
2130                            priorities.len(),
2131                            streams.len()
2132                        )));
2133                    }
2134                    merge_prioritized_streams(streams, priorities.clone(), *eager_complete)
2135                }
2136            };
2137            Ok((stream, NotUsed))
2138        })
2139    }
2140
2141    #[must_use]
2142    pub fn zip_n<Mat2, I>(sources: I) -> Source<Vec<Out>, NotUsed>
2143    where
2144        I: IntoIterator<Item = Source<Out, Mat2>>,
2145        Mat2: Send + 'static,
2146        Out: Clone,
2147    {
2148        Self::zip_with_n(sources, |values| values)
2149    }
2150
2151    #[must_use]
2152    pub fn zip_with_n<Mat2, I, Next, F>(sources: I, zipper: F) -> Source<Next, NotUsed>
2153    where
2154        I: IntoIterator<Item = Source<Out, Mat2>>,
2155        Mat2: Send + 'static,
2156        Next: Send + 'static,
2157        F: Fn(Vec<Out>) -> Next + Send + Sync + 'static,
2158    {
2159        let factories: Vec<_> = sources.into_iter().map(|source| source.factory).collect();
2160        let zipper = Arc::new(zipper);
2161        Source::from_materialized_factory(move |materializer| {
2162            let mut streams = Vec::with_capacity(factories.len());
2163            for factory in &factories {
2164                let stream = match Arc::clone(factory).create(materializer) {
2165                    Ok((stream, _)) => stream,
2166                    Err(error) => {
2167                        return Ok((
2168                            Box::new(std::iter::once(Err(error))) as BoxStream<Next>,
2169                            NotUsed,
2170                        ));
2171                    }
2172                };
2173                streams.push(stream);
2174            }
2175            Ok((zip_n_streams(streams, Arc::clone(&zipper)), NotUsed))
2176        })
2177    }
2178
2179    #[must_use]
2180    pub fn merge_prioritized_n<Mat2, I>(
2181        sources_and_priorities: I,
2182        eager_complete: bool,
2183    ) -> Source<Out, NotUsed>
2184    where
2185        I: IntoIterator<Item = (Source<Out, Mat2>, usize)>,
2186        Mat2: Send + 'static,
2187    {
2188        let sources_and_priorities: Vec<_> = sources_and_priorities.into_iter().collect();
2189        if sources_and_priorities.is_empty() {
2190            return Source::empty();
2191        }
2192        let (factories, priorities): (Vec<_>, Vec<_>) = sources_and_priorities
2193            .into_iter()
2194            .map(|(source, priority)| (source.factory, priority))
2195            .unzip();
2196        Source::from_materialized_factory(move |materializer| {
2197            let mut streams = Vec::with_capacity(factories.len());
2198            for factory in &factories {
2199                let stream = match Arc::clone(factory).create(materializer) {
2200                    Ok((stream, _)) => stream,
2201                    Err(error) => {
2202                        return Ok((
2203                            Box::new(std::iter::once(Err(error))) as BoxStream<Out>,
2204                            NotUsed,
2205                        ));
2206                    }
2207                };
2208                streams.push(stream);
2209            }
2210            Ok((
2211                merge_prioritized_streams(streams, priorities.clone(), eager_complete),
2212                NotUsed,
2213            ))
2214        })
2215    }
2216
2217    #[must_use]
2218    pub fn maybe() -> (MaybeHandle<Out>, Self) {
2219        let value = Arc::new(Mutex::new(None));
2220        let handle = MaybeHandle {
2221            value: Arc::clone(&value),
2222        };
2223        let source = Self::from_factory(move || {
2224            let result = value
2225                .lock()
2226                .expect("maybe source poisoned")
2227                .clone()
2228                .unwrap_or(Err(StreamError::MaybeIncomplete));
2229            Box::new(std::iter::once(result))
2230        });
2231        (handle, source)
2232    }
2233
2234    #[must_use]
2235    pub fn single(item: Out) -> Self {
2236        Self::from_factory_with_hints(
2237            move || Box::new(std::iter::once(Ok(item.clone()))),
2238            SourceHints::with_inline_micro(1),
2239        )
2240    }
2241
2242    #[must_use]
2243    pub fn repeat(item: Out) -> Self {
2244        Self::from_factory(move || {
2245            let item = item.clone();
2246            Box::new(std::iter::repeat_with(move || Ok(item.clone())))
2247        })
2248    }
2249
2250    #[must_use]
2251    pub fn from_iterable<I>(items: I) -> Self
2252    where
2253        I: IntoIterator<Item = Out>,
2254    {
2255        items.into_iter().collect()
2256    }
2257}
2258
2259impl<Out: Clone + Send + Sync + 'static> FromIterator<Out> for Source<Out, NotUsed> {
2260    fn from_iter<T: IntoIterator<Item = Out>>(iter: T) -> Self {
2261        // Share the backing storage across materializations and clone elements
2262        // lazily on demand, instead of cloning the whole container each run.
2263        let items: Arc<[Out]> = iter.into_iter().collect();
2264        let len = items.len();
2265        Self::from_factory_with_hints(
2266            move || {
2267                let items = Arc::clone(&items);
2268                let mut index = 0;
2269                Box::new(std::iter::from_fn(move || {
2270                    let item = items.get(index)?.clone();
2271                    index += 1;
2272                    Some(Ok(item))
2273                }))
2274            },
2275            // Exact success item count: enables the coordinator inline drain path.
2276            SourceHints::with_inline_micro(len),
2277        )
2278    }
2279}
2280
2281impl<Out: Clone + Send + Sync + 'static> From<Vec<Out>> for Source<Out, NotUsed> {
2282    fn from(items: Vec<Out>) -> Self {
2283        Source::from_iter(items)
2284    }
2285}
2286
2287impl<Out: Clone + Send + Sync + 'static, const N: usize> From<[Out; N]> for Source<Out, NotUsed> {
2288    fn from(items: [Out; N]) -> Self {
2289        Source::from_iter(items)
2290    }
2291}
2292
2293/// Converts any [`IntoIterator`] into a [`Source`] explicitly.
2294///
2295/// `Vec<T>` and `[T; N]` also implement `From` for `Source<T>`, while broader
2296/// iterables such as ranges intentionally use this trait to avoid a blanket
2297/// `From<I: IntoIterator>` inference trap.
2298///
2299/// ```
2300/// use datum::{IntoSource, Source};
2301///
2302/// let via_vec_into: Source<u64> = vec![1, 2, 3].into();
2303/// let via_array_into: Source<u64> = [1, 2, 3].into();
2304/// let via_range = (0_u64..3).into_source();
2305/// let via_from_iter = Source::from_iter(0_u64..3);
2306/// let via_from_iterator: Source<u64> = (0_u64..3).collect();
2307///
2308/// let _ = (
2309///     via_vec_into,
2310///     via_array_into,
2311///     via_range,
2312///     via_from_iter,
2313///     via_from_iterator,
2314/// );
2315/// ```
2316///
2317/// ```compile_fail
2318/// use datum::Source;
2319///
2320/// let _source: Source<u64> = (0_u64..3).into();
2321/// ```
2322pub trait IntoSource: IntoIterator + Sized {
2323    #[must_use]
2324    fn into_source(self) -> Source<Self::Item, NotUsed>
2325    where
2326        Self::Item: Clone + Send + Sync + 'static,
2327    {
2328        Source::from_iter(self)
2329    }
2330}
2331
2332impl<I> IntoSource for I where I: IntoIterator {}
2333
2334/// Test-only helper: create a Source whose factory calls the given closure but
2335/// marks it as inline-micro eligible. Used to test the inline drain path with
2336/// custom (possibly blocking) iterators without broadening production
2337/// eligibility.
2338#[cfg(test)]
2339pub(in crate::stream) fn test_source_with_inline_micro_hint<Out: Send + 'static>(
2340    factory: impl Fn() -> BoxStream<Out> + Send + Sync + 'static,
2341    max_success_items: usize,
2342) -> Source<Out, NotUsed> {
2343    Source::from_factory_with_hints(factory, SourceHints::with_inline_micro(max_success_items))
2344}
2345
2346struct UnfoldResourceStream<Resource, Out, Create, Read, Close>
2347where
2348    Create: Fn() -> StreamResult<Resource>,
2349    Read: Fn(&mut Resource) -> StreamResult<Option<Out>>,
2350    Close: Fn(Resource) -> StreamResult<()>,
2351{
2352    create: Arc<Create>,
2353    read: Arc<Read>,
2354    close: Arc<Close>,
2355    resource: Option<Resource>,
2356    created: bool,
2357    terminated: bool,
2358    _marker: PhantomData<fn() -> Out>,
2359}
2360
2361impl<Resource, Out, Create, Read, Close> UnfoldResourceStream<Resource, Out, Create, Read, Close>
2362where
2363    Create: Fn() -> StreamResult<Resource>,
2364    Read: Fn(&mut Resource) -> StreamResult<Option<Out>>,
2365    Close: Fn(Resource) -> StreamResult<()>,
2366{
2367    fn ensure_created(&mut self) -> StreamResult<()> {
2368        if self.created {
2369            return Ok(());
2370        }
2371        self.created = true;
2372        let resource = catch_unwind_failed("unfold_resource create", || (self.create)())
2373            .and_then(|result| result)?;
2374        self.resource = Some(resource);
2375        Ok(())
2376    }
2377
2378    fn close_resource(&mut self) -> StreamResult<()> {
2379        match self.resource.take() {
2380            Some(resource) => {
2381                catch_unwind_failed("unfold_resource close", || (self.close)(resource))
2382                    .and_then(|result| result)
2383            }
2384            None => Ok(()),
2385        }
2386    }
2387}
2388
2389impl<Resource, Out, Create, Read, Close> Iterator
2390    for UnfoldResourceStream<Resource, Out, Create, Read, Close>
2391where
2392    Create: Fn() -> StreamResult<Resource>,
2393    Read: Fn(&mut Resource) -> StreamResult<Option<Out>>,
2394    Close: Fn(Resource) -> StreamResult<()>,
2395{
2396    type Item = StreamResult<Out>;
2397
2398    fn next(&mut self) -> Option<Self::Item> {
2399        if self.terminated {
2400            return None;
2401        }
2402        if let Err(error) = self.ensure_created() {
2403            self.terminated = true;
2404            return Some(Err(error));
2405        }
2406
2407        let result = {
2408            let resource = self
2409                .resource
2410                .as_mut()
2411                .expect("unfold_resource resource is open");
2412            catch_unwind_failed("unfold_resource read", || (self.read)(resource))
2413                .and_then(|result| result)
2414        };
2415
2416        match result {
2417            Ok(Some(item)) => Some(Ok(item)),
2418            Ok(None) => {
2419                self.terminated = true;
2420                match self.close_resource() {
2421                    Ok(()) => None,
2422                    Err(error) => Some(Err(error)),
2423                }
2424            }
2425            Err(read_error) => {
2426                self.terminated = true;
2427                let _ = self.close_resource();
2428                Some(Err(read_error))
2429            }
2430        }
2431    }
2432}
2433
2434impl<Resource, Out, Create, Read, Close> Drop
2435    for UnfoldResourceStream<Resource, Out, Create, Read, Close>
2436where
2437    Create: Fn() -> StreamResult<Resource>,
2438    Read: Fn(&mut Resource) -> StreamResult<Option<Out>>,
2439    Close: Fn(Resource) -> StreamResult<()>,
2440{
2441    fn drop(&mut self) {
2442        let _ = self.close_resource();
2443    }
2444}
2445
2446type UnfoldResourceAsyncMarker<Out, CreateFut, ReadFut, CloseFut> =
2447    fn() -> (Out, CreateFut, ReadFut, CloseFut);
2448
2449struct UnfoldResourceAsyncStream<Resource, Out, Create, CreateFut, Read, ReadFut, Close, CloseFut>
2450where
2451    Resource: Send + 'static,
2452    Create: Fn() -> CreateFut,
2453    CreateFut: Future<Output = StreamResult<Resource>> + Send + 'static,
2454    Read: Fn(&mut Resource) -> ReadFut,
2455    ReadFut: Future<Output = StreamResult<Option<Out>>> + Send + 'static,
2456    Close: Fn(Resource) -> CloseFut,
2457    CloseFut: Future<Output = StreamResult<()>> + Send + 'static,
2458{
2459    create: Arc<Create>,
2460    read: Arc<Read>,
2461    close: Arc<Close>,
2462    resource: Option<Resource>,
2463    created: bool,
2464    terminated: bool,
2465    _marker: PhantomData<UnfoldResourceAsyncMarker<Out, CreateFut, ReadFut, CloseFut>>,
2466}
2467
2468impl<Resource, Out, Create, CreateFut, Read, ReadFut, Close, CloseFut>
2469    UnfoldResourceAsyncStream<Resource, Out, Create, CreateFut, Read, ReadFut, Close, CloseFut>
2470where
2471    Resource: Send + 'static,
2472    Create: Fn() -> CreateFut,
2473    CreateFut: Future<Output = StreamResult<Resource>> + Send + 'static,
2474    Read: Fn(&mut Resource) -> ReadFut,
2475    ReadFut: Future<Output = StreamResult<Option<Out>>> + Send + 'static,
2476    Close: Fn(Resource) -> CloseFut,
2477    CloseFut: Future<Output = StreamResult<()>> + Send + 'static,
2478{
2479    fn ensure_created(&mut self) -> StreamResult<()> {
2480        if self.created {
2481            return Ok(());
2482        }
2483        self.created = true;
2484        let resource = catch_unwind_failed("unfold_resource_async create", || (self.create)())
2485            .and_then(flow::run_future_inline_or_spawn)?;
2486        self.resource = Some(resource);
2487        Ok(())
2488    }
2489
2490    fn close_resource(&mut self) -> StreamResult<()> {
2491        match self.resource.take() {
2492            Some(resource) => {
2493                catch_unwind_failed("unfold_resource_async close", || (self.close)(resource))
2494                    .and_then(flow::run_future_inline_or_spawn)
2495            }
2496            None => Ok(()),
2497        }
2498    }
2499}
2500
2501impl<Resource, Out, Create, CreateFut, Read, ReadFut, Close, CloseFut> Iterator
2502    for UnfoldResourceAsyncStream<Resource, Out, Create, CreateFut, Read, ReadFut, Close, CloseFut>
2503where
2504    Resource: Send + 'static,
2505    Out: Send + 'static,
2506    Create: Fn() -> CreateFut,
2507    CreateFut: Future<Output = StreamResult<Resource>> + Send + 'static,
2508    Read: Fn(&mut Resource) -> ReadFut,
2509    ReadFut: Future<Output = StreamResult<Option<Out>>> + Send + 'static,
2510    Close: Fn(Resource) -> CloseFut,
2511    CloseFut: Future<Output = StreamResult<()>> + Send + 'static,
2512{
2513    type Item = StreamResult<Out>;
2514
2515    fn next(&mut self) -> Option<Self::Item> {
2516        if self.terminated {
2517            return None;
2518        }
2519        if let Err(error) = self.ensure_created() {
2520            self.terminated = true;
2521            return Some(Err(error));
2522        }
2523
2524        let result = {
2525            let resource = self
2526                .resource
2527                .as_mut()
2528                .expect("unfold_resource_async resource is open");
2529            catch_unwind_failed("unfold_resource_async read", || (self.read)(resource))
2530                .and_then(flow::run_future_inline_or_spawn)
2531        };
2532
2533        match result {
2534            Ok(Some(item)) => Some(Ok(item)),
2535            Ok(None) => {
2536                self.terminated = true;
2537                match self.close_resource() {
2538                    Ok(()) => None,
2539                    Err(error) => Some(Err(error)),
2540                }
2541            }
2542            Err(read_error) => {
2543                self.terminated = true;
2544                let _ = self.close_resource();
2545                Some(Err(read_error))
2546            }
2547        }
2548    }
2549}
2550
2551impl<Resource, Out, Create, CreateFut, Read, ReadFut, Close, CloseFut> Drop
2552    for UnfoldResourceAsyncStream<Resource, Out, Create, CreateFut, Read, ReadFut, Close, CloseFut>
2553where
2554    Resource: Send + 'static,
2555    Create: Fn() -> CreateFut,
2556    CreateFut: Future<Output = StreamResult<Resource>> + Send + 'static,
2557    Read: Fn(&mut Resource) -> ReadFut,
2558    ReadFut: Future<Output = StreamResult<Option<Out>>> + Send + 'static,
2559    Close: Fn(Resource) -> CloseFut,
2560    CloseFut: Future<Output = StreamResult<()>> + Send + 'static,
2561{
2562    fn drop(&mut self) {
2563        let _ = self.close_resource();
2564    }
2565}
2566
2567struct LazySourceStream<Out, InnerMat, F> {
2568    create: Arc<F>,
2569    materializer: Materializer,
2570    current: Option<BoxStream<Out>>,
2571    mat_sender: Option<oneshot::Sender<StreamResult<InnerMat>>>,
2572    initialized: bool,
2573    terminated: bool,
2574}
2575
2576impl<Out, InnerMat, F> LazySourceStream<Out, InnerMat, F>
2577where
2578    Out: Send + 'static,
2579    InnerMat: Send + 'static,
2580    F: Fn() -> Source<Out, InnerMat>,
2581{
2582    fn complete_mat(&mut self, result: StreamResult<InnerMat>) {
2583        if let Some(sender) = self.mat_sender.take() {
2584            let _ = sender.send(result);
2585        }
2586    }
2587
2588    fn initialize(&mut self) -> StreamResult<()> {
2589        if self.initialized {
2590            return Ok(());
2591        }
2592        self.initialized = true;
2593        let source = match catch_unwind_failed("lazy_source factory", || (self.create)()) {
2594            Ok(source) => source,
2595            Err(error) => {
2596                self.complete_mat(Err(error.clone()));
2597                return Err(error);
2598            }
2599        };
2600        match Arc::clone(&source.factory).create(&self.materializer) {
2601            Ok((stream, mat)) => {
2602                self.current = Some(stream);
2603                self.complete_mat(Ok(mat));
2604                Ok(())
2605            }
2606            Err(error) => {
2607                self.complete_mat(Err(error.clone()));
2608                Err(error)
2609            }
2610        }
2611    }
2612}
2613
2614impl<Out, InnerMat, F> Iterator for LazySourceStream<Out, InnerMat, F>
2615where
2616    Out: Send + 'static,
2617    InnerMat: Send + 'static,
2618    F: Fn() -> Source<Out, InnerMat>,
2619{
2620    type Item = StreamResult<Out>;
2621
2622    fn next(&mut self) -> Option<Self::Item> {
2623        if self.terminated {
2624            return None;
2625        }
2626        if let Err(error) = self.initialize() {
2627            self.terminated = true;
2628            return Some(Err(error));
2629        }
2630        match self
2631            .current
2632            .as_mut()
2633            .expect("lazy_source current stream initialized")
2634            .next()
2635        {
2636            Some(Ok(item)) => Some(Ok(item)),
2637            Some(Err(error)) => {
2638                self.terminated = true;
2639                Some(Err(error))
2640            }
2641            None => {
2642                self.terminated = true;
2643                None
2644            }
2645        }
2646    }
2647}
2648
2649impl<Out, InnerMat, F> Drop for LazySourceStream<Out, InnerMat, F> {
2650    fn drop(&mut self) {
2651        if !self.initialized
2652            && let Some(sender) = self.mat_sender.take()
2653        {
2654            let _ = sender.send(Err(StreamError::Failed(
2655                "lazy source was never materialized".into(),
2656            )));
2657        }
2658    }
2659}
2660
2661struct LazyFutureSourceStream<Out, InnerMat, F, Fut> {
2662    create: Arc<F>,
2663    materializer: Materializer,
2664    current: Option<BoxStream<Out>>,
2665    mat_sender: Option<oneshot::Sender<StreamResult<InnerMat>>>,
2666    initialized: bool,
2667    terminated: bool,
2668    _marker: PhantomData<fn() -> Fut>,
2669}
2670
2671impl<Out, InnerMat, F, Fut> LazyFutureSourceStream<Out, InnerMat, F, Fut>
2672where
2673    Out: Send + 'static,
2674    InnerMat: Send + 'static,
2675    F: Fn() -> Fut,
2676    Fut: Future<Output = StreamResult<Source<Out, InnerMat>>> + Send + 'static,
2677{
2678    fn complete_mat(&mut self, result: StreamResult<InnerMat>) {
2679        if let Some(sender) = self.mat_sender.take() {
2680            let _ = sender.send(result);
2681        }
2682    }
2683
2684    fn initialize(&mut self) -> StreamResult<()> {
2685        if self.initialized {
2686            return Ok(());
2687        }
2688        self.initialized = true;
2689        let source = match catch_unwind_failed("lazy_future_source factory", || (self.create)())
2690            .and_then(flow::run_future_inline_or_spawn)
2691        {
2692            Ok(source) => source,
2693            Err(error) => {
2694                self.complete_mat(Err(error.clone()));
2695                return Err(error);
2696            }
2697        };
2698        match Arc::clone(&source.factory).create(&self.materializer) {
2699            Ok((stream, mat)) => {
2700                self.current = Some(stream);
2701                self.complete_mat(Ok(mat));
2702                Ok(())
2703            }
2704            Err(error) => {
2705                self.complete_mat(Err(error.clone()));
2706                Err(error)
2707            }
2708        }
2709    }
2710}
2711
2712impl<Out, InnerMat, F, Fut> Iterator for LazyFutureSourceStream<Out, InnerMat, F, Fut>
2713where
2714    Out: Send + 'static,
2715    InnerMat: Send + 'static,
2716    F: Fn() -> Fut,
2717    Fut: Future<Output = StreamResult<Source<Out, InnerMat>>> + Send + 'static,
2718{
2719    type Item = StreamResult<Out>;
2720
2721    fn next(&mut self) -> Option<Self::Item> {
2722        if self.terminated {
2723            return None;
2724        }
2725        if let Err(error) = self.initialize() {
2726            self.terminated = true;
2727            return Some(Err(error));
2728        }
2729        match self
2730            .current
2731            .as_mut()
2732            .expect("lazy_future_source current stream initialized")
2733            .next()
2734        {
2735            Some(Ok(item)) => Some(Ok(item)),
2736            Some(Err(error)) => {
2737                self.terminated = true;
2738                Some(Err(error))
2739            }
2740            None => {
2741                self.terminated = true;
2742                None
2743            }
2744        }
2745    }
2746}
2747
2748impl<Out, InnerMat, F, Fut> Drop for LazyFutureSourceStream<Out, InnerMat, F, Fut> {
2749    fn drop(&mut self) {
2750        if !self.initialized
2751            && let Some(sender) = self.mat_sender.take()
2752        {
2753            let _ = sender.send(Err(StreamError::Failed(
2754                "lazy future source was never materialized".into(),
2755            )));
2756        }
2757    }
2758}
2759
2760fn concat_source_streams<Out>(streams: Vec<BoxStream<Out>>) -> BoxStream<Out>
2761where
2762    Out: Send + 'static,
2763{
2764    let mut streams: VecDeque<_> = streams.into();
2765    let mut current = streams.pop_front();
2766    Box::new(std::iter::from_fn(move || {
2767        loop {
2768            match current.as_mut() {
2769                Some(stream) => match stream.next() {
2770                    Some(item) => return Some(item),
2771                    None => current = streams.pop_front(),
2772                },
2773                None => return None,
2774            }
2775        }
2776    }))
2777}
2778
2779fn concat_source_streams_lazy<Out, Mat>(
2780    initial: BoxStream<Out>,
2781    factories: Vec<Arc<dyn SourceFactory<Out, Mat>>>,
2782    materializer: &Materializer,
2783) -> BoxStream<Out>
2784where
2785    Out: Send + 'static,
2786    Mat: Send + 'static,
2787{
2788    let mut current = Some(initial);
2789    let mut remaining: VecDeque<_> = factories.into();
2790    let materializer = materializer.with_name_prefix(materializer.name_prefix().to_owned());
2791    Box::new(std::iter::from_fn(move || {
2792        loop {
2793            match current.as_mut() {
2794                Some(stream) => match stream.next() {
2795                    Some(item) => return Some(item),
2796                    None => {
2797                        current = remaining.pop_front().map(|factory| {
2798                            match factory.create(&materializer) {
2799                                Ok((stream, _)) => stream,
2800                                Err(error) => {
2801                                    Box::new(std::iter::once(Err(error))) as BoxStream<Out>
2802                                }
2803                            }
2804                        });
2805                    }
2806                },
2807                None => return None,
2808            }
2809        }
2810    }))
2811}
2812
2813fn or_else_source_stream<Out>(
2814    mut primary: BoxStream<Out>,
2815    mut secondary: BoxStream<Out>,
2816) -> BoxStream<Out>
2817where
2818    Out: Send + 'static,
2819{
2820    let mut primary_emitted = false;
2821    let mut using_secondary = false;
2822    Box::new(std::iter::from_fn(move || {
2823        loop {
2824            if using_secondary {
2825                return secondary.next();
2826            }
2827
2828            match primary.next() {
2829                Some(Ok(item)) => {
2830                    primary_emitted = true;
2831                    return Some(Ok(item));
2832                }
2833                Some(Err(error)) => return Some(Err(error)),
2834                None if primary_emitted => return None,
2835                None => using_secondary = true,
2836            }
2837        }
2838    }))
2839}
2840
2841fn interleave_source_streams<Out>(
2842    streams: Vec<BoxStream<Out>>,
2843    segment_size: usize,
2844    eager_close: bool,
2845) -> BoxStream<Out>
2846where
2847    Out: Send + 'static,
2848{
2849    if segment_size == 0 {
2850        return Box::new(std::iter::once(Err(StreamError::GraphValidation(
2851            "interleave segment size must be greater than zero".into(),
2852        ))));
2853    }
2854
2855    let mut streams: Vec<Option<BoxStream<Out>>> = streams.into_iter().map(Some).collect();
2856    let mut pending: Vec<Option<StreamResult<Out>>> = (0..streams.len()).map(|_| None).collect();
2857    let mut current = 0usize;
2858    let mut emitted = 0usize;
2859    Box::new(std::iter::from_fn(move || {
2860        loop {
2861            if streams.iter().all(Option::is_none) {
2862                return None;
2863            }
2864            if streams[current].is_none() {
2865                match next_active_source_stream(&streams, current) {
2866                    Some(next) => {
2867                        current = next;
2868                        emitted = 0;
2869                    }
2870                    None => return None,
2871                }
2872            }
2873
2874            let Some(stream) = streams[current].as_mut() else {
2875                continue;
2876            };
2877            let next_item = pending[current].take().or_else(|| stream.next());
2878            match next_item {
2879                Some(Ok(item)) => {
2880                    emitted += 1;
2881                    if emitted == segment_size {
2882                        emitted = 0;
2883                        if let Some(next) = next_active_source_stream(&streams, current) {
2884                            current = next;
2885                        }
2886                    }
2887                    return Some(Ok(item));
2888                }
2889                Some(Err(error)) => return Some(Err(error)),
2890                None => {
2891                    streams[current] = None;
2892                    emitted = 0;
2893                    if eager_close {
2894                        return None;
2895                    }
2896                    match next_active_source_stream(&streams, current) {
2897                        Some(next) => current = next,
2898                        None => return None,
2899                    }
2900                }
2901            }
2902        }
2903    }))
2904}
2905
2906fn next_active_source_stream<Out>(
2907    streams: &[Option<BoxStream<Out>>],
2908    current: usize,
2909) -> Option<usize>
2910where
2911    Out: Send + 'static,
2912{
2913    if streams.is_empty() {
2914        return None;
2915    }
2916    for offset in 1..=streams.len() {
2917        let index = (current + offset) % streams.len();
2918        if streams[index].is_some() {
2919            return Some(index);
2920        }
2921    }
2922    None
2923}