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}