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