Skip to main content

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