cxxbridge_macro/
derive.rs

1use crate::syntax::{derive, Enum, Struct};
2use proc_macro2::{Ident, Span, TokenStream};
3use quote::{quote, quote_spanned, ToTokens};
4
5pub(crate) use crate::syntax::derive::*;
6
7pub(crate) fn expand_struct(
8    strct: &Struct,
9    actual_derives: &mut Option<TokenStream>,
10) -> TokenStream {
11    let mut expanded = TokenStream::new();
12    let mut traits = Vec::new();
13
14    for derive in &strct.derives {
15        let span = derive.span;
16        match derive.what {
17            Trait::Copy => expanded.extend(struct_copy(strct, span)),
18            Trait::Clone => expanded.extend(struct_clone(strct, span)),
19            Trait::Debug => expanded.extend(struct_debug(strct, span)),
20            Trait::Default => expanded.extend(struct_default(strct, span)),
21            Trait::Eq => traits.push(quote_spanned!(span=> ::cxx::core::cmp::Eq)),
22            Trait::ExternType => unreachable!(),
23            Trait::Hash => traits.push(quote_spanned!(span=> ::cxx::core::hash::Hash)),
24            Trait::Ord => expanded.extend(struct_ord(strct, span)),
25            Trait::PartialEq => traits.push(quote_spanned!(span=> ::cxx::core::cmp::PartialEq)),
26            Trait::PartialOrd => expanded.extend(struct_partial_ord(strct, span)),
27            Trait::Serialize => traits.push(quote_spanned!(span=> ::serde::Serialize)),
28            Trait::Deserialize => traits.push(quote_spanned!(span=> ::serde::Deserialize)),
29        }
30    }
31
32    if traits.is_empty() {
33        *actual_derives = None;
34    } else {
35        *actual_derives = Some(quote!(#[derive(#(#traits),*)]));
36    }
37
38    expanded
39}
40
41pub(crate) fn expand_enum(enm: &Enum, actual_derives: &mut Option<TokenStream>) -> TokenStream {
42    let mut expanded = TokenStream::new();
43    let mut traits = Vec::new();
44    let mut has_copy = false;
45    let mut has_clone = false;
46    let mut has_eq = false;
47    let mut has_partial_eq = false;
48
49    for derive in &enm.derives {
50        let span = derive.span;
51        match derive.what {
52            Trait::Copy => {
53                expanded.extend(enum_copy(enm, span));
54                has_copy = true;
55            }
56            Trait::Clone => {
57                expanded.extend(enum_clone(enm, span));
58                has_clone = true;
59            }
60            Trait::Debug => expanded.extend(enum_debug(enm, span)),
61            Trait::Default => unreachable!(),
62            Trait::Eq => {
63                traits.push(quote_spanned!(span=> ::cxx::core::cmp::Eq));
64                has_eq = true;
65            }
66            Trait::ExternType => unreachable!(),
67            Trait::Hash => traits.push(quote_spanned!(span=> ::cxx::core::hash::Hash)),
68            Trait::Ord => expanded.extend(enum_ord(enm, span)),
69            Trait::PartialEq => {
70                traits.push(quote_spanned!(span=> ::cxx::core::cmp::PartialEq));
71                has_partial_eq = true;
72            }
73            Trait::PartialOrd => expanded.extend(enum_partial_ord(enm, span)),
74            Trait::Serialize => traits.push(quote_spanned!(span=> ::serde::Serialize)),
75            Trait::Deserialize => traits.push(quote_spanned!(span=> ::serde::Deserialize)),
76        }
77    }
78
79    let span = enm.name.rust.span();
80    if !has_copy {
81        expanded.extend(enum_copy(enm, span));
82    }
83    if !has_clone {
84        expanded.extend(enum_clone(enm, span));
85    }
86    if !has_eq {
87        // Required to be derived in order for the enum's "variants" to be
88        // usable in patterns.
89        traits.push(quote!(::cxx::core::cmp::Eq));
90    }
91    if !has_partial_eq {
92        traits.push(quote!(::cxx::core::cmp::PartialEq));
93    }
94
95    *actual_derives = Some(quote!(#[derive(#(#traits),*)]));
96
97    expanded
98}
99
100fn struct_copy(strct: &Struct, span: Span) -> TokenStream {
101    let ident = &strct.name.rust;
102    let generics = &strct.generics;
103
104    quote_spanned! {span=>
105        #[automatically_derived]
106        impl #generics ::cxx::core::marker::Copy for #ident #generics {}
107    }
108}
109
110fn struct_clone(strct: &Struct, span: Span) -> TokenStream {
111    let ident = &strct.name.rust;
112    let generics = &strct.generics;
113
114    let body = if derive::contains(&strct.derives, Trait::Copy) {
115        quote!(*self)
116    } else {
117        let fields = strct.fields.iter().map(|field| &field.name.rust);
118        let values = strct.fields.iter().map(|field| {
119            let ident = &field.name.rust;
120            let ty = field.ty.to_token_stream();
121            let span = ty.into_iter().last().unwrap().span();
122            quote_spanned!(span=> &self.#ident)
123        });
124        quote_spanned!(span=> #ident {
125            #(#fields: ::cxx::core::clone::Clone::clone(#values),)*
126        })
127    };
128
129    quote_spanned! {span=>
130        #[automatically_derived]
131        #[allow(clippy::expl_impl_clone_on_copy)]
132        impl #generics ::cxx::core::clone::Clone for #ident #generics {
133            fn clone(&self) -> Self {
134                #body
135            }
136        }
137    }
138}
139
140fn struct_debug(strct: &Struct, span: Span) -> TokenStream {
141    let ident = &strct.name.rust;
142    let generics = &strct.generics;
143    let struct_name = ident.to_string();
144    let fields = strct.fields.iter().map(|field| &field.name.rust);
145    let field_names = fields.clone().map(Ident::to_string);
146
147    quote_spanned! {span=>
148        #[automatically_derived]
149        impl #generics ::cxx::core::fmt::Debug for #ident #generics {
150            fn fmt(&self, formatter: &mut ::cxx::core::fmt::Formatter<'_>) -> ::cxx::core::fmt::Result {
151                formatter.debug_struct(#struct_name)
152                    #(.field(#field_names, &self.#fields))*
153                    .finish()
154            }
155        }
156    }
157}
158
159fn struct_default(strct: &Struct, span: Span) -> TokenStream {
160    let ident = &strct.name.rust;
161    let generics = &strct.generics;
162    let fields = strct.fields.iter().map(|field| &field.name.rust);
163
164    quote_spanned! {span=>
165        #[automatically_derived]
166        #[allow(clippy::derivable_impls)] // different spans than the derived impl
167        impl #generics ::cxx::core::default::Default for #ident #generics {
168            fn default() -> Self {
169                #ident {
170                    #(
171                        #fields: ::cxx::core::default::Default::default(),
172                    )*
173                }
174            }
175        }
176    }
177}
178
179fn struct_ord(strct: &Struct, span: Span) -> TokenStream {
180    let ident = &strct.name.rust;
181    let generics = &strct.generics;
182    let fields = strct.fields.iter().map(|field| &field.name.rust);
183
184    quote_spanned! {span=>
185        #[automatically_derived]
186        impl #generics ::cxx::core::cmp::Ord for #ident #generics {
187            fn cmp(&self, other: &Self) -> ::cxx::core::cmp::Ordering {
188                #(
189                    match ::cxx::core::cmp::Ord::cmp(&self.#fields, &other.#fields) {
190                        ::cxx::core::cmp::Ordering::Equal => {}
191                        ordering => return ordering,
192                    }
193                )*
194                ::cxx::core::cmp::Ordering::Equal
195            }
196        }
197    }
198}
199
200fn struct_partial_ord(strct: &Struct, span: Span) -> TokenStream {
201    let ident = &strct.name.rust;
202    let generics = &strct.generics;
203
204    let body = if derive::contains(&strct.derives, Trait::Ord) {
205        quote! {
206            ::cxx::core::option::Option::Some(::cxx::core::cmp::Ord::cmp(self, other))
207        }
208    } else {
209        let fields = strct.fields.iter().map(|field| &field.name.rust);
210        quote! {
211            #(
212                match ::cxx::core::cmp::PartialOrd::partial_cmp(&self.#fields, &other.#fields) {
213                    ::cxx::core::option::Option::Some(::cxx::core::cmp::Ordering::Equal) => {}
214                    ordering => return ordering,
215                }
216            )*
217            ::cxx::core::option::Option::Some(::cxx::core::cmp::Ordering::Equal)
218        }
219    };
220
221    quote_spanned! {span=>
222        #[automatically_derived]
223        impl #generics ::cxx::core::cmp::PartialOrd for #ident #generics {
224            #[allow(clippy::non_canonical_partial_ord_impl)]
225            #[allow(renamed_and_removed_lints, clippy::incorrect_partial_ord_impl_on_ord_type)] // Rust 1.73 and older
226            fn partial_cmp(&self, other: &Self) -> ::cxx::core::option::Option<::cxx::core::cmp::Ordering> {
227                #body
228            }
229        }
230    }
231}
232
233fn enum_copy(enm: &Enum, span: Span) -> TokenStream {
234    let ident = &enm.name.rust;
235
236    quote_spanned! {span=>
237        #[automatically_derived]
238        impl ::cxx::core::marker::Copy for #ident {}
239    }
240}
241
242fn enum_clone(enm: &Enum, span: Span) -> TokenStream {
243    let ident = &enm.name.rust;
244
245    quote_spanned! {span=>
246        #[automatically_derived]
247        #[allow(clippy::expl_impl_clone_on_copy)]
248        impl ::cxx::core::clone::Clone for #ident {
249            fn clone(&self) -> Self {
250                *self
251            }
252        }
253    }
254}
255
256fn enum_debug(enm: &Enum, span: Span) -> TokenStream {
257    let ident = &enm.name.rust;
258    let variants = enm.variants.iter().map(|variant| {
259        let variant = &variant.name.rust;
260        let name = variant.to_string();
261        quote_spanned! {span=>
262            #ident::#variant => formatter.write_str(#name),
263        }
264    });
265    let fallback = format!("{}({{}})", ident);
266
267    quote_spanned! {span=>
268        #[automatically_derived]
269        impl ::cxx::core::fmt::Debug for #ident {
270            fn fmt(&self, formatter: &mut ::cxx::core::fmt::Formatter<'_>) -> ::cxx::core::fmt::Result {
271                match *self {
272                    #(#variants)*
273                    _ => ::cxx::core::write!(formatter, #fallback, self.repr),
274                }
275            }
276        }
277    }
278}
279
280fn enum_ord(enm: &Enum, span: Span) -> TokenStream {
281    let ident = &enm.name.rust;
282
283    quote_spanned! {span=>
284        #[automatically_derived]
285        impl ::cxx::core::cmp::Ord for #ident {
286            fn cmp(&self, other: &Self) -> ::cxx::core::cmp::Ordering {
287                ::cxx::core::cmp::Ord::cmp(&self.repr, &other.repr)
288            }
289        }
290    }
291}
292
293fn enum_partial_ord(enm: &Enum, span: Span) -> TokenStream {
294    let ident = &enm.name.rust;
295
296    quote_spanned! {span=>
297        #[automatically_derived]
298        impl ::cxx::core::cmp::PartialOrd for #ident {
299            #[allow(clippy::non_canonical_partial_ord_impl)]
300            #[allow(renamed_and_removed_lints, clippy::incorrect_partial_ord_impl_on_ord_type)] // Rust 1.73 and older
301            fn partial_cmp(&self, other: &Self) -> ::cxx::core::option::Option<::cxx::core::cmp::Ordering> {
302                ::cxx::core::cmp::PartialOrd::partial_cmp(&self.repr, &other.repr)
303            }
304        }
305    }
306}