Skip to main content

zrx_stream/stream/function/signature/
flat_map.rs

1// Copyright (c) 2025-2026 Zensical and contributors
2
3// SPDX-License-Identifier: MIT
4// All contributions are certified under the DCO
5
6// Permission is hereby granted, free of charge, to any person obtaining a copy
7// of this software and associated documentation files (the "Software"), to
8// deal in the Software without restriction, including without limitation the
9// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10// sell copies of the Software, and to permit persons to whom the Software is
11// furnished to do so, subject to the following conditions:
12
13// The above copyright notice and this permission notice shall be included in
14// all copies or substantial portions of the Software.
15
16// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
19// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22// IN THE SOFTWARE.
23
24// ----------------------------------------------------------------------------
25
26//! Flat map function.
27
28use std::fmt::Display;
29
30use zrx_scheduler::step::Result;
31use zrx_scheduler::Scope;
32
33use crate::stream::function::arguments::{
34    ForId, ForIdSplat, ForIdValue, ForScope, ForScopeSplat, ForScopeValue,
35    ForSplat, ForValue,
36};
37use crate::stream::function::catch;
38
39// ----------------------------------------------------------------------------
40// Traits
41// ----------------------------------------------------------------------------
42
43/// Flat map function.
44pub trait FlatMapFn<A, I, T, U, R>: Send + 'static
45where
46    R: IntoIterator<Item = (I, U)>,
47{
48    /// Executes the flat map function.
49    ///
50    /// # Errors
51    ///
52    /// This method returns an error if the function fails to execute.
53    fn execute(&self, scope: &Scope<I>, value: T) -> Result<R>;
54}
55
56// ----------------------------------------------------------------------------
57// Blanket implementations
58// ----------------------------------------------------------------------------
59
60impl<F, I, T, U, R> FlatMapFn<ForScope, I, T, U, R> for F
61where
62    F: Fn(&Scope<I>) -> Result<R> + Send + 'static,
63    R: IntoIterator<Item = (I, U)>,
64    I: Display,
65{
66    #[cfg_attr(
67        feature = "tracing",
68        tracing::instrument(level = "debug", skip_all, fields(scope = %scope))
69    )]
70    #[inline]
71    fn execute(&self, scope: &Scope<I>, _: T) -> Result<R> {
72        catch(|| self(scope))
73    }
74}
75
76impl<F, I, T, U, R> FlatMapFn<ForId, I, T, U, R> for F
77where
78    F: Fn(&I) -> Result<R> + Send + 'static,
79    R: IntoIterator<Item = (I, U)>,
80    I: Display,
81{
82    #[cfg_attr(
83        feature = "tracing",
84        tracing::instrument(level = "debug", skip_all, fields(scope = %scope))
85    )]
86    #[inline]
87    fn execute(&self, scope: &Scope<I>, _: T) -> Result<R> {
88        catch(|| self(scope.try_as_id()?))
89    }
90}
91
92impl<F, I, T, U, R> FlatMapFn<ForValue, I, T, U, R> for F
93where
94    F: Fn(T) -> Result<R> + Send + 'static,
95    R: IntoIterator<Item = (I, U)>,
96    I: Display,
97{
98    #[cfg_attr(
99        feature = "tracing",
100        tracing::instrument(level = "debug", skip_all, fields(scope = %scope))
101    )]
102    #[inline]
103    fn execute(&self, scope: &Scope<I>, value: T) -> Result<R> {
104        catch(|| self(value))
105    }
106}
107
108impl<F, I, T, U, R> FlatMapFn<ForScopeValue, I, T, U, R> for F
109where
110    F: Fn(&Scope<I>, T) -> Result<R> + Send + 'static,
111    R: IntoIterator<Item = (I, U)>,
112    I: Display,
113{
114    #[cfg_attr(
115        feature = "tracing",
116        tracing::instrument(level = "debug", skip_all, fields(scope = %scope))
117    )]
118    #[inline]
119    fn execute(&self, scope: &Scope<I>, value: T) -> Result<R> {
120        catch(|| self(scope, value))
121    }
122}
123
124impl<F, I, T, U, R> FlatMapFn<ForIdValue, I, T, U, R> for F
125where
126    F: Fn(&I, T) -> Result<R> + Send + 'static,
127    R: IntoIterator<Item = (I, U)>,
128    I: Display,
129{
130    #[cfg_attr(
131        feature = "tracing",
132        tracing::instrument(level = "debug", skip_all, fields(scope = %scope))
133    )]
134    #[inline]
135    fn execute(&self, scope: &Scope<I>, value: T) -> Result<R> {
136        catch(|| self(scope.try_as_id()?, value))
137    }
138}
139
140// ----------------------------------------------------------------------------
141// Macros
142// ----------------------------------------------------------------------------
143
144/// Implements flat map function trait for splat arguments.
145macro_rules! impl_flat_map_fn_for_splat {
146    ($($T:ident),+) => {
147        impl<F, I, $($T,)+ U, R> FlatMapFn<ForSplat, I, ($($T,)+), U, R>
148            for F
149        where
150            F: Fn($($T),+) -> Result<R> + Send + 'static,
151            R: IntoIterator<Item = (I, U)>,
152            I: Display,
153        {
154            #[cfg_attr(
155                feature = "tracing",
156                tracing::instrument(
157                    level = "debug", skip_all, fields(scope = %scope)
158                )
159            )]
160            #[inline]
161            fn execute(
162                &self, scope: &Scope<I>, value: ($($T,)+)
163            ) -> Result<R> {
164                #[allow(non_snake_case)]
165                let ($($T,)+) = value;
166                catch(|| self($($T),+))
167            }
168        }
169    };
170}
171
172/// Implements flat map function trait for scope and splat arguments.
173macro_rules! impl_flat_map_fn_for_scope_splat {
174    ($($T:ident),+) => {
175        impl<F, I, $($T,)+ U, R> FlatMapFn<ForScopeSplat, I, ($($T,)+), U, R>
176            for F
177        where
178            F: Fn(&Scope<I>, $($T),+) -> Result<R> + Send + 'static,
179            R: IntoIterator<Item = (I, U)>,
180            I: Display,
181        {
182            #[cfg_attr(
183                feature = "tracing",
184                tracing::instrument(
185                    level = "debug", skip_all, fields(scope = %scope)
186                )
187            )]
188            #[inline]
189            fn execute(
190                &self, scope: &Scope<I>, value: ($($T,)+)
191            ) -> Result<R> {
192                #[allow(non_snake_case)]
193                let ($($T,)+) = value;
194                catch(|| self(scope, $($T),+))
195            }
196        }
197    };
198}
199
200/// Implements flat map function trait for identifier and splat arguments.
201macro_rules! impl_flat_map_fn_for_id_splat {
202    ($($T:ident),+) => {
203        impl<F, I, $($T,)+ U, R> FlatMapFn<ForIdSplat, I, ($($T,)+), U, R>
204            for F
205        where
206            F: Fn(&I, $($T),+) -> Result<R> + Send + 'static,
207            R: IntoIterator<Item = (I, U)>,
208            I: Display,
209        {
210            #[cfg_attr(
211                feature = "tracing",
212                tracing::instrument(
213                    level = "debug", skip_all, fields(scope = %scope)
214                )
215            )]
216            #[inline]
217            fn execute(
218                &self, scope: &Scope<I>, value: ($($T,)+)
219            ) -> Result<R> {
220                #[allow(non_snake_case)]
221                let ($($T,)+) = value;
222                catch(|| self(scope.try_as_id()?, $($T),+))
223            }
224        }
225    };
226}
227
228/// Implements flat map function traits.
229macro_rules! impl_flat_map_fn {
230    ($($T:ident),+) => {
231        impl_flat_map_fn_for_splat!($($T),+);
232        impl_flat_map_fn_for_scope_splat!($($T),+);
233        impl_flat_map_fn_for_id_splat!($($T),+);
234    };
235}
236
237// ----------------------------------------------------------------------------
238
239impl_flat_map_fn!(T1);
240impl_flat_map_fn!(T1, T2);
241impl_flat_map_fn!(T1, T2, T3);
242impl_flat_map_fn!(T1, T2, T3, T4);
243impl_flat_map_fn!(T1, T2, T3, T4, T5);
244impl_flat_map_fn!(T1, T2, T3, T4, T5, T6);
245impl_flat_map_fn!(T1, T2, T3, T4, T5, T6, T7);
246impl_flat_map_fn!(T1, T2, T3, T4, T5, T6, T7, T8);