• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use crate::syntax::{derive, Enum, Struct, Trait};
2 use proc_macro2::{Ident, Span, TokenStream};
3 use quote::{quote, quote_spanned, ToTokens};
4 
5 pub use crate::syntax::derive::*;
6 
expand_struct(strct: &Struct, actual_derives: &mut Option<TokenStream>) -> TokenStream7 pub fn expand_struct(strct: &Struct, actual_derives: &mut Option<TokenStream>) -> TokenStream {
8     let mut expanded = TokenStream::new();
9     let mut traits = Vec::new();
10 
11     for derive in &strct.derives {
12         let span = derive.span;
13         match derive.what {
14             Trait::Copy => expanded.extend(struct_copy(strct, span)),
15             Trait::Clone => expanded.extend(struct_clone(strct, span)),
16             Trait::Debug => expanded.extend(struct_debug(strct, span)),
17             Trait::Default => expanded.extend(struct_default(strct, span)),
18             Trait::Eq => traits.push(quote_spanned!(span=> ::cxx::core::cmp::Eq)),
19             Trait::ExternType => unreachable!(),
20             Trait::Hash => traits.push(quote_spanned!(span=> ::cxx::core::hash::Hash)),
21             Trait::Ord => expanded.extend(struct_ord(strct, span)),
22             Trait::PartialEq => traits.push(quote_spanned!(span=> ::cxx::core::cmp::PartialEq)),
23             Trait::PartialOrd => expanded.extend(struct_partial_ord(strct, span)),
24             Trait::Serialize => traits.push(quote_spanned!(span=> ::serde::Serialize)),
25             Trait::Deserialize => traits.push(quote_spanned!(span=> ::serde::Deserialize)),
26         }
27     }
28 
29     if traits.is_empty() {
30         *actual_derives = None;
31     } else {
32         *actual_derives = Some(quote!(#[derive(#(#traits),*)]));
33     }
34 
35     expanded
36 }
37 
expand_enum(enm: &Enum, actual_derives: &mut Option<TokenStream>) -> TokenStream38 pub fn expand_enum(enm: &Enum, actual_derives: &mut Option<TokenStream>) -> TokenStream {
39     let mut expanded = TokenStream::new();
40     let mut traits = Vec::new();
41     let mut has_copy = false;
42     let mut has_clone = false;
43     let mut has_eq = false;
44     let mut has_partial_eq = false;
45 
46     for derive in &enm.derives {
47         let span = derive.span;
48         match derive.what {
49             Trait::Copy => {
50                 expanded.extend(enum_copy(enm, span));
51                 has_copy = true;
52             }
53             Trait::Clone => {
54                 expanded.extend(enum_clone(enm, span));
55                 has_clone = true;
56             }
57             Trait::Debug => expanded.extend(enum_debug(enm, span)),
58             Trait::Default => unreachable!(),
59             Trait::Eq => {
60                 traits.push(quote_spanned!(span=> ::cxx::core::cmp::Eq));
61                 has_eq = true;
62             }
63             Trait::ExternType => unreachable!(),
64             Trait::Hash => traits.push(quote_spanned!(span=> ::cxx::core::hash::Hash)),
65             Trait::Ord => expanded.extend(enum_ord(enm, span)),
66             Trait::PartialEq => {
67                 traits.push(quote_spanned!(span=> ::cxx::core::cmp::PartialEq));
68                 has_partial_eq = true;
69             }
70             Trait::PartialOrd => expanded.extend(enum_partial_ord(enm, span)),
71             Trait::Serialize => traits.push(quote_spanned!(span=> ::serde::Serialize)),
72             Trait::Deserialize => traits.push(quote_spanned!(span=> ::serde::Deserialize)),
73         }
74     }
75 
76     let span = enm.name.rust.span();
77     if !has_copy {
78         expanded.extend(enum_copy(enm, span));
79     }
80     if !has_clone {
81         expanded.extend(enum_clone(enm, span));
82     }
83     if !has_eq {
84         // Required to be derived in order for the enum's "variants" to be
85         // usable in patterns.
86         traits.push(quote!(::cxx::core::cmp::Eq));
87     }
88     if !has_partial_eq {
89         traits.push(quote!(::cxx::core::cmp::PartialEq));
90     }
91 
92     *actual_derives = Some(quote!(#[derive(#(#traits),*)]));
93 
94     expanded
95 }
96 
struct_copy(strct: &Struct, span: Span) -> TokenStream97 fn struct_copy(strct: &Struct, span: Span) -> TokenStream {
98     let ident = &strct.name.rust;
99     let generics = &strct.generics;
100 
101     quote_spanned! {span=>
102         impl #generics ::cxx::core::marker::Copy for #ident #generics {}
103     }
104 }
105 
struct_clone(strct: &Struct, span: Span) -> TokenStream106 fn struct_clone(strct: &Struct, span: Span) -> TokenStream {
107     let ident = &strct.name.rust;
108     let generics = &strct.generics;
109 
110     let body = if derive::contains(&strct.derives, Trait::Copy) {
111         quote!(*self)
112     } else {
113         let fields = strct.fields.iter().map(|field| &field.name.rust);
114         let values = strct.fields.iter().map(|field| {
115             let ident = &field.name.rust;
116             let ty = field.ty.to_token_stream();
117             let span = ty.into_iter().last().unwrap().span();
118             quote_spanned!(span=> &self.#ident)
119         });
120         quote_spanned!(span=> #ident {
121             #(#fields: ::cxx::core::clone::Clone::clone(#values),)*
122         })
123     };
124 
125     quote_spanned! {span=>
126         #[allow(clippy::expl_impl_clone_on_copy)]
127         impl #generics ::cxx::core::clone::Clone for #ident #generics {
128             fn clone(&self) -> Self {
129                 #body
130             }
131         }
132     }
133 }
134 
struct_debug(strct: &Struct, span: Span) -> TokenStream135 fn struct_debug(strct: &Struct, span: Span) -> TokenStream {
136     let ident = &strct.name.rust;
137     let generics = &strct.generics;
138     let struct_name = ident.to_string();
139     let fields = strct.fields.iter().map(|field| &field.name.rust);
140     let field_names = fields.clone().map(Ident::to_string);
141 
142     quote_spanned! {span=>
143         impl #generics ::cxx::core::fmt::Debug for #ident #generics {
144             fn fmt(&self, formatter: &mut ::cxx::core::fmt::Formatter<'_>) -> ::cxx::core::fmt::Result {
145                 formatter.debug_struct(#struct_name)
146                     #(.field(#field_names, &self.#fields))*
147                     .finish()
148             }
149         }
150     }
151 }
152 
struct_default(strct: &Struct, span: Span) -> TokenStream153 fn struct_default(strct: &Struct, span: Span) -> TokenStream {
154     let ident = &strct.name.rust;
155     let generics = &strct.generics;
156     let fields = strct.fields.iter().map(|field| &field.name.rust);
157 
158     quote_spanned! {span=>
159         #[allow(clippy::derivable_impls)] // different spans than the derived impl
160         impl #generics ::cxx::core::default::Default for #ident #generics {
161             fn default() -> Self {
162                 #ident {
163                     #(
164                         #fields: ::cxx::core::default::Default::default(),
165                     )*
166                 }
167             }
168         }
169     }
170 }
171 
struct_ord(strct: &Struct, span: Span) -> TokenStream172 fn struct_ord(strct: &Struct, span: Span) -> TokenStream {
173     let ident = &strct.name.rust;
174     let generics = &strct.generics;
175     let fields = strct.fields.iter().map(|field| &field.name.rust);
176 
177     quote_spanned! {span=>
178         impl #generics ::cxx::core::cmp::Ord for #ident #generics {
179             fn cmp(&self, other: &Self) -> ::cxx::core::cmp::Ordering {
180                 #(
181                     match ::cxx::core::cmp::Ord::cmp(&self.#fields, &other.#fields) {
182                         ::cxx::core::cmp::Ordering::Equal => {}
183                         ordering => return ordering,
184                     }
185                 )*
186                 ::cxx::core::cmp::Ordering::Equal
187             }
188         }
189     }
190 }
191 
struct_partial_ord(strct: &Struct, span: Span) -> TokenStream192 fn struct_partial_ord(strct: &Struct, span: Span) -> TokenStream {
193     let ident = &strct.name.rust;
194     let generics = &strct.generics;
195 
196     let body = if derive::contains(&strct.derives, Trait::Ord) {
197         quote! {
198             ::cxx::core::option::Option::Some(::cxx::core::cmp::Ord::cmp(self, other))
199         }
200     } else {
201         let fields = strct.fields.iter().map(|field| &field.name.rust);
202         quote! {
203             #(
204                 match ::cxx::core::cmp::PartialOrd::partial_cmp(&self.#fields, &other.#fields) {
205                     ::cxx::core::option::Option::Some(::cxx::core::cmp::Ordering::Equal) => {}
206                     ordering => return ordering,
207                 }
208             )*
209             ::cxx::core::option::Option::Some(::cxx::core::cmp::Ordering::Equal)
210         }
211     };
212 
213     quote_spanned! {span=>
214         impl #generics ::cxx::core::cmp::PartialOrd for #ident #generics {
215             fn partial_cmp(&self, other: &Self) -> ::cxx::core::option::Option<::cxx::core::cmp::Ordering> {
216                 #body
217             }
218         }
219     }
220 }
221 
enum_copy(enm: &Enum, span: Span) -> TokenStream222 fn enum_copy(enm: &Enum, span: Span) -> TokenStream {
223     let ident = &enm.name.rust;
224 
225     quote_spanned! {span=>
226         impl ::cxx::core::marker::Copy for #ident {}
227     }
228 }
229 
enum_clone(enm: &Enum, span: Span) -> TokenStream230 fn enum_clone(enm: &Enum, span: Span) -> TokenStream {
231     let ident = &enm.name.rust;
232 
233     quote_spanned! {span=>
234         #[allow(clippy::expl_impl_clone_on_copy)]
235         impl ::cxx::core::clone::Clone for #ident {
236             fn clone(&self) -> Self {
237                 *self
238             }
239         }
240     }
241 }
242 
enum_debug(enm: &Enum, span: Span) -> TokenStream243 fn enum_debug(enm: &Enum, span: Span) -> TokenStream {
244     let ident = &enm.name.rust;
245     let variants = enm.variants.iter().map(|variant| {
246         let variant = &variant.name.rust;
247         let name = variant.to_string();
248         quote_spanned! {span=>
249             #ident::#variant => formatter.write_str(#name),
250         }
251     });
252     let fallback = format!("{}({{}})", ident);
253 
254     quote_spanned! {span=>
255         impl ::cxx::core::fmt::Debug for #ident {
256             fn fmt(&self, formatter: &mut ::cxx::core::fmt::Formatter<'_>) -> ::cxx::core::fmt::Result {
257                 match *self {
258                     #(#variants)*
259                     _ => ::cxx::core::write!(formatter, #fallback, self.repr),
260                 }
261             }
262         }
263     }
264 }
265 
enum_ord(enm: &Enum, span: Span) -> TokenStream266 fn enum_ord(enm: &Enum, span: Span) -> TokenStream {
267     let ident = &enm.name.rust;
268 
269     quote_spanned! {span=>
270         impl ::cxx::core::cmp::Ord for #ident {
271             fn cmp(&self, other: &Self) -> ::cxx::core::cmp::Ordering {
272                 ::cxx::core::cmp::Ord::cmp(&self.repr, &other.repr)
273             }
274         }
275     }
276 }
277 
enum_partial_ord(enm: &Enum, span: Span) -> TokenStream278 fn enum_partial_ord(enm: &Enum, span: Span) -> TokenStream {
279     let ident = &enm.name.rust;
280 
281     quote_spanned! {span=>
282         impl ::cxx::core::cmp::PartialOrd for #ident {
283             fn partial_cmp(&self, other: &Self) -> ::cxx::core::option::Option<::cxx::core::cmp::Ordering> {
284                 ::cxx::core::cmp::PartialOrd::partial_cmp(&self.repr, &other.repr)
285             }
286         }
287     }
288 }
289