blob: 2109a1f6f887191821b9265290c49816e2ca030e [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 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 Tolnaybc047bb2020-11-27 14:30:12 -080062 let body = if derive::contains(&strct.derives, Trait::Copy) {
David Tolnay2c24e5c2020-11-27 12:47:41 -080063 quote!(*self)
64 } else {
65 let fields = strct.fields.iter().map(|field| &field.ident);
66 let values = strct.fields.iter().map(|field| {
67 let ident = &field.ident;
68 let ty = field.ty.to_token_stream();
69 let span = ty.into_iter().last().unwrap().span();
70 quote_spanned!(span=> &self.#ident)
71 });
72 quote_spanned!(span=> #ident {
73 #(#fields: ::std::clone::Clone::clone(#values),)*
74 })
75 };
76
77 quote_spanned! {span=>
78 impl ::std::clone::Clone for #ident {
79 fn clone(&self) -> Self {
80 #body
David Tolnayd8ad9702020-11-27 12:43:59 -080081 }
82 }
83 }
David Tolnayd8ad9702020-11-27 12:43:59 -080084}
David Tolnayfbc46692020-11-27 11:33:29 -080085
David Tolnayf84c98b2020-11-27 12:59:42 -080086fn struct_debug(strct: &Struct, span: Span) -> TokenStream {
87 let ident = &strct.name.rust;
88 let struct_name = ident.to_string();
89 let fields = strct.fields.iter().map(|field| &field.ident);
90 let field_names = fields.clone().map(Ident::to_string);
91
92 quote_spanned! {span=>
93 impl ::std::fmt::Debug for #ident {
94 fn fmt(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
95 formatter.debug_struct(#struct_name)
96 #(.field(#field_names, &self.#fields))*
97 .finish()
98 }
99 }
100 }
101}
102
David Tolnayfbc46692020-11-27 11:33:29 -0800103fn enum_copy(enm: &Enum, span: Span) -> TokenStream {
104 let ident = &enm.name.rust;
105
106 quote_spanned! {span=>
107 impl ::std::marker::Copy for #ident {}
108 }
109}
110
111fn enum_clone(enm: &Enum, span: Span) -> TokenStream {
112 let ident = &enm.name.rust;
113
114 quote_spanned! {span=>
115 impl ::std::clone::Clone for #ident {
116 fn clone(&self) -> Self {
117 *self
118 }
119 }
120 }
121}
David Tolnayf84c98b2020-11-27 12:59:42 -0800122
123fn enum_debug(enm: &Enum, span: Span) -> TokenStream {
124 let ident = &enm.name.rust;
125 let variants = enm.variants.iter().map(|variant| {
126 let variant = &variant.ident;
127 let name = variant.to_string();
128 quote_spanned! {span=>
129 #ident::#variant => formatter.write_str(#name),
130 }
131 });
132 let fallback = format!("{}({{}})", ident);
133
134 quote_spanned! {span=>
135 impl ::std::fmt::Debug for #ident {
136 fn fmt(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
137 match *self {
138 #(#variants)*
139 _ => ::std::write!(formatter, #fallback, self.repr),
140 }
141 }
142 }
143 }
144}