evmap_derive/
lib.rs

1//! This crate provides procedural derive macros to simplify the usage of `evmap`.
2//!
3//! Currently, only `#[derive(ShallowCopy)]` is supported; see below.
4#![warn(missing_docs, rust_2018_idioms, broken_intra_doc_links)]
5
6#[allow(unused_extern_crates)]
7extern crate proc_macro;
8
9use proc_macro2::{Ident, TokenStream};
10use quote::{format_ident, quote, quote_spanned};
11use syn::spanned::Spanned;
12use syn::{
13    parse_macro_input, parse_quote, Data, DeriveInput, Fields, GenericParam, Generics, Index,
14};
15
16/// Implementation for `#[derive(ShallowCopy)]`
17///
18/// evmap provides the [`ShallowCopy`](evmap::shallow_copy::ShallowCopy) trait, which allows you to
19/// cheaply alias types that don't otherwise implement `Copy`. Basic implementations are provided
20/// for common types such as `String` and `Vec`, but it must be implemented manually for structs
21/// using these types.
22///
23/// This macro attempts to simplify this task. It only works on types whose members all implement
24/// `ShallowCopy`. If this is not possible, consider using
25/// [`CopyValue`](evmap::shallow_copy::CopyValue), `Box`, or `Arc` instead.
26///
27/// ## Usage example
28/// ```
29/// # use evmap_derive::ShallowCopy;
30/// #[derive(ShallowCopy)]
31/// struct Thing { field: i32 }
32///
33/// #[derive(ShallowCopy)]
34/// struct Generic<T> { field: T }
35///
36/// #[derive(ShallowCopy)]
37/// enum Things<T> { One(Thing), Two(Generic<T>) }
38/// ```
39///
40/// ## Generated implementations
41/// The generated implementation calls
42/// [`shallow_copy`](evmap::shallow_copy::ShallowCopy::shallow_copy) on all the members of the
43/// type, and lifts the `ManuallyDrop` wrappers to the top-level return type.
44///
45/// For generic types, the derive adds `ShallowCopy` bounds to all the type parameters.
46///
47/// For instance, for the following code...
48/// ```
49/// # use evmap_derive::ShallowCopy;
50/// #[derive(ShallowCopy)]
51/// struct Generic<T> { field: T }
52/// ```
53/// ...the derive generates...
54/// ```
55/// # use evmap::shallow_copy::ShallowCopy;
56/// # use std::mem::ManuallyDrop;
57/// # struct Generic<T> { field: T }
58/// impl<T: ShallowCopy> ShallowCopy for Generic<T> {
59///     unsafe fn shallow_copy(&self) -> ManuallyDrop<Self> {
60///         ManuallyDrop::new(Self {
61///             field: ManuallyDrop::into_inner(ShallowCopy::shallow_copy(&self.field))
62///         })
63///     }
64/// }
65/// ```
66#[proc_macro_derive(ShallowCopy)]
67pub fn derive_shallow_copy(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
68    let input = parse_macro_input!(input as DeriveInput);
69    let name = input.ident;
70    let generics = add_trait_bounds(input.generics);
71    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
72    let copy = fields(&input.data, &name);
73    proc_macro::TokenStream::from(quote! {
74        impl #impl_generics evmap::shallow_copy::ShallowCopy for #name #ty_generics #where_clause {
75            unsafe fn shallow_copy(&self) -> std::mem::ManuallyDrop<Self> {
76                #copy
77            }
78        }
79    })
80}
81
82fn add_trait_bounds(mut generics: Generics) -> Generics {
83    for param in &mut generics.params {
84        if let GenericParam::Type(ref mut type_param) = *param {
85            type_param
86                .bounds
87                .push(parse_quote!(evmap::shallow_copy::ShallowCopy));
88        }
89    }
90    generics
91}
92
93fn fields(data: &Data, type_name: &Ident) -> TokenStream {
94    match data {
95        Data::Struct(data) => match &data.fields {
96            Fields::Named(fields) => {
97                let recurse = fields.named.iter().map(|f| {
98                    let name = &f.ident;
99                    quote_spanned! {f.span()=>
100                        #name: std::mem::ManuallyDrop::into_inner(
101                            evmap::shallow_copy::ShallowCopy::shallow_copy(&self.#name)
102                        )
103                    }
104                });
105                quote! {
106                    std::mem::ManuallyDrop::new(Self { #(#recurse,)* })
107                }
108            }
109            Fields::Unnamed(fields) => {
110                let recurse = fields.unnamed.iter().enumerate().map(|(i, f)| {
111                    let index = Index::from(i);
112                    quote_spanned! {f.span()=>
113                        std::mem::ManuallyDrop::into_inner(
114                            evmap::shallow_copy::ShallowCopy::shallow_copy(&self.#index)
115                        )
116                    }
117                });
118                quote! {
119                    std::mem::ManuallyDrop::new(#type_name(#(#recurse,)*))
120                }
121            }
122            Fields::Unit => quote!(std::mem::ManuallyDrop::new(#type_name)),
123        },
124        Data::Enum(data) => {
125            let recurse = data.variants.iter().map(|f| {
126                let (names, fields) = match &f.fields {
127                    Fields::Named(fields) => {
128                        let field_names = f.fields.iter().map(|field| {
129                            let ident = field.ident.as_ref().unwrap();
130                            quote_spanned! {
131                                field.span()=> #ident
132                            }
133                        });
134                        let recurse = fields.named.iter().map(|f| {
135                            let name = f.ident.as_ref().unwrap();
136                            quote_spanned! {f.span()=>
137                                #name: std::mem::ManuallyDrop::into_inner(
138                                    evmap::shallow_copy::ShallowCopy::shallow_copy(#name)
139                                )
140                            }
141                        });
142                        (quote! { {#(#field_names,)*} }, quote! { { #(#recurse,)* } })
143                    }
144                    Fields::Unnamed(fields) => {
145                        let field_names = f.fields.iter().enumerate().map(|(i, field)| {
146                            let ident = format_ident!("x{}", i);
147                            quote_spanned! {
148                                field.span()=> #ident
149                            }
150                        });
151                        let recurse = fields.unnamed.iter().enumerate().map(|(i, f)| {
152                            let ident = format_ident!("x{}", i);
153                            quote_spanned! {f.span()=>
154                                std::mem::ManuallyDrop::into_inner(
155                                    evmap::shallow_copy::ShallowCopy::shallow_copy(#ident)
156                                )
157                            }
158                        });
159                        (quote! { (#(#field_names,)*) }, quote! { (#(#recurse,)*) })
160                    }
161                    Fields::Unit => (quote!(), quote!()),
162                };
163                let name = &f.ident;
164                quote_spanned! {f.span()=>
165                    #type_name::#name#names => std::mem::ManuallyDrop::new(#type_name::#name#fields)
166                }
167            });
168            quote! {
169                match self {
170                    #(#recurse,)*
171                }
172            }
173        }
174        Data::Union(_) => unimplemented!("Unions are not supported"),
175    }
176}