tuplez_macros/
lib.rs

1use proc_macro::TokenStream;
2use proc_macro2::Span;
3use quote::quote;
4use syn::{parse_macro_input, parse_quote, Data, DeriveInput, Expr, Fields, Ident, LitInt};
5
6mod parser;
7
8use parser::*;
9
10#[proc_macro]
11pub fn tuple_traits_impl(input: TokenStream) -> TokenStream {
12    let max = parse_macro_input!(input as LitInt);
13    let max: usize = match max.base10_parse() {
14        Ok(v) => v,
15        Err(e) => return e.into_compile_error().into(),
16    };
17
18    let mut impls = vec![];
19    impls.push(quote! {
20        impl ::tuplez::ToPrimitive for ::tuplez::Unit {
21            type Primitive = ();
22            fn primitive(self)  -> Self::Primitive {}
23        }
24        impl From<()> for ::tuplez::Unit {
25            fn from(_: ()) -> Self {
26                ::tuplez::Unit
27            }
28        }
29        #[cfg(not(feature = "any_array"))]
30        impl<T> ::tuplez::ToArray<T> for ::tuplez::Unit {
31            type Array = [T; 0];
32            type Iter<'a> = std::array::IntoIter<&'a T, 0> where Self: 'a, T: 'a;
33            type IterMut<'a> = std::array::IntoIter<&'a mut T, 0> where Self: 'a, T: 'a;
34            fn to_array(self) -> Self::Array {
35                Default::default()
36            }
37            fn iter<'a>(&'a self) -> Self::Iter<'a>
38            where
39                Self: 'a,
40                T: 'a
41            {
42                self.as_ref().to_array().into_iter()
43            }
44            fn iter_mut<'a>(&'a mut self) -> Self::IterMut<'a>
45            where
46                Self: 'a,
47                T: 'a
48            {
49                self.as_mut().to_array().into_iter()
50            }
51        }
52        #[cfg(not(feature = "any_array"))]
53        impl<T> From<[T; 0]> for ::tuplez::Unit {
54            fn from(_: [T; 0]) -> Self {
55                ::tuplez::Unit
56            }
57        }
58    });
59
60    let mut tys = vec![];
61    let mut pats = vec![];
62    for i in 0..max {
63        tys.push(Ident::new(&format!("T{i}"), Span::call_site()));
64        pats.push(Ident::new(&format!("v{i}"), Span::call_site()));
65        let count = LitInt::new(&format!("{}", i + 1), Span::call_site());
66        impls.push(quote! {
67            impl<#(#tys),*> ::tuplez::ToPrimitive for ::tuplez::tuple_t!(#(#tys),*) {
68                type Primitive = (#(#tys),*,);
69                fn primitive(self)  -> Self::Primitive {
70                    let ::tuplez::tuple_pat!(#(#pats),*) = self;
71                    (#(#pats),*,)
72                }
73            }
74            impl<#(#tys),*> From<(#(#tys),*,)> for ::tuplez::tuple_t!(#(#tys),*) {
75                fn from((#(#pats),*,): (#(#tys),*,)) -> Self {
76                    ::tuplez::tuple!(#(#pats),*)
77                }
78            }
79            #[cfg(not(feature = "any_array"))]
80            impl<T> ToArray<T> for ::tuplez::tuple_t!(T; #count) {
81                type Array = [T; #count];
82                type Iter<'a> = std::array::IntoIter<&'a T, #count> where Self: 'a, T: 'a;
83                type IterMut<'a> = std::array::IntoIter<&'a mut T, #count> where Self: 'a, T: 'a;
84                fn to_array(self) -> Self::Array {
85                    let ::tuplez::tuple_pat!(#(#pats),*) = self;
86                    [#(#pats),*]
87                }
88                fn iter<'a>(&'a self) -> Self::Iter<'a>
89                where
90                    Self: 'a,
91                    T: 'a
92                {
93                    self.as_ref().to_array().into_iter()
94                }
95                fn iter_mut<'a>(&'a mut self) -> Self::IterMut<'a>
96                where
97                    Self: 'a,
98                    T: 'a
99                {
100                    self.as_mut().to_array().into_iter()
101                }
102            }
103            #[cfg(not(feature = "any_array"))]
104            impl<T> From<[T; #count]> for ::tuplez::tuple_t!(T; #count) {
105                fn from([#(#pats),*]: [T; #count]) -> Self {
106                    ::tuplez::tuple!(#(#pats),*)
107                }
108            }
109        });
110    }
111
112    quote! {#(#impls)*}.into()
113}
114
115#[proc_macro]
116pub fn tuple(input: TokenStream) -> TokenStream {
117    let ReExportTuplez {
118        path,
119        other: TupleGen(exprs),
120    } = parse_macro_input!(input as ReExportTuplez<TupleGen>);
121    let mut unpack = quote!( #path::Unit );
122    for expr in exprs.into_iter().rev() {
123        unpack = quote!( #path::Tuple( #expr, #unpack) );
124    }
125    unpack.into()
126}
127
128#[proc_macro]
129pub fn tuple_t(input: TokenStream) -> TokenStream {
130    let ReExportTuplez {
131        path,
132        other: TupleType(types),
133    } = parse_macro_input!(input as ReExportTuplez<TupleType>);
134    let mut unpack = quote!( #path::Unit );
135    for ty in types.into_iter().rev() {
136        unpack = quote!( #path::Tuple< #ty, #unpack> );
137    }
138    unpack.into()
139}
140
141#[proc_macro]
142pub fn tuple_pat(input: TokenStream) -> TokenStream {
143    let ReExportTuplez {
144        path,
145        other: TuplePat { mut pats, leader },
146    } = parse_macro_input!(input as ReExportTuplez<TuplePat>);
147    let mut unpack;
148    if pats.is_empty() {
149        unpack = quote!(_);
150    } else if leader {
151        unpack = quote!(..);
152        for pat in pats.into_iter().rev() {
153            unpack = quote!( #path::Tuple( #pat, #unpack) );
154        }
155    } else {
156        let last = pats.pop().unwrap();
157        unpack = quote!(#last);
158        for pat in pats.into_iter().rev() {
159            unpack = quote!( #path::Tuple( #pat, #unpack) );
160        }
161    }
162    unpack.into()
163}
164
165#[proc_macro]
166pub fn get(input: TokenStream) -> TokenStream {
167    let TupleIndex { tup, index } = parse_macro_input!(input as TupleIndex);
168    let field = quote!(. 1);
169    let fields = vec![field.clone(); index];
170    quote!( (#tup) #(#fields)* . 0).into()
171}
172
173#[proc_macro]
174pub fn take(input: TokenStream) -> TokenStream {
175    let ReExportTuplez {
176        path,
177        other: result,
178    } = parse_macro_input!(input as ReExportTuplez<TupleTake>);
179    match result {
180        TupleTake {
181            tup,
182            ext: IndexOrType::Index(index),
183        } => {
184            let tup = quote!( let tup_ = #tup );
185            let field = quote!(. 1);
186            let mut fields = vec![field.clone(); index];
187            let element = quote!( tup_ #(#fields)* . 0 );
188            let mut unpack = quote!( tup_ #(#fields)* . 1 );
189            for _ in 0..index {
190                _ = fields.pop();
191                unpack = quote!( #path::Tuple( tup_ #(#fields)* . 0, #unpack ) )
192            }
193            quote!({
194                #tup ;
195                ( #element, #unpack )
196            })
197        }
198        TupleTake {
199            tup,
200            ext: IndexOrType::Type(ty),
201        } => quote!({
202            use #path::TupleLike;
203            let (element_, remainder_): (#ty, _) = (#tup).take();
204            (element_, remainder_)
205        }),
206    }
207    .into()
208}
209
210#[proc_macro]
211pub fn pick(input: TokenStream) -> TokenStream {
212    let TuplePick { tup, indices } = parse_macro_input!(input as TuplePick);
213    let tup = quote!( let tup_ = #tup );
214    let max = *indices.iter().max().unwrap();
215    let unpicked_indices = (0..max).filter(|i| !indices.contains(i));
216    let field = quote!(. 1);
217    let picked = indices
218        .iter()
219        .map(|x| {
220            let fields = vec![field.clone(); *x];
221            quote!( tup_ #(#fields)* .0 )
222        })
223        .rfold(
224            quote!(tuplez::Unit),
225            |packed, token| quote!( tuplez::Tuple( #token, #packed ) ),
226        );
227    let tail = {
228        let fields = vec![field.clone(); max];
229        quote!( tup_ #(#fields)* .1 )
230    };
231    let unpicked = unpicked_indices
232        .map(|x| {
233            let fields = vec![field.clone(); x];
234            quote!( tup_ #(#fields)* .0 )
235        })
236        .rfold(
237            tail,
238            |packed, token| quote!( tuplez::Tuple( #token, #packed ) ),
239        );
240    quote!({
241        #tup ;
242        ( #picked, #unpicked )
243    })
244    .into()
245}
246
247#[proc_macro]
248pub fn split_at(input: TokenStream) -> TokenStream {
249    let ReExportTuplez {
250        path,
251        other: TupleIndex { tup, index },
252    } = parse_macro_input!(input as ReExportTuplez<TupleIndex>);
253    let tup = quote!( let tup_ = #tup );
254    let field = quote!(. 1);
255    let mut fields = vec![field.clone(); index];
256    let mut unpack = quote!( #path::Unit );
257    let other = quote!( tup_ #(#fields)* );
258    for _ in 0..index {
259        _ = fields.pop();
260        unpack = quote!( #path::Tuple( tup_ #(#fields)* . 0, #unpack ) );
261    }
262    quote!({
263        #tup ;
264        ( #unpack, #other )
265    })
266    .into()
267}
268
269#[proc_macro]
270pub fn swap_at(input: TokenStream) -> TokenStream {
271    let TupleSwap { tup, left, right } = parse_macro_input!(input as TupleSwap);
272    let tup = quote!( let mut tup_ = #tup );
273    let field = quote!(. 1);
274    let max = std::cmp::max(*left.iter().max().unwrap(), *right.iter().max().unwrap());
275    let mut indices: Vec<usize> = (0..=max).collect();
276    for i in 0..left.len() {
277        indices.swap(left[i], right[i]);
278    }
279    let tail = {
280        let fields = vec![field.clone(); max];
281        quote!( tup_ #(#fields)* .1 )
282    };
283    let output = indices
284        .into_iter()
285        .map(|x| {
286            let fields = vec![field.clone(); x];
287            quote!( tup_ #(#fields)* .0 )
288        })
289        .rfold(
290            tail,
291            |packed, token| quote!( tuplez::Tuple( #token, #packed ) ),
292        );
293
294    quote!({
295        #tup ;
296        #output
297    })
298    .into()
299}
300
301#[proc_macro]
302pub fn apply(input: TokenStream) -> TokenStream {
303    let TupleApply {
304        tup,
305        mut func,
306        args,
307    } = parse_macro_input!(input as TupleApply);
308    let tup = quote!( #[allow(unused_mut)] let mut tup_ = #tup );
309    let field_at = |index| {
310        let field = quote!(. 1);
311        let fields = vec![field.clone(); index];
312        parse_quote!( tup_ #(#fields)* . 0)
313    };
314    args.0
315        .into_iter()
316        .map(move |arg| match arg {
317            TupleArg::Move(index) => field_at(index),
318            TupleArg::Ref(index) => {
319                let arg = field_at(index);
320                parse_quote!(& #arg)
321            }
322            TupleArg::Mut(index) => {
323                let arg = field_at(index);
324                parse_quote!(& mut #arg)
325            }
326        })
327        .for_each(|arg| match &mut func {
328            Expr::Call(call) => call.args.push(arg),
329            Expr::MethodCall(call) => call.args.push(arg),
330            _ => (),
331        });
332    quote!({
333        #tup ;
334        #func
335    })
336    .into()
337}
338
339#[proc_macro]
340pub fn mapper(input: TokenStream) -> TokenStream {
341    let ReExportTuplez {
342        path,
343        other: Mapper(rules),
344    } = parse_macro_input!(input as ReExportTuplez<Mapper>);
345    let rules = rules.into_iter().map(
346        |Rule {
347             generic,
348             mut inputs,
349             output_type,
350             body,
351         }| {
352            let (x, tyx, mutx) = inputs.pop_front().unwrap();
353            let tyx = tyx.unwrap();
354            let mutx = if mutx { quote!(mut) } else { quote!() };
355            let tyout = output_type.unwrap();
356
357            quote!(
358                impl #generic Mapper<#tyx> for __Mapper {
359                    type Output = #tyout;
360                    type NextMapper = Self;
361                    fn map(self, value: #tyx) -> (Self::Output, Self::NextMapper) {
362                        let f = | #mutx #x : #tyx | -> #tyout #body;
363                        (f(value), self)
364                    }
365                }
366            )
367        },
368    );
369    quote!(
370        {
371            use #path::foreach::Mapper;
372            #[derive(Copy, Clone, Debug)]
373            struct __Mapper;
374            #(#rules)*
375            __Mapper
376        }
377    )
378    .into()
379}
380
381#[proc_macro]
382pub fn folder(input: TokenStream) -> TokenStream {
383    let ReExportTuplez {
384        path,
385        other: Folder(rules),
386    } = parse_macro_input!(input as ReExportTuplez<Folder>);
387    let rules = rules.into_iter().map(
388        |Rule {
389             generic,
390             mut inputs,
391             output_type,
392             body,
393         }| {
394            let (acc, tyacc, mutacc) = inputs.pop_front().unwrap();
395            let (x, tyx, mutx) = inputs.pop_front().unwrap();
396            let tyacc = tyacc.unwrap();
397            let mutacc = if mutacc { quote!(mut) } else { quote!() };
398            let tyx = tyx.unwrap();
399            let mutx = if mutx { quote!(mut) } else { quote!() };
400            let tyout = output_type.unwrap();
401
402            quote!(
403                impl #generic Folder<#tyx, #tyout> for __Folder {
404                    type Output = #tyout;
405                    type NextFolder = Self;
406                    fn fold(self, acc: #tyacc, value: #tyx) -> (Self::Output, Self::NextFolder) {
407                        let f = | #mutacc #acc: #tyacc, #mutx #x: #tyx | -> #tyout #body;
408                        (f(acc, value), self)
409                    }
410                }
411            )
412        },
413    );
414    quote!(
415        {
416            use #path::fold::Folder;
417            #[derive(Copy, Clone, Debug)]
418            struct __Folder;
419            #(#rules)*
420            __Folder
421        }
422    )
423    .into()
424}
425
426#[proc_macro]
427pub fn unary_pred(input: TokenStream) -> TokenStream {
428    let ReExportTuplez {
429        path,
430        other: UnaryPredicate(rules),
431    } = parse_macro_input!(input as ReExportTuplez<UnaryPredicate>);
432    let rules = rules.into_iter().map(
433        |Rule {
434             generic,
435             mut inputs,
436             output_type,
437             body,
438         }| {
439            let (x, tyx, mutx) = inputs.pop_front().unwrap();
440            let tyx = tyx.unwrap();
441            let mutx = if mutx { quote!(mut) } else { quote!() };
442            let tyout = output_type.unwrap();
443
444            quote!(
445                impl #generic Mapper<& #tyx> for __UnaryPred {
446                    type Output = #tyout;
447                    type NextMapper = Self;
448                    fn map(self, value: & #tyx) -> (Self::Output, Self::NextMapper) {
449                        let f = | #mutx #x : & #tyx | -> #tyout #body;
450                        (f(value), self)
451                    }
452                }
453            )
454        },
455    );
456    quote!(
457        {
458            use #path::foreach::Mapper;
459            #[derive(Copy, Clone, Debug)]
460            struct __UnaryPred;
461            #(#rules)*
462            __UnaryPred
463        }
464    )
465    .into()
466}
467
468#[proc_macro_derive(Tupleize)]
469pub fn tupleize_derive(input: TokenStream) -> TokenStream {
470    let DeriveInput {
471        attrs: _,
472        vis: _,
473        ident,
474        generics,
475        data,
476    } = parse_macro_input!(input as DeriveInput);
477
478    let Data::Struct(data) = data else {
479        return syn::Error::new(ident.span(), "expected struct")
480            .to_compile_error()
481            .into();
482    };
483    let (tuple_ty, from_tuple, to_tuple) = data.fields.iter().enumerate().rev().fold(
484        (quote!(tuplez::Unit), quote!(), quote!(tuplez::Unit)),
485        |(ty, from, to), (index, field)| {
486            let field_ty = &field.ty;
487            let ty = quote!( ::tuplez::Tuple< #field_ty, #ty > );
488            match &field.ident {
489                Some(ident) => {
490                    let field = quote!(. 1);
491                    let fields = vec![field.clone(); index];
492                    let element = quote!( value #(#fields)* . 0);
493                    let from = quote!( #ident: #element, #from );
494                    let to = quote!( ::tuplez::Tuple( value . #ident, #to) );
495                    (ty, from, to)
496                }
497                None => {
498                    let field = quote!(. 1);
499                    let fields = vec![field.clone(); index];
500                    let from = quote!( value #(#fields)* . 0, #from );
501                    let index = syn::Index::from(index);
502                    let to = quote!( ::tuplez::Tuple( value . #index, #to) );
503                    (ty, from, to)
504                }
505            }
506        },
507    );
508    let from_tuple = match &data.fields {
509        Fields::Named(_) => quote!( Self { #from_tuple } ),
510        Fields::Unnamed(_) => quote!( Self(#from_tuple) ),
511        Fields::Unit => quote!(Self),
512    };
513    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
514    quote!(
515        impl #impl_generics ::std::convert::From<#ident #ty_generics> for #tuple_ty #where_clause {
516            fn from(value: #ident #ty_generics) -> Self {
517                #to_tuple
518            }
519        }
520
521        impl #impl_generics ::std::convert::From<#tuple_ty> for #ident #ty_generics #where_clause {
522            fn from(value: #tuple_ty) -> Self {
523                #from_tuple
524            }
525        }
526
527        impl #impl_generics ::tuplez::Tupleize for #ident #ty_generics #where_clause {
528            type Equivalent = #tuple_ty;
529        }
530    )
531    .into()
532}