devela/code/util/
capture.rs

1// devela::code::util::capture
2//
3//! capture tokens macro functionality
4//
5
6/// Captures the first token from a list of inputs.
7///
8/// Usage: `first!(<category> <first>, <tail>*, <optional_comma>?);`
9///
10/// # Example
11/// ```
12/// # use devela::capture_first;
13/// assert_eq![5, capture_first!(expr 5, 6, 7)];
14/// assert_eq![42, capture_first!(literal 42, "hello", true)];
15/// ```
16#[macro_export]
17#[rustfmt::skip]
18#[cfg_attr(cargo_primary_package, doc(hidden))]
19macro_rules! capture_first {
20    (block $first:block $(, $tail:block)* $(,)?) => { $first };
21    (expr $first:expr $(, $tail:expr)* $(,)?) => { $first };
22    (ident $first:ident $(, $tail:ident)* $(,)?) => { $first };
23    (literal $first:literal $(, $tail:literal)* $(,)?) => { $first };
24    (meta $first:meta $(, $tail:meta)* $(,)?) => { $first };
25    (pat $first:pat $(, $tail:pat)* $(,)?) => { $first };
26    (path $first:path $(, $tail:path)* $(,)?) => { $first };
27    (ty $first:ty $(, $tail:ty)* $(,)?) => { $first };
28    (tt $first:tt $(, $tail:tt)* $(,)?) => { $first };
29}
30#[doc(inline)]
31pub use capture_first;
32
33/// Captures all the tokens except the first one, as a tuple.
34#[macro_export]
35#[rustfmt::skip]
36#[cfg_attr(cargo_primary_package, doc(hidden))]
37macro_rules! capture_tail_tuple {
38    (block $first:block, $($tail:block),* $(,)?) => { ($($tail),*) };
39    (expr $first:expr, $($tail:expr),* $(,)?) => { ($($tail),*) };
40    (ident $first:ident, $($tail:ident),* $(,)?) => { ($($tail),*) };
41    (literal $first:literal, $($tail:literal),* $(,)?) => { ($($tail),*) };
42    (meta $first:meta, $($tail:meta),* $(,)?) => { ($($tail),*) };
43    (pat $first:pat, $($tail:pat),* $(,)?) => { ($($tail),*) };
44    (path $first:path, $($tail:path),* $(,)?) => { ($($tail),*) };
45    (tt $first:tt, $($tail:tt),* $(,)?) => { ($($tail),*) };
46    (ty $first:ty, $($tail:ty),* $(,)?) => { ($($tail),*) };
47
48    // Handles the case where there is no tail (optional trailing comma)
49    ($cat:tt $first:tt) => { () };
50}
51#[doc(inline)]
52pub use capture_tail_tuple;
53
54// /// Captures all the tokens except the first one.
55// #[macro_export]
56// #[rustfmt::skip]
57// macro_rules! capture_tail {
58//     (block $first:block, $($tail:block),*) => { $($tail),* };
59//     (expr $first:expr, $($tail:expr),*) => { $($tail),* };
60//     (ident $first:ident, $($tail:ident),*) => { $($tail),* };
61//     (literal $first:literal, $($tail:literal),*) => { $($tail),* };
62//     (meta $first:meta, $($tail:meta),*) => { $($tail),* };
63//     (pat $first:pat, $($tail:pat),*) => { $($tail),* };
64//     (path $first:path, $($tail:path),*) => { $($tail),* };
65//     (tt $first:tt, $($tail:tt),*) => { $($tail),* };
66//     (ty $first:ty, $($tail:ty),*) => { $($tail),* };
67// }
68// pub use capture_tail;
69
70/// Captures the last token from a list of inputs.
71#[doc(hidden)]
72#[macro_export]
73#[rustfmt::skip]
74macro_rules! _capture_last {
75    // Base case: when there is only one item left, return it
76    (block $first:block) => { $first };
77    (expr $first:expr) => { $first };
78    (ident $first:ident) => { $first };
79    (literal $first:literal) => { $first };
80    (meta $first:meta) => { $first };
81    (pat $first:pat) => { $first };
82    (path $first:path) => { $first };
83    (tt $first:tt) => { $first };
84    (ty $first:ty) => { $first };
85
86    // Recursive case: discard the first item and continue with the rest
87    (block $first:block, $($tail:block),* $(,)?) => {
88        $crate::capture_last!(block $($tail),*) };
89    (expr $first:expr, $($tail:expr),* $(,)?) => {
90        $crate::capture_last!(expr $($tail),*) };
91    (ident $first:ident, $($tail:ident),* $(,)?) => {
92        $crate::capture_last!(ident $($tail),*) };
93    (literal $first:literal, $($tail:literal),* $(,)?) => {
94        $crate::capture_last!(literal $($tail),*) };
95    (meta $first:meta, $($tail:meta),* $(,)?) => {
96        $crate::capture_last!(meta $($tail),*) };
97    (pat $first:pat, $($tail:pat),* $(,)?) => {
98        $crate::capture_last!(pat $($tail),*) };
99    (path $first:path, $($tail:path),* $(,)?) => {
100        $crate::capture_last!(path $($tail),*) };
101    (tt $first:tt, $($tail:tt),* $(,)?) => {
102        $crate::capture_last!(tt $($tail),*) };
103    (ty $first:ty, $($tail:ty),* $(,)?) => {
104        $crate::capture_last!(ty $($tail),*) };
105}
106#[doc(inline)]
107pub use _capture_last as capture_last;
108
109#[cfg(test)]
110mod tests {
111    pub use super::*;
112
113    #[test]
114    fn capture_first() {
115        assert_eq![{ 2 }, capture_first!(block { 1 + 1 }, { loop {} }, { 3 * 2 })];
116
117        assert_eq![5, capture_first!(expr 5, "a", if a == 3 {}, 4 + 3)];
118
119        let my_var = ();
120        assert_eq![my_var, capture_first!(ident my_var, another_var)];
121
122        assert_eq!["32", capture_first!(literal "32", 100, '€')];
123
124        assert_eq!['%', capture_first!(tt '%', "$", 3, {}, something)];
125
126        let captured = 1 as capture_first!(ty i32, u64, bool, f32);
127        assert_eq![1_i32, captured];
128
129        /* single item */
130
131        assert_eq!(5, capture_first!(expr 5));
132        assert_eq!(my_var, capture_first!(ident my_var));
133        assert_eq!('€', capture_first!(literal '€'));
134    }
135
136    #[test]
137    fn capture_tail_tuple() {
138        assert_eq![(6, "a"), capture_tail_tuple!(expr if a == 3 {}, 6, "a")];
139
140        let (my_var, another_var) = ((), ());
141        assert_eq![another_var, capture_tail_tuple!(ident my_var, another_var)];
142
143        assert_eq![(my_var, another_var), capture_tail_tuple!(tt { token }, my_var, another_var)];
144
145        struct TestStruct<T>(T);
146        let _instance: TestStruct<capture_tail_tuple!(ty i32, f32, bool)> = TestStruct((2.5, true));
147    }
148
149    #[test]
150    fn capture_last() {
151        assert_eq![{ 6 }, capture_last!(block { 1 + 1 }, { loop {} }, { 3 * 2 })];
152
153        assert_eq![7, capture_last!(expr 5, "a", if a == 3 {}, 4 + 3)];
154
155        let (my_var, another_var) = ((), ());
156        assert_eq![another_var, capture_last!(ident my_var, another_var)];
157
158        assert_eq!['€', capture_last!(literal "32", 100, '€')];
159
160        let something = ();
161        assert_eq![something, capture_last!(tt '%', "$", 3, {}, something)];
162
163        let captured = 1.3 as capture_last!(ty i32, u64, bool, f32);
164        assert_eq![1.3_f32, captured];
165
166        /* single item */
167
168        assert_eq!(5, capture_last!(expr 5));
169        assert_eq!(my_var, capture_last!(ident my_var));
170        assert_eq!('€', capture_last!(literal '€'));
171    }
172}