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 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
//! # Loosen //! //! Declares a derive-like function attr macro which creates another wrap-like //! function with single tuple-loosened argument, retrieved from the former //! function. //! //! Upon call, the loosened function propagates the call with the flattened //! input tuple as arguments to the original function. //! //! ## Example //! //! ``` //! # extern crate loosen; //! # use loosen::loose; //! # //! # pub struct A; //! # pub struct B; //! //! #[loose] //! fn fa(a: A, b: B) {} //! //! // normal call //! fa(A, B); //! //! // loose call //! let args = (A, B); //! fa_loose(args); //! // ie. instead of two arguments, //! // there is only a single tuple argument //! //! // another usage exaple //! (0..10) //! .map(|_| (A, B)) //! .map(fa_loose) //! .collect::<Vec<_>>(); //! ``` //! //! ## Note //! //! This is a draft and is my first try on proc-macros. //! I suggested this as an rfc before realising a proc macro would suffice: //! https://github.com/rust-lang/rfcs/issues/2667 extern crate proc_macro; use syn; use proc_macro::TokenStream; use quote::quote; /// For a function `fa(A, B)`, /// derives `fa_loose((A, B,))` which calls `fa(A, B)`. /// /// ie. /// `fa_loose` has a single argument, a tuple. /// This tuple is flattened and used as arguments to `fa`. #[proc_macro_attribute] pub fn loose(_attr: TokenStream, item: TokenStream) -> TokenStream { let input = syn::parse_macro_input!(item as syn::ItemFn); // replicates the input function let replica = quote! { #input }; // extract parsed information let vis = &input.vis; let constness = &input.constness; let unsafety = &input.unsafety; let ident = &input.ident; let decl = &input.decl; let _block = &input.block; // ------- // TODO: also deal with those information: // let attrs = &input.attrs; // let asyncness = &input.asyncness; // let abi = &input.abi; let generics = &decl.generics; let inputs = &decl.inputs; let output = &decl.output; // ------- // TODO: also deal with this information // let variadic = &decl.variadic; let ident_loose = format!("{}_loose", ident); let ident_loose = syn::Ident::new(&ident_loose, ident.span()); // iterates over FnArgs // and get patterns (such as idents) and types let (pats, types): (Vec<_>, Vec<_>) = inputs .iter() .map(|fn_arg| { match fn_arg { syn::FnArg::SelfRef(_self_ref) => { panic!("TODO FnArg::SelfRef"); } syn::FnArg::SelfValue(_self_value) => { panic!("TODO FnArg::SelfValue"); } syn::FnArg::Captured(captured) => { // panic!("TODO FnArg::Captured"); (&captured.pat, &captured.ty) } syn::FnArg::Inferred(_pat) => { panic!("TODO FnArg::Inferred"); } syn::FnArg::Ignored(_ty) => { panic!("TODO FnArg::Ignored"); } } }) .unzip(); // given vectors of patters (such as idents) and types // quote a tuple definition for them let args_into_tuple = { let pats = pats.clone(); let types = types.clone(); quote! { (#(#pats,)*): (#(#types,)*) } }; // creates a loosened wrapper for the replica // such that the loosened has only one parameter, a tuple, // and flatten that tuple as parameters for the replica call let loosened = quote! { #[inline(always)] #vis #constness #unsafety fn #ident_loose #generics ( #args_into_tuple ) #output { #ident(#(#pats),*) } }; // println!("\n ------> ident: {}", ident.to_string()); // println!(" ==> < {} >", &replica); // println!(" ==> < {} >\n", &loosened); let tokens = quote! { #replica #loosened }; tokens.into() }