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::BitAnd => unreachable!(),
18 Trait::BitOr => unreachable!(),
19 Trait::BitXor => unreachable!(),
20 Trait::Copy => expanded.extend(struct_copy(strct, span)),
21 Trait::Clone => expanded.extend(struct_clone(strct, span)),
22 Trait::Debug => expanded.extend(struct_debug(strct, span)),
23 Trait::Default => expanded.extend(struct_default(strct, span)),
24 Trait::Eq => traits.push(quote_spanned!(span=> ::cxx::core::cmp::Eq)),
25 Trait::ExternType => unreachable!(),
26 Trait::Hash => traits.push(quote_spanned!(span=> ::cxx::core::hash::Hash)),
27 Trait::Ord => expanded.extend(struct_ord(strct, span)),
28 Trait::PartialEq => traits.push(quote_spanned!(span=> ::cxx::core::cmp::PartialEq)),
29 Trait::PartialOrd => expanded.extend(struct_partial_ord(strct, span)),
30 Trait::Serialize => traits.push(quote_spanned!(span=> ::serde::Serialize)),
31 Trait::Deserialize => traits.push(quote_spanned!(span=> ::serde::Deserialize)),
32 }
33 }
34
35 if traits.is_empty() {
36 *actual_derives = None;
37 } else {
38 *actual_derives = Some(quote!(#[derive(#(#traits),*)]));
39 }
40
41 expanded
42}
43
44pub(crate) fn expand_enum(enm: &Enum, actual_derives: &mut Option<TokenStream>) -> TokenStream {
45 let mut expanded = TokenStream::new();
46 let mut traits = Vec::new();
47 let mut has_copy = false;
48 let mut has_clone = false;
49 let mut has_eq = false;
50 let mut has_partial_eq = false;
51
52 for derive in &enm.derives {
53 let span = derive.span;
54 match derive.what {
55 Trait::BitAnd => expanded.extend(enum_bitand(enm, span)),
56 Trait::BitOr => expanded.extend(enum_bitor(enm, span)),
57 Trait::BitXor => expanded.extend(enum_bitxor(enm, span)),
58 Trait::Copy => {
59 expanded.extend(enum_copy(enm, span));
60 has_copy = true;
61 }
62 Trait::Clone => {
63 expanded.extend(enum_clone(enm, span));
64 has_clone = true;
65 }
66 Trait::Debug => expanded.extend(enum_debug(enm, span)),
67 Trait::Default => expanded.extend(enum_default(enm, span)),
68 Trait::Eq => {
69 traits.push(quote_spanned!(span=> ::cxx::core::cmp::Eq));
70 has_eq = true;
71 }
72 Trait::ExternType => unreachable!(),
73 Trait::Hash => traits.push(quote_spanned!(span=> ::cxx::core::hash::Hash)),
74 Trait::Ord => expanded.extend(enum_ord(enm, span)),
75 Trait::PartialEq => {
76 traits.push(quote_spanned!(span=> ::cxx::core::cmp::PartialEq));
77 has_partial_eq = true;
78 }
79 Trait::PartialOrd => expanded.extend(enum_partial_ord(enm, span)),
80 Trait::Serialize => traits.push(quote_spanned!(span=> ::serde::Serialize)),
81 Trait::Deserialize => traits.push(quote_spanned!(span=> ::serde::Deserialize)),
82 }
83 }
84
85 let span = enm.name.rust.span();
86 if !has_copy {
87 expanded.extend(enum_copy(enm, span));
88 }
89 if !has_clone {
90 expanded.extend(enum_clone(enm, span));
91 }
92 if !has_eq {
93 traits.push(quote!(::cxx::core::cmp::Eq));
96 }
97 if !has_partial_eq {
98 traits.push(quote!(::cxx::core::cmp::PartialEq));
99 }
100
101 *actual_derives = Some(quote!(#[derive(#(#traits),*)]));
102
103 expanded
104}
105
106fn struct_copy(strct: &Struct, span: Span) -> TokenStream {
107 let ident = &strct.name.rust;
108 let generics = &strct.generics;
109 let cfg_and_lint_attrs = strct.attrs.cfg_and_lint();
110
111 quote_spanned! {span=>
112 #cfg_and_lint_attrs
113 #[automatically_derived]
114 impl #generics ::cxx::core::marker::Copy for #ident #generics {}
115 }
116}
117
118fn struct_clone(strct: &Struct, span: Span) -> TokenStream {
119 let ident = &strct.name.rust;
120 let generics = &strct.generics;
121 let cfg_and_lint_attrs = strct.attrs.cfg_and_lint();
122
123 let body = if derive::contains(&strct.derives, Trait::Copy) {
124 quote!(*self)
125 } else {
126 let fields = strct.fields.iter().map(|field| &field.name.rust);
127 let values = strct.fields.iter().map(|field| {
128 let ident = &field.name.rust;
129 let ty = field.ty.to_token_stream();
130 let span = ty.into_iter().last().unwrap().span();
131 quote_spanned!(span=> &self.#ident)
132 });
133 quote_spanned!(span=> #ident {
134 #(#fields: ::cxx::core::clone::Clone::clone(#values),)*
135 })
136 };
137
138 quote_spanned! {span=>
139 #cfg_and_lint_attrs
140 #[automatically_derived]
141 #[allow(clippy::expl_impl_clone_on_copy)]
142 impl #generics ::cxx::core::clone::Clone for #ident #generics {
143 fn clone(&self) -> Self {
144 #body
145 }
146 }
147 }
148}
149
150fn struct_debug(strct: &Struct, span: Span) -> TokenStream {
151 let ident = &strct.name.rust;
152 let generics = &strct.generics;
153 let cfg_and_lint_attrs = strct.attrs.cfg_and_lint();
154 let struct_name = ident.to_string();
155 let fields = strct.fields.iter().map(|field| &field.name.rust);
156 let field_names = fields.clone().map(Ident::to_string);
157
158 quote_spanned! {span=>
159 #cfg_and_lint_attrs
160 #[automatically_derived]
161 impl #generics ::cxx::core::fmt::Debug for #ident #generics {
162 fn fmt(&self, formatter: &mut ::cxx::core::fmt::Formatter<'_>) -> ::cxx::core::fmt::Result {
163 formatter.debug_struct(#struct_name)
164 #(.field(#field_names, &self.#fields))*
165 .finish()
166 }
167 }
168 }
169}
170
171fn struct_default(strct: &Struct, span: Span) -> TokenStream {
172 let ident = &strct.name.rust;
173 let generics = &strct.generics;
174 let cfg_and_lint_attrs = strct.attrs.cfg_and_lint();
175 let fields = strct.fields.iter().map(|field| &field.name.rust);
176
177 quote_spanned! {span=>
178 #cfg_and_lint_attrs
179 #[automatically_derived]
180 #[allow(clippy::derivable_impls)] impl #generics ::cxx::core::default::Default for #ident #generics {
182 fn default() -> Self {
183 #ident {
184 #(
185 #fields: ::cxx::core::default::Default::default(),
186 )*
187 }
188 }
189 }
190 }
191}
192
193fn struct_ord(strct: &Struct, span: Span) -> TokenStream {
194 let ident = &strct.name.rust;
195 let generics = &strct.generics;
196 let cfg_and_lint_attrs = strct.attrs.cfg_and_lint();
197 let fields = strct.fields.iter().map(|field| &field.name.rust);
198
199 quote_spanned! {span=>
200 #cfg_and_lint_attrs
201 #[automatically_derived]
202 impl #generics ::cxx::core::cmp::Ord for #ident #generics {
203 fn cmp(&self, other: &Self) -> ::cxx::core::cmp::Ordering {
204 #(
205 match ::cxx::core::cmp::Ord::cmp(&self.#fields, &other.#fields) {
206 ::cxx::core::cmp::Ordering::Equal => {}
207 ordering => return ordering,
208 }
209 )*
210 ::cxx::core::cmp::Ordering::Equal
211 }
212 }
213 }
214}
215
216fn struct_partial_ord(strct: &Struct, span: Span) -> TokenStream {
217 let ident = &strct.name.rust;
218 let generics = &strct.generics;
219 let cfg_and_lint_attrs = strct.attrs.cfg_and_lint();
220
221 let body = if derive::contains(&strct.derives, Trait::Ord) {
222 quote! {
223 ::cxx::core::option::Option::Some(::cxx::core::cmp::Ord::cmp(self, other))
224 }
225 } else {
226 let fields = strct.fields.iter().map(|field| &field.name.rust);
227 quote! {
228 #(
229 match ::cxx::core::cmp::PartialOrd::partial_cmp(&self.#fields, &other.#fields) {
230 ::cxx::core::option::Option::Some(::cxx::core::cmp::Ordering::Equal) => {}
231 ordering => return ordering,
232 }
233 )*
234 ::cxx::core::option::Option::Some(::cxx::core::cmp::Ordering::Equal)
235 }
236 };
237
238 quote_spanned! {span=>
239 #cfg_and_lint_attrs
240 #[automatically_derived]
241 impl #generics ::cxx::core::cmp::PartialOrd for #ident #generics {
242 #[allow(clippy::non_canonical_partial_ord_impl)]
243 fn partial_cmp(&self, other: &Self) -> ::cxx::core::option::Option<::cxx::core::cmp::Ordering> {
244 #body
245 }
246 }
247 }
248}
249
250fn enum_bitand(enm: &Enum, span: Span) -> TokenStream {
251 let ident = &enm.name.rust;
252 let cfg_and_lint_attrs = enm.attrs.cfg_and_lint();
253
254 quote_spanned! {span=>
255 #cfg_and_lint_attrs
256 #[automatically_derived]
257 impl ::cxx::core::ops::BitAnd for #ident {
258 type Output = #ident;
259 fn bitand(self, rhs: Self) -> Self::Output {
260 #ident {
261 repr: self.repr & rhs.repr,
262 }
263 }
264 }
265 }
266}
267
268fn enum_bitor(enm: &Enum, span: Span) -> TokenStream {
269 let ident = &enm.name.rust;
270 let cfg_and_lint_attrs = enm.attrs.cfg_and_lint();
271
272 quote_spanned! {span=>
273 #cfg_and_lint_attrs
274 #[automatically_derived]
275 impl ::cxx::core::ops::BitOr for #ident {
276 type Output = #ident;
277 fn bitor(self, rhs: Self) -> Self::Output {
278 #ident {
279 repr: self.repr | rhs.repr,
280 }
281 }
282 }
283 }
284}
285
286fn enum_bitxor(enm: &Enum, span: Span) -> TokenStream {
287 let ident = &enm.name.rust;
288 let cfg_and_lint_attrs = enm.attrs.cfg_and_lint();
289
290 quote_spanned! {span=>
291 #cfg_and_lint_attrs
292 #[automatically_derived]
293 impl ::cxx::core::ops::BitXor for #ident {
294 type Output = #ident;
295 fn bitxor(self, rhs: Self) -> Self::Output {
296 #ident {
297 repr: self.repr ^ rhs.repr,
298 }
299 }
300 }
301 }
302}
303
304fn enum_copy(enm: &Enum, span: Span) -> TokenStream {
305 let ident = &enm.name.rust;
306 let cfg_and_lint_attrs = enm.attrs.cfg_and_lint();
307
308 quote_spanned! {span=>
309 #cfg_and_lint_attrs
310 #[automatically_derived]
311 impl ::cxx::core::marker::Copy for #ident {}
312 }
313}
314
315fn enum_clone(enm: &Enum, span: Span) -> TokenStream {
316 let ident = &enm.name.rust;
317 let cfg_and_lint_attrs = enm.attrs.cfg_and_lint();
318
319 quote_spanned! {span=>
320 #cfg_and_lint_attrs
321 #[automatically_derived]
322 #[allow(clippy::expl_impl_clone_on_copy)]
323 impl ::cxx::core::clone::Clone for #ident {
324 fn clone(&self) -> Self {
325 *self
326 }
327 }
328 }
329}
330
331fn enum_debug(enm: &Enum, span: Span) -> TokenStream {
332 let ident = &enm.name.rust;
333 let cfg_and_lint_attrs = enm.attrs.cfg_and_lint();
334 let variants = enm.variants.iter().map(|variant| {
335 let variant = &variant.name.rust;
336 let name = variant.to_string();
337 quote_spanned! {span=>
338 #ident::#variant => formatter.write_str(#name),
339 }
340 });
341 let fallback = format!("{}({{}})", ident);
342
343 quote_spanned! {span=>
344 #cfg_and_lint_attrs
345 #[automatically_derived]
346 impl ::cxx::core::fmt::Debug for #ident {
347 fn fmt(&self, formatter: &mut ::cxx::core::fmt::Formatter<'_>) -> ::cxx::core::fmt::Result {
348 match *self {
349 #(#variants)*
350 _ => ::cxx::core::write!(formatter, #fallback, self.repr),
351 }
352 }
353 }
354 }
355}
356
357fn enum_default(enm: &Enum, span: Span) -> TokenStream {
358 let ident = &enm.name.rust;
359 let cfg_and_lint_attrs = enm.attrs.cfg_and_lint();
360
361 for variant in &enm.variants {
362 if variant.default {
363 let variant = &variant.name.rust;
364 return quote_spanned! {span=>
365 #cfg_and_lint_attrs
366 #[automatically_derived]
367 impl ::cxx::core::default::Default for #ident {
368 fn default() -> Self {
369 #ident::#variant
370 }
371 }
372 };
373 }
374 }
375
376 unreachable!("no #[default] variant");
377}
378
379fn enum_ord(enm: &Enum, span: Span) -> TokenStream {
380 let ident = &enm.name.rust;
381 let cfg_and_lint_attrs = enm.attrs.cfg_and_lint();
382
383 quote_spanned! {span=>
384 #cfg_and_lint_attrs
385 #[automatically_derived]
386 impl ::cxx::core::cmp::Ord for #ident {
387 fn cmp(&self, other: &Self) -> ::cxx::core::cmp::Ordering {
388 ::cxx::core::cmp::Ord::cmp(&self.repr, &other.repr)
389 }
390 }
391 }
392}
393
394fn enum_partial_ord(enm: &Enum, span: Span) -> TokenStream {
395 let ident = &enm.name.rust;
396 let cfg_and_lint_attrs = enm.attrs.cfg_and_lint();
397
398 quote_spanned! {span=>
399 #cfg_and_lint_attrs
400 #[automatically_derived]
401 impl ::cxx::core::cmp::PartialOrd for #ident {
402 #[allow(clippy::non_canonical_partial_ord_impl)]
403 fn partial_cmp(&self, other: &Self) -> ::cxx::core::option::Option<::cxx::core::cmp::Ordering> {
404 ::cxx::core::cmp::PartialOrd::partial_cmp(&self.repr, &other.repr)
405 }
406 }
407 }
408}