blob: 87f8ec599be3382862cc7fc328b758f362dfeb21 [file] [log] [blame]
David Tolnayfbc46692020-11-27 11:33:29 -08001use crate::syntax::{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 Tolnayfbc46692020-11-27 11:33:29 -080020pub fn expand_enum(enm: &Enum) -> TokenStream {
21 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
48 expanded
49}
50
David Tolnay2c24e5c2020-11-27 12:47:41 -080051fn struct_copy(strct: &Struct, span: Span) -> TokenStream {
52 let ident = &strct.name.rust;
53
54 quote_spanned! {span=>
55 impl ::std::marker::Copy for #ident {}
56 }
57}
58
59fn struct_clone(strct: &Struct, span: Span) -> TokenStream {
60 let ident = &strct.name.rust;
61
David Tolnayd8ad9702020-11-27 12:43:59 -080062 let is_copy = strct
63 .derives
64 .iter()
65 .any(|derive| derive.what == Trait::Copy);
66
David Tolnay2c24e5c2020-11-27 12:47:41 -080067 let body = if is_copy {
68 quote!(*self)
69 } else {
70 let fields = strct.fields.iter().map(|field| &field.ident);
71 let values = strct.fields.iter().map(|field| {
72 let ident = &field.ident;
73 let ty = field.ty.to_token_stream();
74 let span = ty.into_iter().last().unwrap().span();
75 quote_spanned!(span=> &self.#ident)
76 });
77 quote_spanned!(span=> #ident {
78 #(#fields: ::std::clone::Clone::clone(#values),)*
79 })
80 };
81
82 quote_spanned! {span=>
83 impl ::std::clone::Clone for #ident {
84 fn clone(&self) -> Self {
85 #body
David Tolnayd8ad9702020-11-27 12:43:59 -080086 }
87 }
88 }
David Tolnayd8ad9702020-11-27 12:43:59 -080089}
David Tolnayfbc46692020-11-27 11:33:29 -080090
David Tolnayf84c98b2020-11-27 12:59:42 -080091fn struct_debug(strct: &Struct, span: Span) -> TokenStream {
92 let ident = &strct.name.rust;
93 let struct_name = ident.to_string();
94 let fields = strct.fields.iter().map(|field| &field.ident);
95 let field_names = fields.clone().map(Ident::to_string);
96
97 quote_spanned! {span=>
98 impl ::std::fmt::Debug for #ident {
99 fn fmt(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
100 formatter.debug_struct(#struct_name)
101 #(.field(#field_names, &self.#fields))*
102 .finish()
103 }
104 }
105 }
106}
107
David Tolnayfbc46692020-11-27 11:33:29 -0800108fn enum_copy(enm: &Enum, span: Span) -> TokenStream {
109 let ident = &enm.name.rust;
110
111 quote_spanned! {span=>
112 impl ::std::marker::Copy for #ident {}
113 }
114}
115
116fn enum_clone(enm: &Enum, span: Span) -> TokenStream {
117 let ident = &enm.name.rust;
118
119 quote_spanned! {span=>
120 impl ::std::clone::Clone for #ident {
121 fn clone(&self) -> Self {
122 *self
123 }
124 }
125 }
126}
David Tolnayf84c98b2020-11-27 12:59:42 -0800127
128fn enum_debug(enm: &Enum, span: Span) -> TokenStream {
129 let ident = &enm.name.rust;
130 let variants = enm.variants.iter().map(|variant| {
131 let variant = &variant.ident;
132 let name = variant.to_string();
133 quote_spanned! {span=>
134 #ident::#variant => formatter.write_str(#name),
135 }
136 });
137 let fallback = format!("{}({{}})", ident);
138
139 quote_spanned! {span=>
140 impl ::std::fmt::Debug for #ident {
141 fn fmt(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
142 match *self {
143 #(#variants)*
144 _ => ::std::write!(formatter, #fallback, self.repr),
145 }
146 }
147 }
148 }
149}