blob: 28dfae3d1a967c64438d7708b92f1af31bfb0f63 [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)),
18 Trait::PartialEq => traits.push(quote_spanned!(span=> ::std::cmp::PartialEq)),
David Tolnay2c24e5c2020-11-27 12:47:41 -080019 }
20 }
21
David Tolnay21466df2020-11-27 14:04:42 -080022 if traits.is_empty() {
23 *actual_derives = None;
24 } else {
25 *actual_derives = Some(quote!(#[derive(#(#traits),*)]));
26 }
David Tolnaya3619e92020-11-27 14:13:15 -080027
David Tolnay2c24e5c2020-11-27 12:47:41 -080028 expanded
29}
30
David Tolnayb3d7bb12020-11-27 14:11:41 -080031pub fn expand_enum(enm: &Enum, actual_derives: &mut Option<TokenStream>) -> TokenStream {
David Tolnayfbc46692020-11-27 11:33:29 -080032 let mut expanded = TokenStream::new();
David Tolnay21466df2020-11-27 14:04:42 -080033 let mut traits = Vec::new();
David Tolnayfbc46692020-11-27 11:33:29 -080034 let mut has_copy = false;
35 let mut has_clone = false;
David Tolnay21466df2020-11-27 14:04:42 -080036 let mut has_eq = false;
37 let mut has_partial_eq = false;
David Tolnayfbc46692020-11-27 11:33:29 -080038
39 for derive in &enm.derives {
40 let span = derive.span;
41 match derive.what {
42 Trait::Copy => {
43 expanded.extend(enum_copy(enm, span));
44 has_copy = true;
45 }
46 Trait::Clone => {
47 expanded.extend(enum_clone(enm, span));
48 has_clone = true;
49 }
David Tolnayf84c98b2020-11-27 12:59:42 -080050 Trait::Debug => expanded.extend(enum_debug(enm, span)),
David Tolnay21466df2020-11-27 14:04:42 -080051 Trait::Eq => {
52 traits.push(quote_spanned!(span=> ::std::cmp::Eq));
53 has_eq = true;
54 }
55 Trait::PartialEq => {
56 traits.push(quote_spanned!(span=> ::std::cmp::PartialEq));
57 has_partial_eq = true;
58 }
David Tolnayfbc46692020-11-27 11:33:29 -080059 }
60 }
61
62 let span = enm.name.rust.span();
63 if !has_copy {
64 expanded.extend(enum_copy(enm, span));
65 }
66 if !has_clone {
67 expanded.extend(enum_clone(enm, span));
68 }
David Tolnay21466df2020-11-27 14:04:42 -080069 if !has_eq {
David Tolnayb3d7bb12020-11-27 14:11:41 -080070 // Required to be derived in order for the enum's "variants" to be
71 // usable in patterns.
David Tolnay21466df2020-11-27 14:04:42 -080072 traits.push(quote!(::std::cmp::Eq));
73 }
74 if !has_partial_eq {
75 traits.push(quote!(::std::cmp::PartialEq));
76 }
77
78 *actual_derives = Some(quote!(#[derive(#(#traits),*)]));
David Tolnayb3d7bb12020-11-27 14:11:41 -080079
David Tolnayfbc46692020-11-27 11:33:29 -080080 expanded
81}
82
David Tolnay2c24e5c2020-11-27 12:47:41 -080083fn struct_copy(strct: &Struct, span: Span) -> TokenStream {
84 let ident = &strct.name.rust;
85
86 quote_spanned! {span=>
87 impl ::std::marker::Copy for #ident {}
88 }
89}
90
91fn struct_clone(strct: &Struct, span: Span) -> TokenStream {
92 let ident = &strct.name.rust;
93
David Tolnaybc047bb2020-11-27 14:30:12 -080094 let body = if derive::contains(&strct.derives, Trait::Copy) {
David Tolnay2c24e5c2020-11-27 12:47:41 -080095 quote!(*self)
96 } else {
97 let fields = strct.fields.iter().map(|field| &field.ident);
98 let values = strct.fields.iter().map(|field| {
99 let ident = &field.ident;
100 let ty = field.ty.to_token_stream();
101 let span = ty.into_iter().last().unwrap().span();
102 quote_spanned!(span=> &self.#ident)
103 });
104 quote_spanned!(span=> #ident {
105 #(#fields: ::std::clone::Clone::clone(#values),)*
106 })
107 };
108
109 quote_spanned! {span=>
110 impl ::std::clone::Clone for #ident {
111 fn clone(&self) -> Self {
112 #body
David Tolnayd8ad9702020-11-27 12:43:59 -0800113 }
114 }
115 }
David Tolnayd8ad9702020-11-27 12:43:59 -0800116}
David Tolnayfbc46692020-11-27 11:33:29 -0800117
David Tolnayf84c98b2020-11-27 12:59:42 -0800118fn struct_debug(strct: &Struct, span: Span) -> TokenStream {
119 let ident = &strct.name.rust;
120 let struct_name = ident.to_string();
121 let fields = strct.fields.iter().map(|field| &field.ident);
122 let field_names = fields.clone().map(Ident::to_string);
123
124 quote_spanned! {span=>
125 impl ::std::fmt::Debug for #ident {
126 fn fmt(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
127 formatter.debug_struct(#struct_name)
128 #(.field(#field_names, &self.#fields))*
129 .finish()
130 }
131 }
132 }
133}
134
David Tolnayfbc46692020-11-27 11:33:29 -0800135fn enum_copy(enm: &Enum, span: Span) -> TokenStream {
136 let ident = &enm.name.rust;
137
138 quote_spanned! {span=>
139 impl ::std::marker::Copy for #ident {}
140 }
141}
142
143fn enum_clone(enm: &Enum, span: Span) -> TokenStream {
144 let ident = &enm.name.rust;
145
146 quote_spanned! {span=>
147 impl ::std::clone::Clone for #ident {
148 fn clone(&self) -> Self {
149 *self
150 }
151 }
152 }
153}
David Tolnayf84c98b2020-11-27 12:59:42 -0800154
155fn enum_debug(enm: &Enum, span: Span) -> TokenStream {
156 let ident = &enm.name.rust;
157 let variants = enm.variants.iter().map(|variant| {
158 let variant = &variant.ident;
159 let name = variant.to_string();
160 quote_spanned! {span=>
161 #ident::#variant => formatter.write_str(#name),
162 }
163 });
164 let fallback = format!("{}({{}})", ident);
165
166 quote_spanned! {span=>
167 impl ::std::fmt::Debug for #ident {
168 fn fmt(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
169 match *self {
170 #(#variants)*
171 _ => ::std::write!(formatter, #fallback, self.repr),
172 }
173 }
174 }
175 }
176}