blob: d896afb86ed394a28715e0657527b311efdd43dc [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();
7
David Tolnay2c24e5c2020-11-27 12:47:41 -08008 for derive in &strct.derives {
9 let span = derive.span;
10 match derive.what {
11 Trait::Copy => expanded.extend(struct_copy(strct, span)),
12 Trait::Clone => expanded.extend(struct_clone(strct, span)),
David Tolnayf84c98b2020-11-27 12:59:42 -080013 Trait::Debug => expanded.extend(struct_debug(strct, span)),
David Tolnay2c24e5c2020-11-27 12:47:41 -080014 }
15 }
16
David Tolnaya3619e92020-11-27 14:13:15 -080017 *actual_derives = None;
18
David Tolnay2c24e5c2020-11-27 12:47:41 -080019 expanded
20}
21
David Tolnayb3d7bb12020-11-27 14:11:41 -080022pub fn expand_enum(enm: &Enum, actual_derives: &mut Option<TokenStream>) -> TokenStream {
David Tolnayfbc46692020-11-27 11:33:29 -080023 let mut expanded = TokenStream::new();
24 let mut has_copy = false;
25 let mut has_clone = false;
26
27 for derive in &enm.derives {
28 let span = derive.span;
29 match derive.what {
30 Trait::Copy => {
31 expanded.extend(enum_copy(enm, span));
32 has_copy = true;
33 }
34 Trait::Clone => {
35 expanded.extend(enum_clone(enm, span));
36 has_clone = true;
37 }
David Tolnayf84c98b2020-11-27 12:59:42 -080038 Trait::Debug => expanded.extend(enum_debug(enm, span)),
David Tolnayfbc46692020-11-27 11:33:29 -080039 }
40 }
41
42 let span = enm.name.rust.span();
43 if !has_copy {
44 expanded.extend(enum_copy(enm, span));
45 }
46 if !has_clone {
47 expanded.extend(enum_clone(enm, span));
48 }
49
David Tolnayb3d7bb12020-11-27 14:11:41 -080050 *actual_derives = Some(quote! {
51 // Required to be derived in order for the enum's "variants" to be
52 // usable in patterns.
53 #[derive(::std::cmp::PartialEq, ::std::cmp::Eq)]
54 });
55
David Tolnayfbc46692020-11-27 11:33:29 -080056 expanded
57}
58
David Tolnay2c24e5c2020-11-27 12:47:41 -080059fn struct_copy(strct: &Struct, span: Span) -> TokenStream {
60 let ident = &strct.name.rust;
61
62 quote_spanned! {span=>
63 impl ::std::marker::Copy for #ident {}
64 }
65}
66
67fn struct_clone(strct: &Struct, span: Span) -> TokenStream {
68 let ident = &strct.name.rust;
69
David Tolnaybc047bb2020-11-27 14:30:12 -080070 let body = if derive::contains(&strct.derives, Trait::Copy) {
David Tolnay2c24e5c2020-11-27 12:47:41 -080071 quote!(*self)
72 } else {
73 let fields = strct.fields.iter().map(|field| &field.ident);
74 let values = strct.fields.iter().map(|field| {
75 let ident = &field.ident;
76 let ty = field.ty.to_token_stream();
77 let span = ty.into_iter().last().unwrap().span();
78 quote_spanned!(span=> &self.#ident)
79 });
80 quote_spanned!(span=> #ident {
81 #(#fields: ::std::clone::Clone::clone(#values),)*
82 })
83 };
84
85 quote_spanned! {span=>
86 impl ::std::clone::Clone for #ident {
87 fn clone(&self) -> Self {
88 #body
David Tolnayd8ad9702020-11-27 12:43:59 -080089 }
90 }
91 }
David Tolnayd8ad9702020-11-27 12:43:59 -080092}
David Tolnayfbc46692020-11-27 11:33:29 -080093
David Tolnayf84c98b2020-11-27 12:59:42 -080094fn struct_debug(strct: &Struct, span: Span) -> TokenStream {
95 let ident = &strct.name.rust;
96 let struct_name = ident.to_string();
97 let fields = strct.fields.iter().map(|field| &field.ident);
98 let field_names = fields.clone().map(Ident::to_string);
99
100 quote_spanned! {span=>
101 impl ::std::fmt::Debug for #ident {
102 fn fmt(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
103 formatter.debug_struct(#struct_name)
104 #(.field(#field_names, &self.#fields))*
105 .finish()
106 }
107 }
108 }
109}
110
David Tolnayfbc46692020-11-27 11:33:29 -0800111fn enum_copy(enm: &Enum, span: Span) -> TokenStream {
112 let ident = &enm.name.rust;
113
114 quote_spanned! {span=>
115 impl ::std::marker::Copy for #ident {}
116 }
117}
118
119fn enum_clone(enm: &Enum, span: Span) -> TokenStream {
120 let ident = &enm.name.rust;
121
122 quote_spanned! {span=>
123 impl ::std::clone::Clone for #ident {
124 fn clone(&self) -> Self {
125 *self
126 }
127 }
128 }
129}
David Tolnayf84c98b2020-11-27 12:59:42 -0800130
131fn enum_debug(enm: &Enum, span: Span) -> TokenStream {
132 let ident = &enm.name.rust;
133 let variants = enm.variants.iter().map(|variant| {
134 let variant = &variant.ident;
135 let name = variant.to_string();
136 quote_spanned! {span=>
137 #ident::#variant => formatter.write_str(#name),
138 }
139 });
140 let fallback = format!("{}({{}})", ident);
141
142 quote_spanned! {span=>
143 impl ::std::fmt::Debug for #ident {
144 fn fmt(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
145 match *self {
146 #(#variants)*
147 _ => ::std::write!(formatter, #fallback, self.repr),
148 }
149 }
150 }
151 }
152}