zrx_stream/stream/function/traits/
inspect.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//! Inspect 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/// Inspect function.
41///
42/// This trait defines a function that can be used to inspect borrowed data in
43/// a stream without modifying it, and is not expected to return new data.
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/// Inspect data:
60///
61/// ```
62/// # use std::error::Error;
63/// # fn main() -> Result<(), Box<dyn Error>> {
64/// use zrx_stream::function::InspectFn;
65///
66/// // Define and execute function
67/// let f = |&n: &i32| println!("{n}");
68/// f.execute(&"id", &42)?;
69/// # Ok(())
70/// # }
71/// ```
72///
73/// Inspect data with splat argument:
74///
75/// ```
76/// # use std::error::Error;
77/// # fn main() -> Result<(), Box<dyn Error>> {
78/// use zrx_stream::function::{InspectFn, Splat};
79///
80/// // Define and execute function
81/// let f = |&a: &i32, &b: &i32| println!("{a} < {b}");
82/// f.execute(&"id", Splat::from_ref(&(1, 2)))?;
83/// # Ok(())
84/// # }
85/// ```
86pub trait InspectFn<I, T>: Send + 'static
87where
88    T: ?Sized,
89{
90    /// Executes the inspect function.
91    ///
92    /// # Errors
93    ///
94    /// This method returns an error if the function fails to execute.
95    fn execute(&self, id: &I, data: &T) -> Result;
96}
97
98// ----------------------------------------------------------------------------
99// Blanket implementations
100// ----------------------------------------------------------------------------
101
102impl<F, R, I, T> InspectFn<I, T> for F
103where
104    F: Fn(&T) -> R + Send + 'static,
105    R: IntoReport,
106    I: Display,
107    T: Value + ?Sized,
108{
109    #[cfg_attr(
110        feature = "tracing",
111        tracing::instrument(level = "debug", skip_all, fields(id = %id))
112    )]
113    #[inline]
114    fn execute(&self, id: &I, data: &T) -> Result {
115        catch(|| self(data).into_report())
116    }
117}
118
119impl<F, R, I, T> InspectFn<I, T> for WithId<F>
120where
121    F: Fn(&I, &T) -> R + Send + 'static,
122    R: IntoReport,
123    I: Display,
124    T: Value + ?Sized,
125{
126    #[cfg_attr(
127        feature = "tracing",
128        tracing::instrument(level = "debug", skip_all, fields(id = %id))
129    )]
130    #[inline]
131    fn execute(&self, id: &I, data: &T) -> Result {
132        catch(|| self(id, data).into_report())
133    }
134}
135
136impl<F, I, T> InspectFn<I, T> for WithSplat<F>
137where
138    F: InspectFn<I, Splat<T>>,
139{
140    #[inline]
141    fn execute(&self, id: &I, data: &T) -> Result {
142        F::execute(self, id, Splat::from_ref(data))
143    }
144}
145
146// ----------------------------------------------------------------------------
147// Macros
148// ----------------------------------------------------------------------------
149
150/// Implements inspect function trait for splat arguments.
151macro_rules! impl_inspect_fn_for_splat {
152    ($($T:ident),+) => {
153        impl<F, R, I, $($T,)+> InspectFn<I, Splat<($($T,)+)>> for F
154        where
155            F: Fn($(&$T),+) -> R + Send + 'static,
156            R: IntoReport,
157            I: Display,
158        {
159            #[cfg_attr(
160                feature = "tracing",
161                tracing::instrument(level = "debug", skip_all, fields(id = %id))
162            )]
163            #[inline]
164            fn execute(
165                &self, id: &I, data: &Splat<($($T,)+)>
166            ) -> Result {
167                #[allow(non_snake_case)]
168                let ($($T,)+) = data.inner();
169                catch(|| self($($T),+).into_report())
170            }
171        }
172    };
173}
174
175// ----------------------------------------------------------------------------
176
177impl_inspect_fn_for_splat!(T1);
178impl_inspect_fn_for_splat!(T1, T2);
179impl_inspect_fn_for_splat!(T1, T2, T3);
180impl_inspect_fn_for_splat!(T1, T2, T3, T4);
181impl_inspect_fn_for_splat!(T1, T2, T3, T4, T5);
182impl_inspect_fn_for_splat!(T1, T2, T3, T4, T5, T6);
183impl_inspect_fn_for_splat!(T1, T2, T3, T4, T5, T6, T7);
184impl_inspect_fn_for_splat!(T1, T2, T3, T4, T5, T6, T7, T8);