magic_args/
lib.rs

1//! "Magic" declarative-style function arguments.
2//!
3//! ```
4//! # use magic_args::{apply, MagicArgs};
5//! fn f0() { /* ... */ }
6//! fn f1(x: i32) { /* ... */ }
7//! fn f2(x: i32, z: usize) { /* ... */ }
8//! async fn f3(y: &'static str) { /* ... */ }
9//! async fn f4(y: &'static str, x: i32, z: usize) { /* ... */ }
10//!
11//! #[derive(MagicArgs)]
12//! struct Args(i32, &'static str, usize);
13//!
14//! let args = Args(-42, "🦀", 42);
15//!
16//! let _ = apply(f0, &args);
17//! let _ = apply(f1, &args);
18//! let _ = apply(f2, &args);
19//! let _ = apply(f3, &args);
20//! let _ = apply(f4, &args);
21//! ```
22//!
23//! The original idea for this crate comes from [axum's route handlers], which
24//! do this exact thing with their arguments.
25//!
26//! # Quick start
27//!
28//! 1. You define a "set of arguments".
29//!
30//! ```no_run
31//! # use magic_args::MagicArgs;
32//! #[derive(MagicArgs)]
33//! pub struct MyArgs(&'static str, usize /*, ... */);
34//!
35//! let args = MyArgs("Hello, world!", 42);
36//! ```
37//!
38//! 2. You use [`apply`] to call a function of your choosing.
39//!
40//! ```no_run
41//! # use magic_args::{apply, MagicArgs};
42//! # #[derive(MagicArgs)]
43//! # pub struct MyArgs(&'static str, usize /*, ... */);
44//! # let args = MyArgs("Hello, world!", 42);
45//! fn my_func(msg: &'static str) { /* ... */ }
46//!
47//! let result = apply(my_func, args);
48//! ```
49//!
50//! 3. Profit? 💰
51//!
52//!
53//!
54//! <sub>*this product contains limitations which are not featured above</sub>
55//!
56//! # How it works
57//!
58//! All of the "magic" of this crate is done via [`Callable`] and [`Args`].
59//! Everything else is purely cosmetic and for convinience.
60//!
61//! This documentation will use the term "argument set" to refer to any type $T$
62//! which implements `Args<U>` for some $U$.
63//!
64//! [`Callable`] is defined in terms of $A$ and $T$. $A$ is the argument set
65//! which contains all arguments available to the function. $T$ is an ordered
66//! tuple containing the arguments the function expects to be given.
67//! [`Callable`] is implemented automatically for all `FnOnce(T0, T1, ...) -> O`
68//! (up to 32 arguments). The job of the [`Callable`] trait is to pick out what
69//! arguments the function wants from a given $A$. For this to be possible, $A$
70//! must be bounded such that $T \subseteq A$. In other words; to be able to
71//! even call the function with the arguments it expects, we must _have_ the
72//! arguments in the first place. For example, let $T = (T_0, T_1, T_2)$. It
73//! must be that $T_0, T_1, T_2 \in A$ or, in code,
74//! `A: Args<T0> + Args<T1> + Args<T2>`. Picking the correct argument from the
75//! argument set $A$ for each argument of the function is done with
76//! [`Args::get`] and the compiler's type inference.
77//!
78//! # Limitations
79//!
80//! This whole crates operates on the type-level of arguments. For this reason,
81//! it is impossible to have a function which accepts 2 different instances of
82//! the same type. To see why this is, consider the following function:
83//!
84//! ```no_run
85//! fn f(x: i32, _y: i32) -> i32 { x }
86//! ```
87//!
88//! Given its signature $f: (i32, i32) \to i32$ and an arguments set
89//! $A = \\{ 42, -42 \\}$. It is impossible to know the correct order of passing
90//! these values to $f$ such that it returns 42, or -42 for that matter.
91//! This means all values in the argument set must be of a different type. In
92//! cases like above when a function has 2 arguments of the same type, the
93//! value of the matching type in the argument set is passed twice. This incurs
94//! a [`Clone::clone`] call.
95//!
96//! [axum's route handlers]: https://docs.rs/axum/latest/axum/handler/index.html
97
98#![cfg_attr(docsrs, feature(doc_auto_cfg))]
99#![forbid(unsafe_code)]
100#![no_std]
101extern crate self as magic_args;
102
103#[cfg(feature = "derive")]
104/// A derive macro to help you create argument sets.
105///
106/// This macro can be used only on `struct` items.
107///
108/// Generate the appropriate [`Args`] implementations for the annotated type
109/// allowing it to be used with [`Callable`].
110///
111/// # Field attributes
112///
113/// ## `skip`
114///
115/// * **Syntax:** `#[magic_args(skip)]`
116///
117/// Do not expose this field as an available argument. The resulting argument
118/// set will act as if this field does not exist.
119///
120/// ```compile_fail,E0277
121/// # use magic_args::{apply, MagicArgs};
122/// #[derive(MagicArgs)]
123/// struct Args(i32, usize, #[magic_args(skip)] &'static str);
124///
125/// fn f(_x: usize, _y: &'static str) {}
126///
127/// apply(f, Args(42, 42, "Hello, world!"));
128/// ```
129#[doc(inline)]
130pub use magic_args_derive::MagicArgs;
131
132///////////////////////////////////////////////////////////////////////////////
133
134/// A "set of arguments" that contains `T`.
135pub trait Args<T> {
136    /// Get `T` from the set of arguments.
137    ///
138    /// The signature of this method usually means that some copying/cloning
139    /// has to happen. To see why it is designed like this, please refer to the
140    /// [crate-level documentation](crate).
141    fn get(&self) -> T;
142}
143
144impl<T, U> Args<T> for &U
145where
146    U: Args<T>,
147{
148    fn get(&self) -> T {
149        U::get(*self)
150    }
151}
152
153///////////////////////////////////////////////////////////////////////////////
154
155/// A trait to describe any kind of type that can be called.
156///
157/// This trait and the [`Args`] trait are the foundation of the crate. It
158/// provides [`Callable::call`] which is how [`apply`] (and friends) work.
159pub trait Callable<A, T> {
160    type Output;
161
162    fn call(self, args: A) -> Self::Output;
163}
164
165macro_rules! impl_callable_fnonce {
166    ($($t:ident),*) => {
167        impl<F, O, A, $($t,)*> Callable<A, ($($t,)*)> for F
168        where
169            F: FnOnce($($t),*) -> O,
170            $(A: Args<$t>,)*
171        {
172            type Output = O;
173
174            fn call(self, _args: A) -> Self::Output {
175                (self)($(<A as Args<$t>>::get(&_args)),*)
176            }
177        }
178    };
179}
180
181impl_callable_fnonce! {}
182impl_callable_fnonce! { T0 }
183impl_callable_fnonce! { T0, T1 }
184impl_callable_fnonce! { T0, T1, T2 }
185impl_callable_fnonce! { T0, T1, T2, T3 }
186impl_callable_fnonce! { T0, T1, T2, T3, T4 }
187impl_callable_fnonce! { T0, T1, T2, T3, T4, T5 }
188impl_callable_fnonce! { T0, T1, T2, T3, T4, T5, T6 }
189impl_callable_fnonce! { T0, T1, T2, T3, T4, T5, T6, T7 }
190impl_callable_fnonce! { T0, T1, T2, T3, T4, T5, T6, T7, T8 }
191impl_callable_fnonce! { T0, T1, T2, T3, T4, T5, T6, T7, T8, T9 }
192impl_callable_fnonce! { T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10 }
193impl_callable_fnonce! { T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11 }
194impl_callable_fnonce! { T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12 }
195impl_callable_fnonce! { T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13 }
196impl_callable_fnonce! { T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14 }
197impl_callable_fnonce! { T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15 }
198impl_callable_fnonce! { T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16 }
199impl_callable_fnonce! { T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17 }
200impl_callable_fnonce! { T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18 }
201impl_callable_fnonce! { T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19 }
202impl_callable_fnonce! { T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20 }
203impl_callable_fnonce! { T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21 }
204impl_callable_fnonce! { T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22 }
205impl_callable_fnonce! { T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23 }
206impl_callable_fnonce! { T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24 }
207impl_callable_fnonce! { T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25 }
208impl_callable_fnonce! { T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26 }
209impl_callable_fnonce! { T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27 }
210impl_callable_fnonce! { T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28 }
211impl_callable_fnonce! { T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29 }
212impl_callable_fnonce! { T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30 }
213impl_callable_fnonce! { T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30, T31 }
214
215///////////////////////////////////////////////////////////////////////////////
216
217/// A convinience trait to provide the `args.apply(f)` syntax.
218pub trait MagicArgs {
219    /// Apply _f_ on `self`.
220    ///
221    /// Equivalent to: `apply(f, self)`.
222    ///
223    /// See: [`apply`].
224    fn apply<C, T>(self, f: C) -> C::Output
225    where
226        C: Callable<Self, T>,
227        Self: Sized;
228}
229
230impl<U> MagicArgs for U {
231    #[inline]
232    fn apply<C, T>(self, f: C) -> C::Output
233    where
234        C: Callable<Self, T>,
235        Self: Sized,
236    {
237        apply(f, self)
238    }
239}
240
241/// Apply _f_ on `args`.
242///
243/// Equivalent to: `f.call(args)`.
244///
245/// See: [`Callable::call`].
246///
247/// # Examples
248///
249/// ```
250/// # use magic_args::{apply, MagicArgs};
251/// #[derive(MagicArgs)]
252/// struct Args(String, i32);
253///
254/// fn f(x: i32) -> i32 { x + 1 }
255///
256/// let args = Args("🦀".into(), 41);
257/// let y = apply(f, args);
258/// assert_eq!(y, 42);
259/// ```
260///
261/// ---
262///
263/// ```
264/// # use magic_args::{apply, MagicArgs};
265/// #[derive(MagicArgs)]
266/// struct Args(String, i32);
267///
268/// let args = Args("🦀".into(), 41);
269///
270/// let fs: [fn(i32) -> i32; _] = [
271///     (|x| x + 1),
272///     (|x| x * 2),
273///     (|x| 3 * x + 1),
274///     (|x| -x),
275/// ];
276///
277/// let results = fs.map(|f| apply(f, &args));
278///
279/// assert_eq!(results, [
280///     42,
281///     82,
282///     124,
283///     -41
284/// ]);
285/// ```
286#[inline]
287pub fn apply<C, A, T>(f: C, args: A) -> C::Output
288where
289    C: Callable<A, T>,
290{
291    f.call(args)
292}
293
294#[cfg(test)]
295mod tests {
296    use super::*;
297
298    #[derive(Clone)]
299    struct A;
300    #[derive(Clone)]
301    struct B;
302    #[derive(Clone)]
303    struct C;
304    #[derive(Clone)]
305    struct D;
306
307    #[derive(MagicArgs)]
308    struct Args(A, B, C, D);
309
310    const fn args() -> Args {
311        Args(A, B, C, D)
312    }
313
314    #[test]
315    fn test_sync_functions() {
316        let args = args();
317
318        fn f0() {}
319        fn f1(_a: A) {}
320        fn f2(_c: C, _d: D) {}
321        fn f3(_a: A, _b: B, _c: C, _d: D) {}
322
323        apply(f0, &args);
324        apply(f1, &args);
325        apply(f2, &args);
326        apply(f3, &args);
327    }
328
329    #[test]
330    fn test_sync_closures() {
331        let args = args();
332
333        let f0 = || {};
334        let f1 = |_a: A| {};
335        let f2 = |_c: C, _d: D| {};
336        let f3 = |_a: A, _b: B, _c: C, _d: D| {};
337
338        apply(f0, &args);
339        apply(f1, &args);
340        apply(f2, &args);
341        apply(f3, &args);
342    }
343
344    #[test]
345    fn test_async_functions() {
346        let args = args();
347
348        async fn f0() {}
349        async fn f1(_a: A) {}
350        async fn f2(_c: C, _d: D) {}
351        async fn f3(_a: A, _b: B, _c: C, _d: D) {}
352
353        drop(apply(f0, &args));
354        drop(apply(f1, &args));
355        drop(apply(f2, &args));
356        drop(apply(f3, &args));
357    }
358
359    #[test]
360    fn test_async_closures() {
361        let args = args();
362
363        let f0 = async || {};
364        let f1 = async |_a: A| {};
365        let f2 = async |_c: C, _d: D| {};
366        let f3 = async |_a: A, _b: B, _c: C, _d: D| {};
367
368        drop(apply(f0, &args));
369        drop(apply(f1, &args));
370        drop(apply(f2, &args));
371        drop(apply(f3, &args));
372    }
373}