ouroboros_macro/generate/
derives.rs

1use crate::info_structures::{Derive, StructInfo};
2use proc_macro2::TokenStream;
3use quote::quote;
4use syn::{Error, GenericParam, TypeParamBound};
5
6fn add_trait_bound(param: &GenericParam, bound: &TypeParamBound) -> GenericParam {
7    let mut new = param.clone();
8
9    if let GenericParam::Type(t) = &mut new {
10        t.bounds.push(bound.clone())
11    }
12
13    new
14}
15
16fn impl_trait(info: &StructInfo, trait_name: TypeParamBound, body: TokenStream) -> TokenStream {
17    let generic_params = info.generic_params();
18    let generic_params = generic_params
19        .into_iter()
20        .map(|i| add_trait_bound(i, &trait_name))
21        .collect::<Vec<_>>();
22    let generic_args = info.generic_arguments();
23    let generic_where = &info.generics.where_clause;
24    let struct_name = &info.ident;
25    quote! {
26        impl <#(#generic_params),*> #trait_name for #struct_name <#(#generic_args),*> #generic_where {
27            #body
28        }
29    }
30}
31
32fn impl_debug(info: &StructInfo) -> Result<TokenStream, Error> {
33    let fields = info
34        .fields
35        .iter()
36        .filter(|field| !field.is_mutably_borrowed())
37        .map(|field| {
38            let name = &field.name;
39            quote! {
40                field(stringify!(#name), &safe_self.#name)
41            }
42        })
43        .collect::<Vec<_>>();
44    let trait_name = syn::parse_quote! { ::core::fmt::Debug };
45    let struct_name = &info.ident;
46    let body = quote! {
47        fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
48            self.with(|safe_self| {
49                f.debug_struct(stringify!(#struct_name))
50                #(.#fields)*
51                .finish()
52            })
53        }
54    };
55    Ok(impl_trait(info, trait_name, body))
56}
57
58fn impl_partial_eq(info: &StructInfo) -> Result<TokenStream, Error> {
59    let fields = info
60        .fields
61        .iter()
62        .filter(|field| !field.is_mutably_borrowed())
63        .map(|field| {
64            let name = &field.name;
65            quote! {
66                &*safe_self.#name == &*safe_other.#name
67            }
68        })
69        .collect::<Vec<_>>();
70    let trait_name = syn::parse_quote! { ::core::cmp::PartialEq };
71    let body = quote! {
72        fn eq(&self, other: &Self) -> bool {
73            self.with(|safe_self| {
74                other.with(|safe_other| {
75                    #(#fields)&&*
76                })
77            })
78        }
79    };
80    Ok(impl_trait(info, trait_name, body))
81}
82
83fn impl_eq(info: &StructInfo) -> Result<TokenStream, Error> {
84    let trait_name = syn::parse_quote! { ::core::cmp::Eq };
85    let body = quote! {};
86    Ok(impl_trait(info, trait_name, body))
87}
88
89pub fn create_derives(info: &StructInfo) -> Result<TokenStream, Error> {
90    let mut impls = Vec::new();
91    for derive in &info.derives {
92        match derive {
93            Derive::Debug => impls.push(impl_debug(info)?),
94            Derive::PartialEq => impls.push(impl_partial_eq(info)?),
95            Derive::Eq => impls.push(impl_eq(info)?),
96        }
97    }
98    Ok(quote! { #(#impls)* })
99}