cxxbridge_macro/
derive.rs1use 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 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)] 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}