zrx_stream/stream/function/traits/
select.rs

1// Copyright (c) Zensical LLC <https://zensical.org>
2
3// SPDX-License-Identifier: MIT
4// Third-party contributions licensed under CLA
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//! Select function.
27
28use std::fmt::Display;
29use zrx_scheduler::action::report::IntoReport;
30use zrx_scheduler::action::Result;
31use zrx_scheduler::Value;
32
33use crate::stream::function::adapter::{WithId, WithSplat};
34use crate::stream::function::{catch, Splat};
35
36// ----------------------------------------------------------------------------
37// Traits
38// ----------------------------------------------------------------------------
39
40/// Select function.
41///
42/// This trait defines a function that can be used to select data from an item,
43/// e.g., to extract a key to group items or to compute a duration for a timer.
44///
45/// There's a range of different implementations of this trait, allowing you to
46/// use a variety of function shapes, including support for [`Splat`], as well
47/// as support for the [`WithId`] and [`WithSplat`] adapters. Furthermore, the
48/// trait can be implemented for custom types to add new behaviors. Note that
49/// all implementations also allow to return a [`Report`][], which makes it
50/// possible to return diagnostics from the function execution.
51///
52/// The `'static` lifetimes is mandatory as closures must be moved into actions,
53/// so requiring it here allows us to reduce the verbosity of trait bounds.
54///
55/// [`Report`]: zrx_scheduler::action::report::Report
56///
57/// # Examples
58///
59/// Group by odd/even:
60///
61/// ```
62/// # use std::error::Error;
63/// # fn main() -> Result<(), Box<dyn Error>> {
64/// use zrx_stream::function::SelectFn;
65///
66/// // Define and execute function
67/// let f = |&n: &i32| n & 1;
68/// f.execute(&"id", &42)?;
69/// # Ok(())
70/// # }
71/// ```
72pub trait SelectFn<I, T, S>: Send + 'static
73where
74    T: ?Sized,
75{
76    /// Executes the select function.
77    ///
78    /// # Errors
79    ///
80    /// This method returns an error if the function fails to execute.
81    fn execute(&self, id: &I, data: &T) -> Result<S>;
82}
83
84// ----------------------------------------------------------------------------
85// Blanket implementations
86// ----------------------------------------------------------------------------
87
88impl<F, R, I, T, S> SelectFn<I, T, S> for F
89where
90    F: Fn(&T) -> R + Send + 'static,
91    R: IntoReport<S>,
92    I: Display,
93    T: Value + ?Sized,
94{
95    #[cfg_attr(
96        feature = "tracing",
97        tracing::instrument(level = "debug", skip_all, fields(id = %id))
98    )]
99    #[inline]
100    fn execute(&self, id: &I, data: &T) -> Result<S> {
101        catch(|| self(data).into_report())
102    }
103}
104
105impl<F, R, I, T, S> SelectFn<I, T, S> for WithId<F>
106where
107    F: Fn(&I, &T) -> R + Send + 'static,
108    R: IntoReport<S>,
109    I: Display,
110    T: Value + ?Sized,
111{
112    #[cfg_attr(
113        feature = "tracing",
114        tracing::instrument(level = "debug", skip_all, fields(id = %id))
115    )]
116    #[inline]
117    fn execute(&self, id: &I, data: &T) -> Result<S> {
118        catch(|| self(id, data).into_report())
119    }
120}
121
122impl<F, I, T, S> SelectFn<I, T, S> for WithSplat<F>
123where
124    F: SelectFn<I, Splat<T>, S>,
125{
126    #[inline]
127    fn execute(&self, id: &I, data: &T) -> Result<S> {
128        F::execute(self, id, Splat::from_ref(data))
129    }
130}
131
132// ----------------------------------------------------------------------------
133// Macros
134// ----------------------------------------------------------------------------
135
136/// Implements select function trait for splat arguments.
137macro_rules! impl_select_fn_for_splat {
138    ($($T:ident),+) => {
139        impl<F, R, I, $($T,)+ S> SelectFn<I, Splat<($($T,)+)>, S> for F
140        where
141            F: Fn($(&$T),+) -> R + Send + 'static,
142            R: IntoReport<S>,
143            I: Display,
144        {
145            #[cfg_attr(
146                feature = "tracing",
147                tracing::instrument(level = "debug", skip_all, fields(id = %id))
148            )]
149            #[inline]
150            fn execute(
151                &self, id: &I, data: &Splat<($($T,)+)>
152            ) -> Result<S> {
153                #[allow(non_snake_case)]
154                let ($($T,)+) = data.inner();
155                catch(|| self($($T),+).into_report())
156            }
157        }
158    };
159}
160
161// ----------------------------------------------------------------------------
162
163impl_select_fn_for_splat!(T1);
164impl_select_fn_for_splat!(T1, T2);
165impl_select_fn_for_splat!(T1, T2, T3);
166impl_select_fn_for_splat!(T1, T2, T3, T4);
167impl_select_fn_for_splat!(T1, T2, T3, T4, T5);
168impl_select_fn_for_splat!(T1, T2, T3, T4, T5, T6);
169impl_select_fn_for_splat!(T1, T2, T3, T4, T5, T6, T7);
170impl_select_fn_for_splat!(T1, T2, T3, T4, T5, T6, T7, T8);