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 => expanded.extend(enum_default(enm, span)),
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    let attrs = &strct.attrs;
104
105    quote_spanned! {span=>
106        #attrs
107        #[automatically_derived]
108        impl #generics ::cxx::core::marker::Copy for #ident #generics {}
109    }
110}
111
112fn struct_clone(strct: &Struct, span: Span) -> TokenStream {
113    let ident = &strct.name.rust;
114    let generics = &strct.generics;
115    let attrs = &strct.attrs;
116
117    let body = if derive::contains(&strct.derives, Trait::Copy) {
118        quote!(*self)
119    } else {
120        let fields = strct.fields.iter().map(|field| &field.name.rust);
121        let values = strct.fields.iter().map(|field| {
122            let ident = &field.name.rust;
123            let ty = field.ty.to_token_stream();
124            let span = ty.into_iter().last().unwrap().span();
125            quote_spanned!(span=> &self.#ident)
126        });
127        quote_spanned!(span=> #ident {
128            #(#fields: ::cxx::core::clone::Clone::clone(#values),)*
129        })
130    };
131
132    quote_spanned! {span=>
133        #attrs
134        #[automatically_derived]
135        #[allow(clippy::expl_impl_clone_on_copy)]
136        impl #generics ::cxx::core::clone::Clone for #ident #generics {
137            fn clone(&self) -> Self {
138                #body
139            }
140        }
141    }
142}
143
144fn struct_debug(strct: &Struct, span: Span) -> TokenStream {
145    let ident = &strct.name.rust;
146    let generics = &strct.generics;
147    let attrs = &strct.attrs;
148    let struct_name = ident.to_string();
149    let fields = strct.fields.iter().map(|field| &field.name.rust);
150    let field_names = fields.clone().map(Ident::to_string);
151
152    quote_spanned! {span=>
153        #attrs
154        #[automatically_derived]
155        impl #generics ::cxx::core::fmt::Debug for #ident #generics {
156            fn fmt(&self, formatter: &mut ::cxx::core::fmt::Formatter<'_>) -> ::cxx::core::fmt::Result {
157                formatter.debug_struct(#struct_name)
158                    #(.field(#field_names, &self.#fields))*
159                    .finish()
160            }
161        }
162    }
163}
164
165fn struct_default(strct: &Struct, span: Span) -> TokenStream {
166    let ident = &strct.name.rust;
167    let generics = &strct.generics;
168    let attrs = &strct.attrs;
169    let fields = strct.fields.iter().map(|field| &field.name.rust);
170
171    quote_spanned! {span=>
172        #attrs
173        #[automatically_derived]
174        #[allow(clippy::derivable_impls)] // different spans than the derived impl
175        impl #generics ::cxx::core::default::Default for #ident #generics {
176            fn default() -> Self {
177                #ident {
178                    #(
179                        #fields: ::cxx::core::default::Default::default(),
180                    )*
181                }
182            }
183        }
184    }
185}
186
187fn struct_ord(strct: &Struct, span: Span) -> TokenStream {
188    let ident = &strct.name.rust;
189    let generics = &strct.generics;
190    let attrs = &strct.attrs;
191    let fields = strct.fields.iter().map(|field| &field.name.rust);
192
193    quote_spanned! {span=>
194        #attrs
195        #[automatically_derived]
196        impl #generics ::cxx::core::cmp::Ord for #ident #generics {
197            fn cmp(&self, other: &Self) -> ::cxx::core::cmp::Ordering {
198                #(
199                    match ::cxx::core::cmp::Ord::cmp(&self.#fields, &other.#fields) {
200                        ::cxx::core::cmp::Ordering::Equal => {}
201                        ordering => return ordering,
202                    }
203                )*
204                ::cxx::core::cmp::Ordering::Equal
205            }
206        }
207    }
208}
209
210fn struct_partial_ord(strct: &Struct, span: Span) -> TokenStream {
211    let ident = &strct.name.rust;
212    let generics = &strct.generics;
213    let attrs = &strct.attrs;
214
215    let body = if derive::contains(&strct.derives, Trait::Ord) {
216        quote! {
217            ::cxx::core::option::Option::Some(::cxx::core::cmp::Ord::cmp(self, other))
218        }
219    } else {
220        let fields = strct.fields.iter().map(|field| &field.name.rust);
221        quote! {
222            #(
223                match ::cxx::core::cmp::PartialOrd::partial_cmp(&self.#fields, &other.#fields) {
224                    ::cxx::core::option::Option::Some(::cxx::core::cmp::Ordering::Equal) => {}
225                    ordering => return ordering,
226                }
227            )*
228            ::cxx::core::option::Option::Some(::cxx::core::cmp::Ordering::Equal)
229        }
230    };
231
232    quote_spanned! {span=>
233        #attrs
234        #[automatically_derived]
235        impl #generics ::cxx::core::cmp::PartialOrd for #ident #generics {
236            #[allow(clippy::non_canonical_partial_ord_impl)]
237            fn partial_cmp(&self, other: &Self) -> ::cxx::core::option::Option<::cxx::core::cmp::Ordering> {
238                #body
239            }
240        }
241    }
242}
243
244fn enum_copy(enm: &Enum, span: Span) -> TokenStream {
245    let ident = &enm.name.rust;
246    let attrs = &enm.attrs;
247
248    quote_spanned! {span=>
249        #attrs
250        #[automatically_derived]
251        impl ::cxx::core::marker::Copy for #ident {}
252    }
253}
254
255fn enum_clone(enm: &Enum, span: Span) -> TokenStream {
256    let ident = &enm.name.rust;
257    let attrs = &enm.attrs;
258
259    quote_spanned! {span=>
260        #attrs
261        #[automatically_derived]
262        #[allow(clippy::expl_impl_clone_on_copy)]
263        impl ::cxx::core::clone::Clone for #ident {
264            fn clone(&self) -> Self {
265                *self
266            }
267        }
268    }
269}
270
271fn enum_debug(enm: &Enum, span: Span) -> TokenStream {
272    let ident = &enm.name.rust;
273    let attrs = &enm.attrs;
274    let variants = enm.variants.iter().map(|variant| {
275        let variant = &variant.name.rust;
276        let name = variant.to_string();
277        quote_spanned! {span=>
278            #ident::#variant => formatter.write_str(#name),
279        }
280    });
281    let fallback = format!("{}({{}})", ident);
282
283    quote_spanned! {span=>
284        #attrs
285        #[automatically_derived]
286        impl ::cxx::core::fmt::Debug for #ident {
287            fn fmt(&self, formatter: &mut ::cxx::core::fmt::Formatter<'_>) -> ::cxx::core::fmt::Result {
288                match *self {
289                    #(#variants)*
290                    _ => ::cxx::core::write!(formatter, #fallback, self.repr),
291                }
292            }
293        }
294    }
295}
296
297fn enum_default(enm: &Enum, span: Span) -> TokenStream {
298    let ident = &enm.name.rust;
299    let attrs = &enm.attrs;
300
301    for variant in &enm.variants {
302        if variant.default {
303            let variant = &variant.name.rust;
304            return quote_spanned! {span=>
305                #attrs
306                #[automatically_derived]
307                impl ::cxx::core::default::Default for #ident {
308                    fn default() -> Self {
309                        #ident::#variant
310                    }
311                }
312            };
313        }
314    }
315
316    unreachable!("no #[default] variant");
317}
318
319fn enum_ord(enm: &Enum, span: Span) -> TokenStream {
320    let ident = &enm.name.rust;
321    let attrs = &enm.attrs;
322
323    quote_spanned! {span=>
324        #attrs
325        #[automatically_derived]
326        impl ::cxx::core::cmp::Ord for #ident {
327            fn cmp(&self, other: &Self) -> ::cxx::core::cmp::Ordering {
328                ::cxx::core::cmp::Ord::cmp(&self.repr, &other.repr)
329            }
330        }
331    }
332}
333
334fn enum_partial_ord(enm: &Enum, span: Span) -> TokenStream {
335    let ident = &enm.name.rust;
336    let attrs = &enm.attrs;
337
338    quote_spanned! {span=>
339        #attrs
340        #[automatically_derived]
341        impl ::cxx::core::cmp::PartialOrd for #ident {
342            #[allow(clippy::non_canonical_partial_ord_impl)]
343            fn partial_cmp(&self, other: &Self) -> ::cxx::core::option::Option<::cxx::core::cmp::Ordering> {
344                ::cxx::core::cmp::PartialOrd::partial_cmp(&self.repr, &other.repr)
345            }
346        }
347    }
348}