blob: 91d4c641973bb41a9385a7b61add4e8f65cbc290 [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 Tolnaya3619e92020-11-27 14:13:15 -08005pub fn expand_struct(strct: &Struct, actual_derives: &mut Option<TokenStream>) -> TokenStream {
David Tolnayd8ad9702020-11-27 12:43:59 -08006 let mut expanded = TokenStream::new();
David Tolnay21466df2020-11-27 14:04:42 -08007 let mut traits = Vec::new();
David Tolnayd8ad9702020-11-27 12:43:59 -08008
David Tolnay2c24e5c2020-11-27 12:47:41 -08009 for derive in &strct.derives {
10 let span = derive.span;
11 match derive.what {
12 Trait::Copy => expanded.extend(struct_copy(strct, span)),
13 Trait::Clone => expanded.extend(struct_clone(strct, span)),
David Tolnayf84c98b2020-11-27 12:59:42 -080014 Trait::Debug => expanded.extend(struct_debug(strct, span)),
David Tolnay21466df2020-11-27 14:04:42 -080015 Trait::Eq => traits.push(quote_spanned!(span=> ::std::cmp::Eq)),
16 Trait::PartialEq => traits.push(quote_spanned!(span=> ::std::cmp::PartialEq)),
David Tolnay2c24e5c2020-11-27 12:47:41 -080017 }
18 }
19
David Tolnay21466df2020-11-27 14:04:42 -080020 if traits.is_empty() {
21 *actual_derives = None;
22 } else {
23 *actual_derives = Some(quote!(#[derive(#(#traits),*)]));
24 }
David Tolnaya3619e92020-11-27 14:13:15 -080025
David Tolnay2c24e5c2020-11-27 12:47:41 -080026 expanded
27}
28
David Tolnayb3d7bb12020-11-27 14:11:41 -080029pub fn expand_enum(enm: &Enum, actual_derives: &mut Option<TokenStream>) -> TokenStream {
David Tolnayfbc46692020-11-27 11:33:29 -080030 let mut expanded = TokenStream::new();
David Tolnay21466df2020-11-27 14:04:42 -080031 let mut traits = Vec::new();
David Tolnayfbc46692020-11-27 11:33:29 -080032 let mut has_copy = false;
33 let mut has_clone = false;
David Tolnay21466df2020-11-27 14:04:42 -080034 let mut has_eq = false;
35 let mut has_partial_eq = false;
David Tolnayfbc46692020-11-27 11:33:29 -080036
37 for derive in &enm.derives {
38 let span = derive.span;
39 match derive.what {
40 Trait::Copy => {
41 expanded.extend(enum_copy(enm, span));
42 has_copy = true;
43 }
44 Trait::Clone => {
45 expanded.extend(enum_clone(enm, span));
46 has_clone = true;
47 }
David Tolnayf84c98b2020-11-27 12:59:42 -080048 Trait::Debug => expanded.extend(enum_debug(enm, span)),
David Tolnay21466df2020-11-27 14:04:42 -080049 Trait::Eq => {
50 traits.push(quote_spanned!(span=> ::std::cmp::Eq));
51 has_eq = true;
52 }
53 Trait::PartialEq => {
54 traits.push(quote_spanned!(span=> ::std::cmp::PartialEq));
55 has_partial_eq = true;
56 }
David Tolnayfbc46692020-11-27 11:33:29 -080057 }
58 }
59
60 let span = enm.name.rust.span();
61 if !has_copy {
62 expanded.extend(enum_copy(enm, span));
63 }
64 if !has_clone {
65 expanded.extend(enum_clone(enm, span));
66 }
David Tolnay21466df2020-11-27 14:04:42 -080067 if !has_eq {
David Tolnayb3d7bb12020-11-27 14:11:41 -080068 // Required to be derived in order for the enum's "variants" to be
69 // usable in patterns.
David Tolnay21466df2020-11-27 14:04:42 -080070 traits.push(quote!(::std::cmp::Eq));
71 }
72 if !has_partial_eq {
73 traits.push(quote!(::std::cmp::PartialEq));
74 }
75
76 *actual_derives = Some(quote!(#[derive(#(#traits),*)]));
David Tolnayb3d7bb12020-11-27 14:11:41 -080077
David Tolnayfbc46692020-11-27 11:33:29 -080078 expanded
79}
80
David Tolnay2c24e5c2020-11-27 12:47:41 -080081fn struct_copy(strct: &Struct, span: Span) -> TokenStream {
82 let ident = &strct.name.rust;
83
84 quote_spanned! {span=>
85 impl ::std::marker::Copy for #ident {}
86 }
87}
88
89fn struct_clone(strct: &Struct, span: Span) -> TokenStream {
90 let ident = &strct.name.rust;
91
David Tolnaybc047bb2020-11-27 14:30:12 -080092 let body = if derive::contains(&strct.derives, Trait::Copy) {
David Tolnay2c24e5c2020-11-27 12:47:41 -080093 quote!(*self)
94 } else {
95 let fields = strct.fields.iter().map(|field| &field.ident);
96 let values = strct.fields.iter().map(|field| {
97 let ident = &field.ident;
98 let ty = field.ty.to_token_stream();
99 let span = ty.into_iter().last().unwrap().span();
100 quote_spanned!(span=> &self.#ident)
101 });
102 quote_spanned!(span=> #ident {
103 #(#fields: ::std::clone::Clone::clone(#values),)*
104 })
105 };
106
107 quote_spanned! {span=>
108 impl ::std::clone::Clone for #ident {
109 fn clone(&self) -> Self {
110 #body
David Tolnayd8ad9702020-11-27 12:43:59 -0800111 }
112 }
113 }
David Tolnayd8ad9702020-11-27 12:43:59 -0800114}
David Tolnayfbc46692020-11-27 11:33:29 -0800115
David Tolnayf84c98b2020-11-27 12:59:42 -0800116fn struct_debug(strct: &Struct, span: Span) -> TokenStream {
117 let ident = &strct.name.rust;
118 let struct_name = ident.to_string();
119 let fields = strct.fields.iter().map(|field| &field.ident);
120 let field_names = fields.clone().map(Ident::to_string);
121
122 quote_spanned! {span=>
123 impl ::std::fmt::Debug for #ident {
124 fn fmt(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
125 formatter.debug_struct(#struct_name)
126 #(.field(#field_names, &self.#fields))*
127 .finish()
128 }
129 }
130 }
131}
132
David Tolnayfbc46692020-11-27 11:33:29 -0800133fn enum_copy(enm: &Enum, span: Span) -> TokenStream {
134 let ident = &enm.name.rust;
135
136 quote_spanned! {span=>
137 impl ::std::marker::Copy for #ident {}
138 }
139}
140
141fn enum_clone(enm: &Enum, span: Span) -> TokenStream {
142 let ident = &enm.name.rust;
143
144 quote_spanned! {span=>
145 impl ::std::clone::Clone for #ident {
146 fn clone(&self) -> Self {
147 *self
148 }
149 }
150 }
151}
David Tolnayf84c98b2020-11-27 12:59:42 -0800152
153fn enum_debug(enm: &Enum, span: Span) -> TokenStream {
154 let ident = &enm.name.rust;
155 let variants = enm.variants.iter().map(|variant| {
156 let variant = &variant.ident;
157 let name = variant.to_string();
158 quote_spanned! {span=>
159 #ident::#variant => formatter.write_str(#name),
160 }
161 });
162 let fallback = format!("{}({{}})", ident);
163
164 quote_spanned! {span=>
165 impl ::std::fmt::Debug for #ident {
166 fn fmt(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
167 match *self {
168 #(#variants)*
169 _ => ::std::write!(formatter, #fallback, self.repr),
170 }
171 }
172 }
173 }
174}