blob: 91a5d9dc9c1af67a674849afe738d3ae9521a4c4 [file] [log] [blame]
David Tolnaybc047bb2020-11-27 14:30:12 -08001use crate::syntax::{derive, Enum, Struct, Trait};
David Tolnayf84c98b2020-11-27 12:59:42 -08002use proc_macro2::{Ident, Span, TokenStream};
David Tolnayd8ad9702020-11-27 12:43:59 -08003use quote::{quote, quote_spanned, ToTokens};
4
David Tolnayb960ed22020-11-27 14:34:30 -08005pub use crate::syntax::derive::*;
6
David Tolnaya3619e92020-11-27 14:13:15 -08007pub fn expand_struct(strct: &Struct, actual_derives: &mut Option<TokenStream>) -> TokenStream {
David Tolnayd8ad9702020-11-27 12:43:59 -08008 let mut expanded = TokenStream::new();
David Tolnay21466df2020-11-27 14:04:42 -08009 let mut traits = Vec::new();
David Tolnayd8ad9702020-11-27 12:43:59 -080010
David Tolnay2c24e5c2020-11-27 12:47:41 -080011 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)),
David Tolnayf84c98b2020-11-27 12:59:42 -080016 Trait::Debug => expanded.extend(struct_debug(strct, span)),
David Tolnay21466df2020-11-27 14:04:42 -080017 Trait::Eq => traits.push(quote_spanned!(span=> ::std::cmp::Eq)),
David Tolnay577135e2020-11-27 16:23:53 -080018 Trait::Ord => expanded.extend(struct_ord(strct, span)),
David Tolnay21466df2020-11-27 14:04:42 -080019 Trait::PartialEq => traits.push(quote_spanned!(span=> ::std::cmp::PartialEq)),
David Tolnay577135e2020-11-27 16:23:53 -080020 Trait::PartialOrd => expanded.extend(struct_partial_ord(strct, span)),
David Tolnay2c24e5c2020-11-27 12:47:41 -080021 }
22 }
23
David Tolnay21466df2020-11-27 14:04:42 -080024 if traits.is_empty() {
25 *actual_derives = None;
26 } else {
27 *actual_derives = Some(quote!(#[derive(#(#traits),*)]));
28 }
David Tolnaya3619e92020-11-27 14:13:15 -080029
David Tolnay2c24e5c2020-11-27 12:47:41 -080030 expanded
31}
32
David Tolnayb3d7bb12020-11-27 14:11:41 -080033pub fn expand_enum(enm: &Enum, actual_derives: &mut Option<TokenStream>) -> TokenStream {
David Tolnayfbc46692020-11-27 11:33:29 -080034 let mut expanded = TokenStream::new();
David Tolnay21466df2020-11-27 14:04:42 -080035 let mut traits = Vec::new();
David Tolnayfbc46692020-11-27 11:33:29 -080036 let mut has_copy = false;
37 let mut has_clone = false;
David Tolnay21466df2020-11-27 14:04:42 -080038 let mut has_eq = false;
39 let mut has_partial_eq = false;
David Tolnayfbc46692020-11-27 11:33:29 -080040
41 for derive in &enm.derives {
42 let span = derive.span;
43 match derive.what {
44 Trait::Copy => {
45 expanded.extend(enum_copy(enm, span));
46 has_copy = true;
47 }
48 Trait::Clone => {
49 expanded.extend(enum_clone(enm, span));
50 has_clone = true;
51 }
David Tolnayf84c98b2020-11-27 12:59:42 -080052 Trait::Debug => expanded.extend(enum_debug(enm, span)),
David Tolnay21466df2020-11-27 14:04:42 -080053 Trait::Eq => {
54 traits.push(quote_spanned!(span=> ::std::cmp::Eq));
55 has_eq = true;
56 }
David Tolnay577135e2020-11-27 16:23:53 -080057 Trait::Ord => expanded.extend(enum_ord(enm, span)),
David Tolnay21466df2020-11-27 14:04:42 -080058 Trait::PartialEq => {
59 traits.push(quote_spanned!(span=> ::std::cmp::PartialEq));
60 has_partial_eq = true;
61 }
David Tolnay577135e2020-11-27 16:23:53 -080062 Trait::PartialOrd => expanded.extend(enum_partial_ord(enm, span)),
David Tolnayfbc46692020-11-27 11:33:29 -080063 }
64 }
65
66 let span = enm.name.rust.span();
67 if !has_copy {
68 expanded.extend(enum_copy(enm, span));
69 }
70 if !has_clone {
71 expanded.extend(enum_clone(enm, span));
72 }
David Tolnay21466df2020-11-27 14:04:42 -080073 if !has_eq {
David Tolnayb3d7bb12020-11-27 14:11:41 -080074 // Required to be derived in order for the enum's "variants" to be
75 // usable in patterns.
David Tolnay21466df2020-11-27 14:04:42 -080076 traits.push(quote!(::std::cmp::Eq));
77 }
78 if !has_partial_eq {
79 traits.push(quote!(::std::cmp::PartialEq));
80 }
81
82 *actual_derives = Some(quote!(#[derive(#(#traits),*)]));
David Tolnayb3d7bb12020-11-27 14:11:41 -080083
David Tolnayfbc46692020-11-27 11:33:29 -080084 expanded
85}
86
David Tolnay2c24e5c2020-11-27 12:47:41 -080087fn struct_copy(strct: &Struct, span: Span) -> TokenStream {
88 let ident = &strct.name.rust;
89
90 quote_spanned! {span=>
91 impl ::std::marker::Copy for #ident {}
92 }
93}
94
95fn struct_clone(strct: &Struct, span: Span) -> TokenStream {
96 let ident = &strct.name.rust;
97
David Tolnaybc047bb2020-11-27 14:30:12 -080098 let body = if derive::contains(&strct.derives, Trait::Copy) {
David Tolnay2c24e5c2020-11-27 12:47:41 -080099 quote!(*self)
100 } else {
101 let fields = strct.fields.iter().map(|field| &field.ident);
102 let values = strct.fields.iter().map(|field| {
103 let ident = &field.ident;
104 let ty = field.ty.to_token_stream();
105 let span = ty.into_iter().last().unwrap().span();
106 quote_spanned!(span=> &self.#ident)
107 });
108 quote_spanned!(span=> #ident {
109 #(#fields: ::std::clone::Clone::clone(#values),)*
110 })
111 };
112
113 quote_spanned! {span=>
114 impl ::std::clone::Clone for #ident {
115 fn clone(&self) -> Self {
116 #body
David Tolnayd8ad9702020-11-27 12:43:59 -0800117 }
118 }
119 }
David Tolnayd8ad9702020-11-27 12:43:59 -0800120}
David Tolnayfbc46692020-11-27 11:33:29 -0800121
David Tolnayf84c98b2020-11-27 12:59:42 -0800122fn struct_debug(strct: &Struct, span: Span) -> TokenStream {
123 let ident = &strct.name.rust;
124 let struct_name = ident.to_string();
125 let fields = strct.fields.iter().map(|field| &field.ident);
126 let field_names = fields.clone().map(Ident::to_string);
127
128 quote_spanned! {span=>
129 impl ::std::fmt::Debug for #ident {
130 fn fmt(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
131 formatter.debug_struct(#struct_name)
132 #(.field(#field_names, &self.#fields))*
133 .finish()
134 }
135 }
136 }
137}
138
David Tolnay577135e2020-11-27 16:23:53 -0800139fn struct_ord(strct: &Struct, span: Span) -> TokenStream {
140 let ident = &strct.name.rust;
141 let fields = strct.fields.iter().map(|field| &field.ident);
142
143 quote_spanned! {span=>
144 impl ::std::cmp::Ord for #ident {
145 fn cmp(&self, other: &Self) -> ::std::cmp::Ordering {
146 #(
147 match ::std::cmp::Ord::cmp(&self.#fields, &other.#fields) {
148 ::std::cmp::Ordering::Equal => {}
149 ordering => return ordering,
150 }
151 )*
152 ::std::cmp::Ordering::Equal
153 }
154 }
155 }
156}
157
158fn struct_partial_ord(strct: &Struct, span: Span) -> TokenStream {
159 let ident = &strct.name.rust;
160
161 let body = if derive::contains(&strct.derives, Trait::Ord) {
162 quote! {
163 ::std::option::Option::Some(::std::cmp::Ord::cmp(self, other))
164 }
165 } else {
166 let fields = strct.fields.iter().map(|field| &field.ident);
167 quote! {
168 #(
169 match ::std::cmp::PartialOrd::partial_cmp(&self.#fields, &other.#fields) {
170 ::std::option::Option::Some(::std::cmp::Ordering::Equal) => {}
171 ordering => return ordering,
172 }
173 )*
174 ::std::option::Option::Some(::std::cmp::Ordering::Equal)
175 }
176 };
177
178 quote_spanned! {span=>
179 impl ::std::cmp::PartialOrd for #ident {
180 fn partial_cmp(&self, other: &Self) -> ::std::option::Option<::std::cmp::Ordering> {
181 #body
182 }
183 }
184 }
185}
186
David Tolnayfbc46692020-11-27 11:33:29 -0800187fn enum_copy(enm: &Enum, span: Span) -> TokenStream {
188 let ident = &enm.name.rust;
189
190 quote_spanned! {span=>
191 impl ::std::marker::Copy for #ident {}
192 }
193}
194
195fn enum_clone(enm: &Enum, span: Span) -> TokenStream {
196 let ident = &enm.name.rust;
197
198 quote_spanned! {span=>
199 impl ::std::clone::Clone for #ident {
200 fn clone(&self) -> Self {
201 *self
202 }
203 }
204 }
205}
David Tolnayf84c98b2020-11-27 12:59:42 -0800206
207fn enum_debug(enm: &Enum, span: Span) -> TokenStream {
208 let ident = &enm.name.rust;
209 let variants = enm.variants.iter().map(|variant| {
210 let variant = &variant.ident;
211 let name = variant.to_string();
212 quote_spanned! {span=>
213 #ident::#variant => formatter.write_str(#name),
214 }
215 });
216 let fallback = format!("{}({{}})", ident);
217
218 quote_spanned! {span=>
219 impl ::std::fmt::Debug for #ident {
220 fn fmt(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
221 match *self {
222 #(#variants)*
223 _ => ::std::write!(formatter, #fallback, self.repr),
224 }
225 }
226 }
227 }
228}
David Tolnay577135e2020-11-27 16:23:53 -0800229
230fn enum_ord(enm: &Enum, span: Span) -> TokenStream {
231 let ident = &enm.name.rust;
232
233 quote_spanned! {span=>
234 impl ::std::cmp::Ord for #ident {
235 pub fn cmp(&self, other: &Self) -> ::std::cmp::Ordering {
236 ::std::cmp::Ord::cmp(&self.repr, &other.repr)
237 }
238 }
239 }
240}
241
242fn enum_partial_ord(enm: &Enum, span: Span) -> TokenStream {
243 let ident = &enm.name.rust;
244
245 quote_spanned! {span=>
246 impl ::std::cmp::PartialOrd for #ident {
247 pub fn cmp(&self, other: &Self) -> ::std::option::Option<::std::cmp::Ordering> {
248 ::std::cmp::PartialOrd::cmp(&self.repr, &other.repr)
249 }
250 }
251 }
252}