1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, parse_quote, punctuated::Punctuated};

mod parse;

use parse::*;

#[proc_macro]
pub fn stru(input: TokenStream) -> TokenStream {
    let AnonymousStruct(input) = parse_macro_input!(input as AnonymousStruct);
    let fields: Vec<_> = input
        .into_iter()
        .map(|(ident, expr)| match expr {
            Some(expr) => quote! { (<::stringz::ident!(#ident)>::default(), #expr) },
            None => quote! { (<::stringz::ident!(#ident)>::default(), #ident) },
        })
        .collect();
    quote! {
        ::tuplez::tuple!(#(#fields),*)
    }
    .into()
}

#[proc_macro]
pub fn stru_t(input: TokenStream) -> TokenStream {
    let AnonymousStructType(input) = parse_macro_input!(input as AnonymousStructType);
    let fields: Vec<_> = input
        .into_iter()
        .map(|(ident, ty)| quote! { (::stringz::ident!(#ident), #ty) })
        .collect();
    quote! {
        ::tuplez::tuple_t!(#(#fields),*)
    }
    .into()
}

#[proc_macro_attribute]
pub fn named_args(_: TokenStream, item: TokenStream) -> TokenStream {
    let mut input = parse_macro_input!(item as syn::ItemFn);
    let mut args = vec![];
    let mut has_self = None;
    for arg in input.sig.inputs {
        match arg {
            syn::FnArg::Receiver(arg) => has_self = Some(arg),
            syn::FnArg::Typed(arg) => {
                if let syn::Pat::Ident(pat) = *arg.pat {
                    args.push((pat, arg.ty))
                } else {
                    return quote! {
                       compile_error!("arguments must be an identifier or receiver");
                    }
                    .into();
                }
            }
        }
    }
    args.sort_by(|x, y| x.0.ident.cmp(&y.0.ident));
    let (idents, tys): (Vec<syn::PatIdent>, Vec<Box<syn::Type>>) = args.into_iter().unzip();
    let struct_type: syn::Type = parse_quote! {
        structz::stru_t! { #(#idents: #tys),* }
    };
    let mut inputs = Punctuated::new();
    if let Some(arg) = has_self {
        inputs.push(syn::FnArg::Receiver(arg));
    }
    inputs.push(syn::FnArg::Typed(syn::PatType {
        attrs: vec![],
        pat: parse_quote! { structz_s },
        colon_token: Default::default(),
        ty: Box::new(struct_type),
    }));
    input.sig.inputs = inputs;
    let mut unpack: Vec<(syn::Pat, syn::Type)> = vec![];
    for ident in idents {
        let ident = ident.ident;
        unpack.push((
            parse_quote! { #ident },
            parse_quote! { ::stringz::ident!(#ident) },
        ))
    }
    let (pat, ident_tys): (Vec<syn::Pat>, Vec<syn::Type>) = unpack.into_iter().unzip();
    let pat: syn::Pat = parse_quote! { ::tuplez::tuple_pat!( #( (_, #pat) ),* )};
    let ident_tys: syn::Type = parse_quote! { ::tuplez::tuple_t!( #( (#ident_tys, #tys) ),* )};
    let unpack: syn::Stmt = parse_quote! { let #pat: #ident_tys = structz_s; };
    input.block.stmts.insert(0, unpack);
    quote!(#input).into()
}