summaryrefslogtreecommitdiff
path: root/src/generate/derives.rs
blob: 0233a3f3391fa1ed26cdbd8a516b7bbac1dec10e (plain)
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
use crate::info_structures::{Derive, StructInfo};
use proc_macro2::TokenStream;
use quote::quote;
use syn::{Error, GenericParam, TypeParamBound};

fn add_trait_bound(param: &GenericParam, bound: &TypeParamBound) -> GenericParam {
    let mut new = param.clone();
    match &mut new {
        GenericParam::Type(t) => t.bounds.push(bound.clone()),
        _ => (),
    }
    new
}

fn impl_trait(info: &StructInfo, trait_name: TypeParamBound, body: TokenStream) -> TokenStream {
    let generic_params = info.generic_params();
    let generic_params = generic_params
        .into_iter()
        .map(|i| add_trait_bound(i, &trait_name))
        .collect::<Vec<_>>();
    let generic_args = info.generic_arguments();
    let generic_where = &info.generics.where_clause;
    let struct_name = &info.ident;
    quote! {
        impl <#(#generic_params),*> #trait_name for #struct_name <#(#generic_args),*> #generic_where {
            #body
        }
    }
}

fn impl_debug(info: &StructInfo) -> Result<TokenStream, Error> {
    let fields = info
        .fields
        .iter()
        .filter(|field| !field.is_mutably_borrowed())
        .map(|field| {
            let name = &field.name;
            quote! {
                field(stringify!(#name), &safe_self.#name)
            }
        })
        .collect::<Vec<_>>();
    let trait_name = syn::parse_quote! { ::core::fmt::Debug };
    let struct_name = &info.ident;
    let body = quote! {
        fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
            self.with(|safe_self| {
                f.debug_struct(stringify!(#struct_name))
                #(.#fields)*
                .finish()
            })
        }
    };
    Ok(impl_trait(info, trait_name, body))
}

fn impl_partial_eq(info: &StructInfo) -> Result<TokenStream, Error> {
    let fields = info
        .fields
        .iter()
        .filter(|field| !field.is_mutably_borrowed())
        .map(|field| {
            let name = &field.name;
            quote! {
                &*safe_self.#name == &*safe_other.#name
            }
        })
        .collect::<Vec<_>>();
    let trait_name = syn::parse_quote! { ::core::cmp::PartialEq };
    let body = quote! {
        fn eq(&self, other: &Self) -> bool {
            self.with(|safe_self| {
                other.with(|safe_other| {
                    #(#fields)&&*
                })
            })
        }
    };
    Ok(impl_trait(info, trait_name, body))
}

fn impl_eq(info: &StructInfo) -> Result<TokenStream, Error> {
    let trait_name = syn::parse_quote! { ::core::cmp::Eq };
    let body = quote! {};
    Ok(impl_trait(info, trait_name, body))
}

pub fn create_derives(info: &StructInfo) -> Result<TokenStream, Error> {
    let mut impls = Vec::new();
    for derive in &info.derives {
        match derive {
            Derive::Debug => impls.push(impl_debug(info)?),
            Derive::PartialEq => impls.push(impl_partial_eq(info)?),
            Derive::Eq => impls.push(impl_eq(info)?),
        }
    }
    Ok(quote! { #(#impls)* })
}