pathfinder_tagged_debug_derive/
lib.rs1use std::convert::From;
2
3use proc_macro2::Ident;
4use quote::{format_ident, quote, quote_spanned};
5use syn::spanned::Spanned;
6use syn::{parse_macro_input, Data, DeriveInput};
7
8#[proc_macro_derive(TaggedDebug)]
9pub fn derive_tagged_debug(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
10 let input = parse_macro_input!(input as DeriveInput);
11
12 derive_tagged_debug_impl(input)
13 .unwrap_or_else(|e| e.to_compile_error())
14 .into()
15}
16
17fn derive_tagged_debug_impl(input: DeriveInput) -> Result<proc_macro2::TokenStream, syn::Error> {
18 let name = input.ident;
19 let fmt_body = iterate_tagged_debug(&name, &input.data)?;
20
21 Ok(quote! {
22 impl std::fmt::Debug for #name {
23 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
24 #fmt_body
25 }
26 }
27 })
28}
29
30fn iterate_tagged_debug(name: &Ident, data: &Data) -> Result<proc_macro2::TokenStream, syn::Error> {
31 match *data {
32 Data::Struct(ref data) => Ok(iterate_struct_fields(name, &data.fields)),
33 Data::Enum(ref e) => Ok(iterate_enum_variants(name, &e.variants)),
34 Data::Union(ref u) => Err(syn::Error::new(
35 u.union_token.span(),
36 "union is not supported",
37 )),
38 }
39}
40
41fn iterate_struct_fields(struct_name: &Ident, fields: &syn::Fields) -> proc_macro2::TokenStream {
42 match fields {
43 syn::Fields::Named(ref fields) => {
44 let fields = fields.named.iter().map(|f| {
45 let name = &f.ident;
46 quote_spanned! {f.span()=>
47 .field(stringify!(#name), &self.#name)
48 }
49 });
50 let fields_clone = fields.clone();
51 quote! {
52 match Tagged::<#struct_name>::tag(self) {
53 Ok(tag) => {
54 f.debug_struct(stringify!(#struct_name))
55 .field("TAG", &tag)
56 #(#fields)*
57 .finish()
58 }
59 Err(_) => {
60 f.debug_struct(stringify!(#struct_name))
61 #(#fields_clone)*
62 .finish()
63 }
64 }
65 }
66 }
67 syn::Fields::Unit => quote! {
68 match Tagged::<#struct_name>::tag(self) {
69 Ok(tag) => {
70 f.debug_struct(stringify!(#struct_name))
71 .field("TAG", &tag)
72 .finish()
73 }
74 Err(_) => {
75 f.debug_struct(stringify!(#struct_name))
76 .finish()
77 }
78 }
79 },
80 syn::Fields::Unnamed(ref fields) => {
81 let fields = fields.unnamed.iter().enumerate().map(|(i, f)| {
82 let i = syn::Index::from(i);
83 quote_spanned! {f.span()=>
84 .field(&self.#i)
85 }
86 });
87 let fields_clone = fields.clone();
88 quote! {
89 match Tagged::<#struct_name>::tag(self) {
90 Ok(tag) => {
91 f.debug_tuple(stringify!(#struct_name))
92 .field(&format!("TAG: {}", &tag))
93 #(#fields)*
94 .finish()
95 }
96 Err(_) => {
97 f.debug_tuple(stringify!(#struct_name))
98 #(#fields_clone)*
99 .finish()
100 }
101 }
102 }
103 }
104 }
105}
106
107fn iterate_enum_variants(
108 enum_name: &Ident,
109 variants: &syn::punctuated::Punctuated<syn::Variant, syn::token::Comma>,
110) -> proc_macro2::TokenStream {
111 let variant_impls = variants.iter().map(|v| {
112 let variant_name = &v.ident;
113
114 match v.fields {
115 syn::Fields::Named(ref fields) => {
116 let fields_pattern = fields.named.iter().map(|f| {
117 let name = &f.ident;
118 quote_spanned! {f.span()=>
119 #name,
120 }
121 });
122
123 let fields_impl = fields.named.iter().map(|f| {
124 let name = &f.ident;
125 quote_spanned! {f.span()=>
126 .field(stringify!(#name), &#name)
127 }
128 });
129 let fields_impl_clone = fields_impl.clone();
130
131 quote! {
132 #enum_name::#variant_name {
133 #(#fields_pattern)*
134 } => {
135 match Tagged::<#enum_name>::tag(self) {
136 Ok(tag) => {
137 f.debug_struct(stringify!(#variant_name))
138 .field("TAG", &tag)
139 #(#fields_impl)*
140 .finish()
141 }
142 Err(_) => {
143 f.debug_struct(stringify!(#variant_name))
144 #(#fields_impl_clone)*
145 .finish()
146 }
147 }}
148 }
149 }
150 syn::Fields::Unit => quote! {
151 #enum_name::#variant_name => {
152 match Tagged::<#enum_name>::tag(self) {
153 Ok(tag) => {
154 f.debug_struct(stringify!(#variant_name))
155 .field("TAG", &tag)
156 .finish()
157 }
158 Err(_) => {
159 f.debug_struct(stringify!(#variant_name))
160 .finish()
161 }
162 }}
163 },
164 syn::Fields::Unnamed(ref fields) => {
165 let fields_pattern = fields.unnamed.iter().enumerate().map(|(i, f)| {
166 let i = syn::Index::from(i);
167 let field_name = format_ident!("field_{}", i);
168
169 quote_spanned! {f.span()=>
170 #field_name,
171 }
172 });
173
174 let fields_impl = fields.unnamed.iter().enumerate().map(|(i, f)| {
175 let i = syn::Index::from(i);
176 let field_name = format_ident!("field_{}", i);
177
178 quote_spanned! {f.span()=>
179 .field(&#field_name)
180 }
181 });
182 let fields_impl_clone = fields_impl.clone();
183
184 quote! {
185 #enum_name::#variant_name (
186 #(#fields_pattern)*
187 ) => {
188 match Tagged::<#enum_name>::tag(self) {
189 Ok(tag) => {
190 f.debug_tuple(stringify!(#variant_name))
191 .field(&format!("TAG: {}", &tag))
192 #(#fields_impl)*
193 .finish()
194 }
195 Err(_) => {
196 f.debug_tuple(stringify!(#variant_name))
197 #(#fields_impl_clone)*
198 .finish()
199 }
200 }}
201 }
202 }
203 }
204 });
205 quote! {
206 match self {
207 #(#variant_impls)*
208 }
209 }
210}