1extern crate proc_macro;
2
3mod attributes;
4mod casing_style;
5mod error;
6mod parse;
7
8use proc_macro2::TokenStream;
9use proc_macro_error::proc_macro_error;
10use quote::{quote, ToTokens, TokenStreamExt};
11use std::{collections::HashMap, str};
12use syn::{
13 parse_macro_input, token, Data, DataEnum, DataStruct, DeriveInput, Field, Fields, Ident, Index,
14 Type, Variant,
15};
16
17use attributes::{Attributes, ObjectAttributes};
18use error::Error;
19
20#[proc_macro_derive(Tabled, attributes(tabled))]
21#[proc_macro_error]
22pub fn tabled(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
23 let input = parse_macro_input!(input as DeriveInput);
24 let ast = impl_tabled(&input);
25 proc_macro::TokenStream::from(ast)
26}
27
28fn impl_tabled(ast: &DeriveInput) -> TokenStream {
29 let attrs = ObjectAttributes::parse(&ast.attrs)
30 .map_err(error::abort)
31 .unwrap();
32
33 let length = get_tabled_length(ast).map_err(error::abort).unwrap();
34 let info = collect_info(ast, &attrs).map_err(error::abort).unwrap();
35 let fields = info.values;
36 let headers = info.headers;
37
38 let name = &ast.ident;
39 let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
40 let expanded = quote! {
41 impl #impl_generics Tabled for #name #ty_generics #where_clause {
42 const LENGTH: usize = #length;
43
44 fn fields(&self) -> Vec<::std::borrow::Cow<'_, str>> {
45 #fields
46 }
47
48 fn headers() -> Vec<::std::borrow::Cow<'static, str>> {
49 #headers
50 }
51 }
52 };
53
54 expanded
55}
56
57fn get_tabled_length(ast: &DeriveInput) -> Result<TokenStream, Error> {
58 match &ast.data {
59 Data::Struct(data) => get_fields_length(&data.fields),
60 Data::Enum(data) => get_enum_length(data),
61 Data::Union(_) => Err(Error::message("Union type isn't supported")),
62 }
63}
64
65fn get_fields_length(fields: &Fields) -> Result<TokenStream, Error> {
66 let size_components = fields
67 .iter()
68 .map(|field| {
69 let attributes = Attributes::parse(&field.attrs)?;
70 Ok((field, attributes))
71 })
72 .collect::<Result<Vec<_>, Error>>()?
73 .into_iter()
74 .filter(|(_, attr)| !attr.is_ignored())
75 .map(|(field, attr)| {
76 if attr.inline {
77 let field_type = &field.ty;
78 quote!({<#field_type as Tabled>::LENGTH})
79 } else {
80 quote!({ 1 })
81 }
82 });
83
84 let size_components = std::iter::once(quote!(0)).chain(size_components);
85
86 let mut stream = TokenStream::new();
87 stream.append_separated(size_components, syn::token::Add::default());
88
89 Ok(stream)
90}
91
92fn get_enum_length(enum_ast: &DataEnum) -> Result<TokenStream, Error> {
93 let variant_sizes = get_enum_variant_length(enum_ast);
94
95 let mut stream = TokenStream::new();
96 for (i, size) in variant_sizes.enumerate() {
97 let size = size?;
98
99 if i != 0 {
100 stream.append_all(syn::token::Add::default().into_token_stream());
101 }
102
103 stream.append_all(size);
104 }
105
106 Ok(stream)
107}
108
109fn get_enum_variant_length(
110 enum_ast: &DataEnum,
111) -> impl Iterator<Item = Result<TokenStream, Error>> + '_ {
112 enum_ast
113 .variants
114 .iter()
115 .map(|variant| -> Result<_, Error> {
116 let attributes = Attributes::parse(&variant.attrs)?;
117 Ok((variant, attributes))
118 })
119 .filter(|result| result.is_err() || matches!(result, Ok((_, attr)) if !attr.is_ignored()))
120 .map(|result| {
121 let (variant, attr) = result?;
122
123 if attr.inline {
124 get_fields_length(&variant.fields)
125 } else {
126 Ok(quote!(1))
127 }
128 })
129}
130
131fn collect_info(ast: &DeriveInput, attrs: &ObjectAttributes) -> Result<Impl, Error> {
132 match &ast.data {
133 Data::Struct(data) => collect_info_struct(data, attrs),
134 Data::Enum(data) => collect_info_enum(data, attrs),
135 Data::Union(_) => Err(Error::message("Union type isn't supported")),
136 }
137}
138
139fn collect_info_struct(ast: &DataStruct, attrs: &ObjectAttributes) -> Result<Impl, Error> {
140 info_from_fields(&ast.fields, attrs, field_var_name, "")
141}
142
143fn info_from_fields(
147 fields: &Fields,
148 attrs: &ObjectAttributes,
149 field_name: impl Fn(usize, &Field) -> TokenStream,
150 header_prefix: &str,
151) -> Result<Impl, Error> {
152 let count_fields = fields.len();
153
154 let fields = fields
155 .into_iter()
156 .enumerate()
157 .map(|(i, field)| -> Result<_, Error> {
158 let mut attributes = Attributes::parse(&field.attrs)?;
159 merge_attributes(&mut attributes, attrs);
160
161 Ok((i, field, attributes))
162 });
163
164 let mut headers = Vec::new();
165 let mut values = Vec::new();
166 let mut reorder = HashMap::new();
167
168 for result in fields {
169 let (i, field, attributes) = result?;
170 if attributes.is_ignored() {
171 continue;
172 }
173
174 if let Some(order) = attributes.order {
175 if order >= count_fields {
176 return Err(Error::message(format!(
177 "An order index '{}' is out of fields scope",
178 order
179 )));
180 }
181
182 reorder.insert(order, i);
183 }
184
185 let header = field_headers(field, i, &attributes, header_prefix);
186
187 headers.push(header);
188
189 let field_name = field_name(i, field);
190 let value = get_field_fields(&field_name, &attributes);
191
192 values.push(value);
193 }
194
195 if !reorder.is_empty() {
196 values = reorder_fields(&reorder, &values);
197 headers = reorder_fields(&reorder, &headers);
198 }
199
200 let headers = quote!({
201 let mut out = Vec::new();
202 #(out.extend(#headers);)*
203 out
204 });
205
206 let values = quote!({
207 let mut out = Vec::new();
208 #(out.extend(#values);)*
209 out
210 });
211
212 Ok(Impl { headers, values })
213}
214
215fn reorder_fields<T: Clone>(order: &HashMap<usize, usize>, elements: &[T]) -> Vec<T> {
216 let mut out: Vec<Option<T>> = Vec::with_capacity(elements.len());
217 out.resize(elements.len(), None);
218
219 for (pos, index) in order {
220 let value = elements[*index].clone();
221 out[*pos] = Some(value);
222 }
223
224 let mut j = 0;
225 for el in &mut out {
226 if el.is_some() {
227 continue;
228 }
229
230 while order.values().any(|&pos| j == pos) {
231 j += 1;
232 }
233
234 let v = elements[j].clone();
235 *el = Some(v);
236
237 j += 1;
238 }
239
240 out.into_iter().flatten().collect()
241}
242
243fn field_headers(
244 field: &Field,
245 index: usize,
246 attributes: &Attributes,
247 prefix: &str,
248) -> TokenStream {
249 if attributes.inline {
250 let prefix = attributes
251 .inline_prefix
252 .as_ref()
253 .map_or_else(|| "", |s| s.as_str());
254 return get_type_headers(&field.ty, prefix, "");
255 }
256
257 let header_name = field_header_name(field, attributes, index);
258 if prefix.is_empty() {
259 quote!(vec![::std::borrow::Cow::Borrowed(#header_name)])
260 } else {
261 let name = format!("{}{}", prefix, header_name);
262 quote!(vec![::std::borrow::Cow::Borrowed(#name)])
263 }
264}
265
266fn collect_info_enum(ast: &DataEnum, attrs: &ObjectAttributes) -> Result<Impl, Error> {
267 let mut headers_list = Vec::new();
268 let mut variants = Vec::new();
269 for variant in &ast.variants {
270 let mut attributes = Attributes::parse(&variant.attrs)?;
271 merge_attributes(&mut attributes, attrs);
272 if attributes.is_ignored() {
273 continue;
274 }
275
276 let info = info_from_variant(variant, &attributes, attrs)?;
277 variants.push((variant, info.values));
278 headers_list.push(info.headers);
279 }
280
281 let variant_sizes = get_enum_variant_length(ast)
282 .collect::<Result<Vec<_>, Error>>()?
283 .into_iter();
284 let values = values_for_enum(variant_sizes, &variants);
285
286 let headers = quote! {
287 vec![
288 #(#headers_list,)*
289 ]
290 .concat()
291 };
292
293 Ok(Impl { headers, values })
294}
295
296fn info_from_variant(
297 variant: &Variant,
298 attributes: &Attributes,
299 attrs: &ObjectAttributes,
300) -> Result<Impl, Error> {
301 if attributes.inline {
302 let prefix = attributes
303 .inline_prefix
304 .as_ref()
305 .map_or_else(|| "", |s| s.as_str());
306 return info_from_fields(&variant.fields, attrs, variant_var_name, prefix);
307 }
308
309 let variant_name = variant_name(variant, attributes);
310 let value = "+";
311
312 let headers = quote! { vec![::std::borrow::Cow::Borrowed(#variant_name)] };
314 let values = quote! { vec![::std::borrow::Cow::Borrowed(#value)] };
316
317 Ok(Impl { headers, values })
318}
319
320struct Impl {
321 headers: TokenStream,
322 values: TokenStream,
323}
324
325fn get_type_headers(field_type: &Type, inline_prefix: &str, prefix: &str) -> TokenStream {
326 if prefix.is_empty() && inline_prefix.is_empty() {
327 quote! { <#field_type as Tabled>::headers() }
328 } else {
329 quote! {
330 <#field_type as Tabled>::headers().into_iter()
331 .map(|header| {
332 let header = format!("{}{}{}", #prefix, #inline_prefix, header);
333 ::std::borrow::Cow::Owned(header)
334 })
335 .collect::<Vec<_>>()
336 }
337 }
338}
339
340fn get_field_fields(field: &TokenStream, attr: &Attributes) -> TokenStream {
341 if attr.inline {
342 return quote! { #field.fields() };
343 }
344
345 if let Some(func) = &attr.display_with {
346 let func_call = match attr.display_with_use_self {
347 true => use_function_with_self(func),
348 false => use_function_for(field, func),
349 };
350
351 return quote!(vec![::std::borrow::Cow::from(#func_call)]);
352 }
353
354 quote!(vec![::std::borrow::Cow::Owned(format!("{}", #field))])
355}
356
357fn use_function_for(field: &TokenStream, function: &str) -> TokenStream {
358 let path: syn::Result<syn::ExprPath> = syn::parse_str(function);
359 match path {
360 Ok(path) => {
361 quote! { #path(&#field) }
362 }
363 Err(_) => {
364 let function = Ident::new(function, proc_macro2::Span::call_site());
365 quote! { #function(&#field) }
366 }
367 }
368}
369
370fn use_function_with_self(function: &str) -> TokenStream {
371 let path: syn::Result<syn::ExprPath> = syn::parse_str(function);
372 match path {
373 Ok(path) => {
374 quote! { #path(&self) }
375 }
376 Err(_) => {
377 let function = Ident::new(function, proc_macro2::Span::call_site());
378 quote! { #function(&self) }
379 }
380 }
381}
382
383fn field_var_name(index: usize, field: &Field) -> TokenStream {
384 let f = field.ident.as_ref().map_or_else(
385 || Index::from(index).to_token_stream(),
386 quote::ToTokens::to_token_stream,
387 );
388 quote!(self.#f)
389}
390
391fn variant_var_name(index: usize, field: &Field) -> TokenStream {
392 match &field.ident {
393 Some(indent) => indent.to_token_stream(),
394 None => Ident::new(
395 format!("x_{}", index).as_str(),
396 proc_macro2::Span::call_site(),
397 )
398 .to_token_stream(),
399 }
400}
401
402fn values_for_enum(
403 variant_sizes: impl Iterator<Item = TokenStream>,
404 variants: &[(&Variant, TokenStream)],
405) -> TokenStream {
406 let branches = variants.iter().map(|(variant, _)| match_variant(variant));
407
408 let fields = variants
409 .iter()
410 .map(|(_, values)| values)
411 .collect::<Vec<_>>();
412
413 let mut stream = TokenStream::new();
414 for (i, (branch, fields)) in branches.into_iter().zip(fields).enumerate() {
415 let branch = quote! {
416 Self::#branch => {
417 let offset = offsets[#i];
418 let fields: Vec<::std::borrow::Cow<'_, str>> = #fields;
419
420 for (i, field) in fields.into_iter().enumerate() {
421 out_vec[i+offset] = field;
422 }
423 },
424 };
425
426 stream.append_all(branch);
427 }
428
429 quote! {
430 let mut offsets: &mut [usize] = &mut [0, #(#variant_sizes,)*];
438 for i in 1 .. offsets.len() {
439 offsets[i] += offsets[i-1]
440 }
441
442 let size = <Self as Tabled>::LENGTH;
443 let mut out_vec = vec![::std::borrow::Cow::Borrowed(""); size];
444
445 #[allow(unused_variables)]
446 match &self {
447 #stream
448 _ => return vec![], };
450
451 out_vec
452 }
453}
454
455fn variant_idents(v: &Variant) -> Vec<TokenStream> {
456 v.fields
457 .iter()
458 .enumerate()
459 .map(|(index, field)| variant_var_name(index, field))
462 .collect::<Vec<_>>()
463}
464
465fn match_variant(v: &Variant) -> TokenStream {
466 let mut token = TokenStream::new();
467 token.append_all(v.ident.to_token_stream());
468
469 let fields = variant_idents(v);
470
471 match &v.fields {
472 Fields::Named(_) => {
473 token::Brace::default().surround(&mut token, |s| {
474 s.append_separated(fields, quote! {,});
475 });
476 }
477 Fields::Unnamed(_) => {
478 token::Paren::default().surround(&mut token, |s| {
479 s.append_separated(fields, quote! {,});
480 });
481 }
482 Fields::Unit => {}
483 };
484
485 token
486}
487
488fn variant_name(variant: &Variant, attributes: &Attributes) -> String {
489 attributes
490 .rename
491 .clone()
492 .or_else(|| {
493 attributes
494 .rename_all
495 .as_ref()
496 .map(|case| case.cast(variant.ident.to_string()))
497 })
498 .unwrap_or_else(|| variant.ident.to_string())
499}
500
501fn field_header_name(f: &Field, attr: &Attributes, index: usize) -> String {
502 if let Some(name) = &attr.rename {
503 return name.to_string();
504 }
505
506 match &f.ident {
507 Some(name) => {
508 let name = name.to_string();
509 match &attr.rename_all {
510 Some(case) => case.cast(name),
511 None => name,
512 }
513 }
514 None => index.to_string(),
515 }
516}
517
518fn merge_attributes(attr: &mut Attributes, global_attr: &ObjectAttributes) {
519 if attr.rename_all.is_none() {
520 attr.rename_all = global_attr.rename_all;
521 }
522}