zrx_stream/stream/function/argument/splat.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//! Splat argument.
27
28use std::ptr;
29
30// ----------------------------------------------------------------------------
31// Structs
32// ----------------------------------------------------------------------------
33
34/// Splat argument.
35///
36/// This is a marker trait that is used as a wrapper around tuples of arguments
37/// allowing to invoke a function with multiple arguments as if it were a single
38/// argument. Its purpose is to make writing operators with multiple arguments
39/// more ergonomic, which is particularly useful for joins.
40///
41/// All function traits implement variations of [`Splat`] for tuples of up to 8
42/// elements, which is likely to be sufficient for most use cases. Please note
43/// that most of the time, this type is only ever needed for creating flexible
44/// operator functions, so you'll rarely want to use it directly. Additionally,
45/// note that [`Splat`] itself doesn't impose any trait bounds in order to not
46/// require even more trait bounds in the signature of implementors. Moreover,
47/// it doesn't implement [`Value`][], meaning it can only be used temporarily
48/// inside operators, but never returned.
49///
50/// [`Value`]: zrx_scheduler::Value
51///
52/// # Examples
53///
54/// ```
55/// use zrx_stream::function::Splat;
56///
57/// // Create splat from tuple
58/// let splat = Splat::from((1, 2));
59/// ```
60#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
61#[repr(transparent)] // do not remove
62pub struct Splat<T> {
63 /// Inner arguments.
64 inner: T,
65}
66
67// ----------------------------------------------------------------------------
68// Implementations
69// ----------------------------------------------------------------------------
70
71impl<T> Splat<T> {
72 /// Returns the inner tuple of arguments.
73 ///
74 /// # Examples
75 ///
76 /// ```
77 /// use zrx_stream::function::Splat;
78 ///
79 /// // Create splat from tuple
80 /// let splat = Splat::from((1, 2));
81 /// assert_eq!(splat.inner(), &(1, 2));
82 /// ```
83 #[inline]
84 pub fn inner(&self) -> &T {
85 &self.inner
86 }
87
88 /// Returns the inner tuple of arguments, consuming the splat.
89 ///
90 /// # Examples
91 ///
92 /// ```
93 /// use zrx_stream::function::Splat;
94 ///
95 /// // Create splat from tuple
96 /// let splat = Splat::from((1, 2));
97 /// assert_eq!(splat.into_inner(), (1, 2));
98 /// ```
99 #[inline]
100 pub fn into_inner(self) -> T {
101 self.inner
102 }
103}
104
105impl<T> Splat<T> {
106 /// Creates a splat argument from a reference to a tuple.
107 ///
108 /// This method converts `&T` to `&Splat<T>`, which allows us to use splat
109 /// arguments in any function that takes a reference to a value `T` without
110 /// cloning the value. While the conversion mandates the use of `unsafe`,
111 /// it's safe due to identical memory layout guarantees.
112 ///
113 /// # Examples
114 ///
115 /// ```
116 /// use zrx_stream::function::Splat;
117 ///
118 /// // Create splat from tuple reference
119 /// let splat = Splat::from_ref(&(1, 2));
120 /// ```
121 #[inline]
122 pub fn from_ref(inner: &T) -> &Self {
123 // SAFETY: Safe, because `repr(transparent)` guarantees that the outer
124 // type always has the exact same memory layout as the inner type
125 unsafe { &*ptr::from_ref::<T>(inner).cast::<Splat<T>>() }
126 }
127}
128
129// ----------------------------------------------------------------------------
130
131impl<T> From<T> for Splat<T> {
132 /// Creates a splat from a tuple of arguments.
133 ///
134 /// # Examples
135 ///
136 /// ```
137 /// use zrx_stream::function::Splat;
138 ///
139 /// // Create splat from tuple
140 /// let splat = Splat::from((1, 2));
141 /// ```
142 #[inline]
143 fn from(inner: T) -> Self {
144 Self { inner }
145 }
146}