blob: 22c05cccf923751ad6c55a4f9463cba5ce973a7b [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
5pub fn expand_struct(strct: &Struct) -> 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
17 expanded
18}
19
David Tolnayb3d7bb12020-11-27 14:11:41 -080020pub fn expand_enum(enm: &Enum, actual_derives: &mut Option<TokenStream>) -> TokenStream {
David Tolnayfbc46692020-11-27 11:33:29 -080021 let mut expanded = TokenStream::new();
22 let mut has_copy = false;
23 let mut has_clone = false;
24
25 for derive in &enm.derives {
26 let span = derive.span;
27 match derive.what {
28 Trait::Copy => {
29 expanded.extend(enum_copy(enm, span));
30 has_copy = true;
31 }
32 Trait::Clone => {
33 expanded.extend(enum_clone(enm, span));
34 has_clone = true;
35 }
David Tolnayf84c98b2020-11-27 12:59:42 -080036 Trait::Debug => expanded.extend(enum_debug(enm, span)),
David Tolnayfbc46692020-11-27 11:33:29 -080037 }
38 }
39
40 let span = enm.name.rust.span();
41 if !has_copy {
42 expanded.extend(enum_copy(enm, span));
43 }
44 if !has_clone {
45 expanded.extend(enum_clone(enm, span));
46 }
47
David Tolnayb3d7bb12020-11-27 14:11:41 -080048 *actual_derives = Some(quote! {
49 // Required to be derived in order for the enum's "variants" to be
50 // usable in patterns.
51 #[derive(::std::cmp::PartialEq, ::std::cmp::Eq)]
52 });
53
David Tolnayfbc46692020-11-27 11:33:29 -080054 expanded
55}
56
David Tolnay2c24e5c2020-11-27 12:47:41 -080057fn struct_copy(strct: &Struct, span: Span) -> TokenStream {
58 let ident = &strct.name.rust;
59
60 quote_spanned! {span=>
61 impl ::std::marker::Copy for #ident {}
62 }
63}
64
65fn struct_clone(strct: &Struct, span: Span) -> TokenStream {
66 let ident = &strct.name.rust;
67
David Tolnaybc047bb2020-11-27 14:30:12 -080068 let body = if derive::contains(&strct.derives, Trait::Copy) {
David Tolnay2c24e5c2020-11-27 12:47:41 -080069 quote!(*self)
70 } else {
71 let fields = strct.fields.iter().map(|field| &field.ident);
72 let values = strct.fields.iter().map(|field| {
73 let ident = &field.ident;
74 let ty = field.ty.to_token_stream();
75 let span = ty.into_iter().last().unwrap().span();
76 quote_spanned!(span=> &self.#ident)
77 });
78 quote_spanned!(span=> #ident {
79 #(#fields: ::std::clone::Clone::clone(#values),)*
80 })
81 };
82
83 quote_spanned! {span=>
84 impl ::std::clone::Clone for #ident {
85 fn clone(&self) -> Self {
86 #body
David Tolnayd8ad9702020-11-27 12:43:59 -080087 }
88 }
89 }
David Tolnayd8ad9702020-11-27 12:43:59 -080090}
David Tolnayfbc46692020-11-27 11:33:29 -080091
David Tolnayf84c98b2020-11-27 12:59:42 -080092fn struct_debug(strct: &Struct, span: Span) -> TokenStream {
93 let ident = &strct.name.rust;
94 let struct_name = ident.to_string();
95 let fields = strct.fields.iter().map(|field| &field.ident);
96 let field_names = fields.clone().map(Ident::to_string);
97
98 quote_spanned! {span=>
99 impl ::std::fmt::Debug for #ident {
100 fn fmt(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
101 formatter.debug_struct(#struct_name)
102 #(.field(#field_names, &self.#fields))*
103 .finish()
104 }
105 }
106 }
107}
108
David Tolnayfbc46692020-11-27 11:33:29 -0800109fn enum_copy(enm: &Enum, span: Span) -> TokenStream {
110 let ident = &enm.name.rust;
111
112 quote_spanned! {span=>
113 impl ::std::marker::Copy for #ident {}
114 }
115}
116
117fn enum_clone(enm: &Enum, span: Span) -> TokenStream {
118 let ident = &enm.name.rust;
119
120 quote_spanned! {span=>
121 impl ::std::clone::Clone for #ident {
122 fn clone(&self) -> Self {
123 *self
124 }
125 }
126 }
127}
David Tolnayf84c98b2020-11-27 12:59:42 -0800128
129fn enum_debug(enm: &Enum, span: Span) -> TokenStream {
130 let ident = &enm.name.rust;
131 let variants = enm.variants.iter().map(|variant| {
132 let variant = &variant.ident;
133 let name = variant.to_string();
134 quote_spanned! {span=>
135 #ident::#variant => formatter.write_str(#name),
136 }
137 });
138 let fallback = format!("{}({{}})", ident);
139
140 quote_spanned! {span=>
141 impl ::std::fmt::Debug for #ident {
142 fn fmt(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
143 match *self {
144 #(#variants)*
145 _ => ::std::write!(formatter, #fallback, self.repr),
146 }
147 }
148 }
149 }
150}