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 => 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 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)] 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)] 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)] 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}