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