winwrap_derive/
lib.rs

1use quote::quote;
2use syn::{parse_macro_input, DeriveInput, Data, FnArg, ItemFn};
3
4macro_rules! derive_handle {
5    ($name:ident) => (
6    #[proc_macro_derive($name)]
7    #[allow(non_snake_case)]
8    pub fn $name(ast: proc_macro::TokenStream) -> proc_macro::TokenStream {
9        let ast = parse_macro_input!(ast as DeriveInput);
10        let (impl_generics, type_generics, where_clause) = ast.generics.split_for_impl();
11        let struct_name = &ast.ident;
12        let k = match ast.data {
13            Data::Struct(_) => {
14                quote! {
15                    impl #impl_generics crate::handle::$name for #struct_name #type_generics #where_clause {}
16                }
17            }
18            _ => panic!(concat!(stringify!($name)," can only be derived from struct")),
19        };
20        k.into()
21    }
22    );
23}
24
25derive_handle! {WritableHandle}
26derive_handle! {ReadableHandle}
27derive_handle! {CancelableIoHandle}
28derive_handle! {WaitableHandle}
29derive_handle! {DuplicatableHandle}
30
31/// Generates `xxx` function from `xxx_a` function.
32#[proc_macro_attribute]
33pub fn ansi_fn(_attr: proc_macro::TokenStream, ast: proc_macro::TokenStream) -> proc_macro::TokenStream {
34    generate_fn(ast, true)
35}
36
37/// Generates `xxx` function from `xxx_w` function.
38#[proc_macro_attribute]
39pub fn unicode_fn(_attr: proc_macro::TokenStream, ast: proc_macro::TokenStream) -> proc_macro::TokenStream {
40    generate_fn(ast, false)
41}
42
43fn generate_fn(ast: proc_macro::TokenStream, is_ansi: bool) -> proc_macro::TokenStream {
44    let orig_fn = parse_macro_input!(ast as ItemFn);
45    let orig_name = orig_fn.sig.ident.to_string();
46    assert!(orig_name.ends_with(if is_ansi { "_a" } else { "_w" }));
47    let ident = syn::Ident::new(&orig_name, proc_macro2::Span::call_site()); // create_file_w
48    // new function name.
49    // e.g. create_file_w -> create_file
50    let new_ident = syn::Ident::new(&orig_name[..orig_name.len() - 2], proc_macro2::Span::call_site());
51
52    let (impl_generics, _, where_clause) = orig_fn.sig.generics.split_for_impl();
53    let vis = orig_fn.vis.clone(); // Visibility
54    let unsafety = orig_fn.sig.unsafety.clone(); // Unsafety
55
56    // generates argument tokens.
57    let mut idents = proc_macro2::TokenStream::new();
58    for x in orig_fn.sig.inputs.iter() {
59        match x {
60            FnArg::Typed(pt) => {
61                let x = pt.pat.as_ref();
62                idents.extend(quote! {#x,});
63            }
64            _ => panic!("Unknown arg type"),
65        }
66    }
67    let rty = orig_fn.sig.output.clone(); // ReturnType
68
69    let m = orig_fn.sig.inputs.clone(); // FnArg declarations.
70    let body = quote! { #ident(#idents) };
71
72    let new_fn = if is_ansi {
73        quote! {
74        #[cfg(feature = "ansi")]
75        #vis #unsafety fn #new_ident #impl_generics(#m) #rty #where_clause{
76            #body
77        }
78    }
79    } else {
80        quote! {
81        #[cfg(not(feature = "ansi"))]
82        #vis #unsafety fn #new_ident #impl_generics(#m) #rty #where_clause{
83            #body
84        }
85    }
86    };
87    let item = quote! { #orig_fn #new_fn };
88    item.into()
89}