1#![allow(clippy::uninlined_format_args)]
2#![doc(
3 html_logo_url = "https://raw.githubusercontent.com/zhiburt/tabled/86ac146e532ce9f7626608d7fd05072123603a2e/assets/tabled-gear.svg"
4)]
5
6extern crate proc_macro;
7
8mod attributes;
9mod casing_style;
10mod error;
11mod parse;
12
13use attributes::FormatArg;
14use proc_macro2::TokenStream;
15use proc_macro_error2::proc_macro_error;
16use quote::{quote, ToTokens, TokenStreamExt};
17use std::{collections::HashMap, str};
18use syn::spanned::Spanned;
19use syn::visit_mut::VisitMut;
20use syn::{
21 parse_macro_input, token, Data, DataEnum, DataStruct, DeriveInput, ExprPath, Field, Fields,
22 Ident, Index, PathSegment, Type, TypePath, Variant,
23};
24
25use crate::attributes::{FieldAttributes, TypeAttributes};
26use crate::error::Error;
27
28type FieldNameFn = fn(usize, &Field) -> TokenStream;
29
30#[proc_macro_derive(Tabled, attributes(tabled))]
31#[proc_macro_error]
32pub fn tabled(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
33 let input = parse_macro_input!(input as DeriveInput);
34 let ast = impl_tabled(&input);
35 proc_macro::TokenStream::from(ast)
36}
37
38fn impl_tabled(ast: &DeriveInput) -> TokenStream {
39 let attrs = TypeAttributes::parse(&ast.attrs)
40 .map_err(error::abort)
41 .unwrap();
42
43 let tabled_trait_path = get_crate_name_expr(&attrs).map_err(error::abort).unwrap();
44
45 let length = get_tabled_length(ast, &attrs, &tabled_trait_path)
46 .map_err(error::abort)
47 .unwrap();
48 let info = collect_info(ast, &attrs, &tabled_trait_path)
49 .map_err(error::abort)
50 .unwrap();
51 let fields = info.values;
52 let headers = info.headers;
53
54 let name = &ast.ident;
55 let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
56
57 let expanded = quote! {
58 impl #impl_generics #tabled_trait_path for #name #ty_generics #where_clause {
59 const LENGTH: usize = #length;
60
61 fn fields(&self) -> Vec<::std::borrow::Cow<'_, str>> {
62 #fields
63 }
64
65 fn headers() -> Vec<::std::borrow::Cow<'static, str>> {
66 #headers
67 }
68 }
69 };
70
71 expanded
72}
73
74fn get_tabled_length(
75 ast: &DeriveInput,
76 attrs: &TypeAttributes,
77 trait_path: &ExprPath,
78) -> Result<TokenStream, Error> {
79 match &ast.data {
80 Data::Struct(data) => get_fields_length(&data.fields, trait_path),
81 Data::Enum(data) => {
82 if attrs.inline {
83 Ok(quote! { 1 })
84 } else {
85 get_enum_length(data, trait_path)
86 }
87 }
88 Data::Union(_) => Err(Error::message("Union type isn't supported")),
89 }
90}
91
92fn get_fields_length(fields: &Fields, tabled_trait: &ExprPath) -> Result<TokenStream, Error> {
93 let size_components = fields
94 .iter()
95 .map(|field| {
96 let attributes = FieldAttributes::parse(&field.attrs)?;
97 Ok((field, attributes))
98 })
99 .collect::<Result<Vec<_>, Error>>()?
100 .into_iter()
101 .filter(|(_, attr)| !attr.is_ignored)
102 .map(|(field, attr)| {
103 if attr.inline {
104 if attr.map.is_some() {
105 match attr.map_type {
106 Some(map_type) => Ok(quote!({ <#map_type as #tabled_trait>::LENGTH })),
107 None => Err(Error::new(
108 "map type was not given",
109 field.span(),
110 Some(String::from("provide a type to map attribute")),
111 )),
112 }
113 } else {
114 let field_type = &field.ty;
115 Ok(quote!({<#field_type as #tabled_trait>::LENGTH}))
116 }
117 } else {
118 Ok(quote!({ 1 }))
119 }
120 })
121 .collect::<Result<Vec<_>, Error>>()?
122 .into_iter();
123
124 let size_components = std::iter::once(quote!(0)).chain(size_components);
125
126 let mut stream = TokenStream::new();
127 stream.append_separated(size_components, syn::token::Plus::default());
128
129 Ok(stream)
130}
131
132fn get_enum_length(enum_ast: &DataEnum, trait_path: &ExprPath) -> Result<TokenStream, Error> {
133 let variant_sizes = get_enum_variant_length(enum_ast, trait_path);
134
135 let mut stream = TokenStream::new();
136 for (i, size) in variant_sizes.enumerate() {
137 let size = size?;
138
139 if i != 0 {
140 stream.append_all(syn::token::Plus::default().into_token_stream());
141 }
142
143 stream.append_all(size);
144 }
145
146 Ok(stream)
147}
148
149fn get_enum_variant_length<'a>(
150 enum_ast: &'a DataEnum,
151 trait_path: &'a ExprPath,
152) -> impl Iterator<Item = Result<TokenStream, Error>> + 'a {
153 enum_ast
154 .variants
155 .iter()
156 .map(|variant| -> Result<_, Error> {
157 let attributes = FieldAttributes::parse(&variant.attrs)?;
158 Ok((variant, attributes))
159 })
160 .filter(|result| result.is_err() || matches!(result, Ok((_, attr)) if !attr.is_ignored))
161 .map(move |result| {
162 let (variant, attr) = result?;
163
164 if attr.inline {
165 get_fields_length(&variant.fields, trait_path)
166 } else {
167 Ok(quote!(1))
168 }
169 })
170}
171
172fn collect_info(
173 ast: &DeriveInput,
174 attrs: &TypeAttributes,
175 trait_path: &ExprPath,
176) -> Result<Impl, Error> {
177 match &ast.data {
178 Data::Struct(data) => collect_info_struct(data, attrs, trait_path),
179 Data::Enum(data) => collect_info_enum(data, attrs, &ast.ident, trait_path),
180 Data::Union(_) => Err(Error::message("Union type isn't supported")),
181 }
182}
183
184fn collect_info_struct(
185 ast: &DataStruct,
186 attrs: &TypeAttributes,
187 trait_path: &ExprPath,
188) -> Result<Impl, Error> {
189 info_from_fields(&ast.fields, attrs, struct_field_name, "", trait_path)
190}
191
192fn info_from_fields(
196 fields: &Fields,
197 attrs: &TypeAttributes,
198 field_name: FieldNameFn,
199 header_prefix: &str,
200 trait_path: &ExprPath,
201) -> Result<Impl, Error> {
202 let count_fields = fields.len();
203
204 let attributes = fields
205 .into_iter()
206 .enumerate()
207 .map(|(i, field)| -> Result<_, Error> {
208 let mut attributes = FieldAttributes::parse(&field.attrs)?;
209 merge_attributes(&mut attributes, attrs);
210
211 Ok((i, field, attributes))
212 });
213
214 let mut headers = Vec::new();
215 let mut values = Vec::new();
216 let mut reorder = HashMap::new();
217
218 let mut skipped = 0;
219 for result in attributes {
220 let (i, field, attributes) = result?;
221 if attributes.is_ignored {
222 skipped += 1;
223 continue;
224 }
225
226 if let Some(order) = attributes.order {
227 if order >= count_fields {
228 return Err(Error::message(format!(
229 "An order index '{order}' is out of fields scope"
230 )));
231 }
232
233 reorder.insert(order, i - skipped);
234 }
235
236 let header = field_headers(field, i, &attributes, header_prefix, trait_path);
237 headers.push(header);
238
239 let field_name_result = field_name(i, field);
240 let value = get_field_fields(
241 &field_name_result,
242 &field.ty,
243 &attributes,
244 fields,
245 field_name,
246 attrs,
247 );
248 values.push(value);
249 }
250
251 if !reorder.is_empty() {
252 values = reorder_fields(&reorder, &values);
253 headers = reorder_fields(&reorder, &headers);
254 }
255
256 let headers = quote!({
257 let mut out = Vec::new();
258 #(out.extend(#headers);)*
259 out
260 });
261
262 let values = quote!({
263 let mut out = Vec::new();
264 #(out.extend(#values);)*
265 out
266 });
267
268 Ok(Impl { headers, values })
269}
270
271fn reorder_fields<T: Clone>(order: &HashMap<usize, usize>, elements: &[T]) -> Vec<T> {
272 let mut out: Vec<Option<T>> = Vec::with_capacity(elements.len());
273 out.resize(elements.len(), None);
274
275 for (pos, index) in order {
276 let value = elements[*index].clone();
277 out[*pos] = Some(value);
278 }
279
280 let mut j = 0;
281 for el in &mut out {
282 if el.is_some() {
283 continue;
284 }
285
286 while order.values().any(|&pos| j == pos) {
287 j += 1;
288 }
289
290 let v = elements[j].clone();
291 *el = Some(v);
292
293 j += 1;
294 }
295
296 out.into_iter().flatten().collect()
297}
298
299fn field_headers(
300 field: &Field,
301 index: usize,
302 attrs: &FieldAttributes,
303 prefix: &str,
304 trait_path: &ExprPath,
305) -> TokenStream {
306 if attrs.inline {
307 let prefix = attrs
308 .inline_prefix
309 .as_ref()
310 .map_or_else(|| "", |s| s.as_str());
311
312 if attrs.map.is_some() {
313 if let Some(map_type) = &attrs.map_type {
314 return get_type_headers(map_type, prefix, "", trait_path);
315 } else {
316 unreachable!();
318 }
319 }
320
321 return get_type_headers(&field.ty, prefix, "", trait_path);
322 }
323
324 let header_name = field_header_name(field, attrs, index);
325 if prefix.is_empty() {
326 quote!(vec![::std::borrow::Cow::Borrowed(#header_name)])
327 } else {
328 let name = format!("{prefix}{header_name}");
329 quote!(vec![::std::borrow::Cow::Borrowed(#name)])
330 }
331}
332
333fn collect_info_enum(
334 ast: &DataEnum,
335 attrs: &TypeAttributes,
336 name: &Ident,
337 trait_path: &ExprPath,
338) -> Result<Impl, Error> {
339 match &attrs.inline {
340 true => {
341 let enum_name = attrs
342 .inline_value
343 .clone()
344 .unwrap_or_else(|| name.to_string());
345
346 collect_info_enum_inlined(ast, attrs, enum_name)
347 }
348 false => _collect_info_enum(ast, attrs, trait_path),
349 }
350}
351
352fn _collect_info_enum(
353 ast: &DataEnum,
354 attrs: &TypeAttributes,
355 trait_path: &ExprPath,
356) -> Result<Impl, Error> {
357 let orderedvariants = reodered_variants(ast)?;
359
360 let mut headers_list = Vec::new();
361 let mut variants = Vec::new();
362 for v in orderedvariants {
363 let mut attributes = FieldAttributes::parse(&v.attrs)?;
364 merge_attributes(&mut attributes, attrs);
365 if attributes.is_ignored {
366 continue;
367 }
368
369 let info = info_from_variant(v, &attributes, attrs, trait_path)?;
370 variants.push((v, info.values));
371 headers_list.push(info.headers);
372 }
373
374 let variant_sizes = get_enum_variant_length(ast, trait_path)
375 .collect::<Result<Vec<_>, Error>>()?
376 .into_iter();
377 let values = values_for_enum(variant_sizes, &variants, trait_path);
378
379 let headers = quote! {
380 [
381 #(#headers_list,)*
382 ]
383 .concat()
384 };
385
386 Ok(Impl { headers, values })
387}
388
389fn collect_info_enum_inlined(
390 ast: &DataEnum,
391 attrs: &TypeAttributes,
392 enum_name: String,
393) -> Result<Impl, Error> {
394 let orderedvariants = reodered_variants(ast)?;
395
396 let mut variants = Vec::new();
397 let mut names = Vec::new();
398 for variant in orderedvariants {
399 let mut attributes = FieldAttributes::parse(&variant.attrs)?;
400 merge_attributes(&mut attributes, attrs);
401 let mut name = String::new();
402 if !attributes.is_ignored {
403 name = variant_name(variant, &attributes);
404 }
405
406 variants.push(match_variant(variant));
407 names.push(name);
408 }
409
410 let headers = quote! { vec![::std::borrow::Cow::Borrowed(#enum_name)] };
411 let values = quote! {
412 #[allow(unused_variables)]
413 match &self {
414 #(Self::#variants => vec![::std::borrow::Cow::Borrowed(#names)],)*
415 }
416 };
417
418 Ok(Impl { headers, values })
419}
420
421fn info_from_variant(
422 variant: &Variant,
423 attr: &FieldAttributes,
424 attrs: &TypeAttributes,
425 trait_path: &ExprPath,
426) -> Result<Impl, Error> {
427 if attr.inline {
428 let prefix = attr
429 .inline_prefix
430 .as_ref()
431 .map_or_else(|| "", |s| s.as_str());
432 return info_from_fields(
433 &variant.fields,
434 attrs,
435 variant_field_name,
436 prefix,
437 trait_path,
438 );
439 }
440
441 let variant_name = variant_name(variant, attr);
442 let value = if let Some(func) = &attr.display_with {
443 let args = match &attr.display_with_args {
444 Some(args) => {
445 args_to_tokens_with(&Fields::Unit, "e!(self), struct_field_name, args)
446 }
447 None => quote!(&self),
448 };
449
450 let result = use_function(&args, func);
451
452 quote! { ::std::borrow::Cow::from(#result) }
453 } else if let Some(fmt) = &attr.format {
454 let args = attr
455 .format_with_args
456 .as_ref()
457 .and_then(|args| args_to_tokens(&Fields::Unit, struct_field_name, args));
458
459 let call = match args {
460 Some(args) => use_format(fmt, &args),
461 None => use_format_no_args(fmt),
462 };
463
464 quote! { ::std::borrow::Cow::from(#call) }
465 } else {
466 let default_value = "+";
467 quote! { ::std::borrow::Cow::Borrowed(#default_value) }
468 };
469
470 let headers = quote! { vec![::std::borrow::Cow::Borrowed(#variant_name)] };
472 let values = quote! { vec![#value] };
474
475 Ok(Impl { headers, values })
476}
477
478struct Impl {
479 headers: TokenStream,
480 values: TokenStream,
481}
482
483fn get_type_headers(
484 field_type: &Type,
485 inline_prefix: &str,
486 prefix: &str,
487 tabled_trait: &ExprPath,
488) -> TokenStream {
489 if prefix.is_empty() && inline_prefix.is_empty() {
490 quote! { <#field_type as #tabled_trait>::headers() }
491 } else {
492 quote! {
493 <#field_type as #tabled_trait>::headers().into_iter()
494 .map(|header| {
495 let header = format!("{}{}{}", #prefix, #inline_prefix, header);
496 ::std::borrow::Cow::Owned(header)
497 })
498 .collect::<Vec<_>>()
499 }
500 }
501}
502
503fn get_field_fields(
504 field: &TokenStream,
505 field_type: &Type,
506 attr: &FieldAttributes,
507 fields: &Fields,
508 field_name: FieldNameFn,
509 type_attrs: &TypeAttributes,
510) -> TokenStream {
511 let mut field = std::borrow::Cow::Borrowed(field);
512 if let Some(map_fn) = &attr.map {
513 let arg = quote!(&#field);
514 let result = use_function(&arg, map_fn);
515 field = std::borrow::Cow::Owned(result);
516
517 if attr.inline {
518 return quote! {
519 {
520 let field = #field;
521 let fields = field.fields();
522 let fields = fields.into_iter()
523 .map(|f| f.into_owned())
524 .map(std::borrow::Cow::Owned)
525 .collect::<Vec<_>>();
526 fields
527 }
528 };
529 }
530 }
531
532 if attr.inline {
533 return quote! { #field.fields() };
534 }
535
536 if let Some(func) = &attr.display_with {
537 let args = match &attr.display_with_args {
538 Some(args) => args_to_tokens_with(fields, &field, field_name, args),
539 None => quote!(&#field),
540 };
541
542 let result = use_function(&args, func);
543
544 return quote!(vec![::std::borrow::Cow::from(#result)]);
545 } else if let Some(fmt) = &attr.format {
546 let args = attr
547 .format_with_args
548 .as_ref()
549 .and_then(|args| args_to_tokens(fields, field_name, args));
550
551 let call = match args {
552 Some(args) => use_format(fmt, &args),
553 None => use_format_with_one_arg(fmt, &field),
554 };
555
556 return quote!(vec![::std::borrow::Cow::Owned(#call)]);
557 }
558
559 if let Some(i) = find_display_type(field_type, &type_attrs.display_types) {
560 let (_, func, args) = &type_attrs.display_types[i];
561 let args = args_to_tokens_with(fields, &field, field_name, args);
562 let func = use_function(&args, func);
563
564 return quote!(vec![::std::borrow::Cow::from(#func)]);
565 }
566
567 quote!(vec![::std::borrow::Cow::Owned(format!("{}", #field))])
568}
569
570fn args_to_tokens(
571 fields: &Fields,
572 field_name: fn(usize, &Field) -> TokenStream,
573 args: &[FormatArg],
574) -> Option<TokenStream> {
575 if args.is_empty() {
576 return None;
577 }
578
579 let args = args
580 .iter()
581 .map(|arg| fnarg_tokens(arg, fields, field_name))
582 .collect::<Vec<_>>();
583 Some(quote!( #(#args,)* ))
584}
585
586fn args_to_tokens_with(
587 fields: &Fields,
588 field: &TokenStream,
589 field_name: fn(usize, &Field) -> TokenStream,
590 args: &[FormatArg],
591) -> TokenStream {
592 if args.is_empty() {
593 return quote!(&#field);
594 }
595
596 let mut out = vec![quote!(&#field)];
597 for arg in args {
598 let arg = fnarg_tokens(arg, fields, field_name);
599 out.push(arg);
600 }
601
602 quote!( #(#out,)* )
603}
604
605fn find_display_type(ty: &Type, types: &[(TypePath, String, Vec<FormatArg>)]) -> Option<usize> {
606 let p: &TypePath = match ty {
607 Type::Path(path) => path,
608 _ => return None,
609 };
610
611 let args = types.iter().enumerate().rev();
617 for (i, (arg, _, _)) in args {
618 if arg.path == p.path {
619 return Some(i);
620 }
621
622 let arg_segment = arg.path.segments.last();
629 let type_segment = p.path.segments.last();
630 if let Some(arg) = arg_segment {
631 if arg.arguments.is_empty() {
632 if let Some(p) = type_segment {
633 if p.ident == arg.ident {
634 return Some(i);
635 }
636 }
637 }
638 }
639 }
640
641 None
642}
643
644fn use_function(args: &TokenStream, function: &str) -> TokenStream {
645 let path: syn::Result<syn::ExprPath> = syn::parse_str(function);
646 match path {
647 Ok(path) => {
648 quote! { #path(#args) }
649 }
650 Err(_) => {
651 let function = Ident::new(function, proc_macro2::Span::call_site());
652 quote! { #function(#args) }
653 }
654 }
655}
656
657fn use_format(custom_format: &str, args: &TokenStream) -> TokenStream {
658 quote! { format!(#custom_format, #args) }
659}
660
661fn use_format_with_one_arg(custom_format: &str, field: &TokenStream) -> TokenStream {
662 quote! { format!(#custom_format, #field) }
663}
664
665fn use_format_no_args(custom_format: &str) -> TokenStream {
666 quote! { format!(#custom_format) }
667}
668
669fn struct_field_name(index: usize, field: &Field) -> TokenStream {
670 let f = field.ident.as_ref().map_or_else(
671 || Index::from(index).to_token_stream(),
672 quote::ToTokens::to_token_stream,
673 );
674 quote!(self.#f)
675}
676
677fn variant_field_name(index: usize, field: &Field) -> TokenStream {
678 match &field.ident {
679 Some(indent) => indent.to_token_stream(),
680 None => Ident::new(
681 format!("x_{index}").as_str(),
682 proc_macro2::Span::call_site(),
683 )
684 .to_token_stream(),
685 }
686}
687
688fn values_for_enum(
689 variant_sizes: impl Iterator<Item = TokenStream>,
690 variants: &[(&Variant, TokenStream)],
691 tabled_trait: &ExprPath,
692) -> TokenStream {
693 let branches = variants.iter().map(|(variant, _)| match_variant(variant));
694
695 let fields = variants
696 .iter()
697 .map(|(_, values)| values)
698 .collect::<Vec<_>>();
699
700 let mut stream = TokenStream::new();
701 for (i, (branch, fields)) in branches.into_iter().zip(fields).enumerate() {
702 let branch = quote! {
703 Self::#branch => {
704 let offset = offsets[#i];
705 let fields: Vec<::std::borrow::Cow<'_, str>> = #fields;
706
707 for (i, field) in fields.into_iter().enumerate() {
708 out_vec[i+offset] = field;
709 }
710 },
711 };
712
713 stream.append_all(branch);
714 }
715
716 quote! {
717 let mut offsets: &mut [usize] = &mut [0, #(#variant_sizes,)*];
725 for i in 1 .. offsets.len() {
726 offsets[i] += offsets[i-1]
727 }
728
729 let size = <Self as #tabled_trait>::LENGTH;
730 let mut out_vec = vec![::std::borrow::Cow::Borrowed(""); size];
731
732 #[allow(unused_variables)]
733 match &self {
734 #stream
735 _ => return vec![::std::borrow::Cow::Borrowed(""); size], };
737
738 out_vec
739 }
740}
741
742fn variant_idents(v: &Variant) -> Vec<TokenStream> {
743 v.fields
744 .iter()
745 .enumerate()
746 .map(|(index, field)| variant_field_name(index, field))
749 .collect::<Vec<_>>()
750}
751
752fn match_variant(v: &Variant) -> TokenStream {
753 let mut token = TokenStream::new();
754 token.append_all(v.ident.to_token_stream());
755
756 let fields = variant_idents(v);
757
758 match &v.fields {
759 Fields::Named(_) => {
760 token::Brace::default().surround(&mut token, |s| {
761 s.append_separated(fields, quote! {,});
762 });
763 }
764 Fields::Unnamed(_) => {
765 token::Paren::default().surround(&mut token, |s| {
766 s.append_separated(fields, quote! {,});
767 });
768 }
769 Fields::Unit => {}
770 };
771
772 token
773}
774
775fn variant_name(variant: &Variant, attributes: &FieldAttributes) -> String {
776 attributes
777 .rename
778 .clone()
779 .or_else(|| {
780 attributes
781 .rename_all
782 .as_ref()
783 .map(|case| case.cast(variant.ident.to_string()))
784 })
785 .unwrap_or_else(|| variant.ident.to_string())
786}
787
788fn field_header_name(f: &Field, attr: &FieldAttributes, index: usize) -> String {
789 if let Some(name) = &attr.rename {
790 return name.to_string();
791 }
792
793 match &f.ident {
794 Some(name) => {
795 let name = name.to_string();
796 match &attr.rename_all {
797 Some(case) => case.cast(name),
798 None => name,
799 }
800 }
801 None => index.to_string(),
802 }
803}
804
805fn merge_attributes(attr: &mut FieldAttributes, global_attr: &TypeAttributes) {
806 if attr.rename_all.is_none() {
807 attr.rename_all = global_attr.rename_all;
808 }
809}
810
811struct ExprSelfReplace<'a>(Option<(&'a Fields, FieldNameFn)>);
814
815impl syn::visit_mut::VisitMut for ExprSelfReplace<'_> {
816 fn visit_expr_mut(&mut self, node: &mut syn::Expr) {
817 match &node {
818 syn::Expr::Path(path) => {
819 let indent = path.path.get_ident();
820 if let Some(indent) = indent {
821 if indent == "self" {
822 *node = syn::parse_quote! { (&self) };
823 return;
824 }
825 }
826 }
827 syn::Expr::Field(field) => {
828 if let Some((fields, field_name)) = &self.0 {
830 if let syn::Expr::Path(path) = field.base.as_ref() {
833 let indent = path.path.get_ident();
834 if let Some(indent) = indent {
835 if indent != "self" {
836 return;
837 }
838 }
839 }
840
841 let used_field = {
842 match &field.member {
843 syn::Member::Named(ident) => ident.to_string(),
844 syn::Member::Unnamed(index) => index.index.to_string(),
845 }
846 };
847
848 for (i, field) in fields.iter().enumerate() {
852 let field_name_result = (field_name)(i, field);
853 let field_name = field
854 .ident
855 .as_ref()
856 .map_or_else(|| i.to_string(), |i| i.to_string());
857 if field_name == used_field {
858 *node = syn::parse_quote! { #field_name_result };
859 return;
860 }
861 }
862 }
863 }
864 syn::Expr::Macro(_) => {
865 }
880 _ => (),
881 }
882
883 syn::visit_mut::visit_expr_mut(self, node);
885 }
886}
887
888fn fnarg_tokens(arg: &FormatArg, fields: &Fields, field_name: FieldNameFn) -> TokenStream {
889 let mut exp = arg.expr.clone();
890
891 ExprSelfReplace(Some((fields, field_name))).visit_expr_mut(&mut exp);
892
893 quote!(#exp)
894}
895
896fn reodered_variants(ast: &DataEnum) -> Result<Vec<&Variant>, Error> {
897 let mut reorder = HashMap::new();
898 let mut skip = 0;
899 let count = ast.variants.len();
900 for (i, attr) in ast
901 .variants
902 .iter()
903 .map(|v| FieldAttributes::parse(&v.attrs).unwrap_or_default())
904 .enumerate()
905 {
906 if attr.is_ignored {
907 skip += 1;
908 continue;
909 }
910
911 if let Some(order) = attr.order {
912 if order >= count {
913 return Err(Error::message(format!(
914 "An order index '{order}' is out of fields scope"
915 )));
916 }
917
918 reorder.insert(order, i - skip);
919 }
920 }
921
922 let mut orderedvariants = ast.variants.iter().collect::<Vec<_>>();
923 if !reorder.is_empty() {
924 orderedvariants = reorder_fields(&reorder, &orderedvariants);
925 }
926
927 Ok(orderedvariants)
928}
929
930fn get_crate_name_expr(attrs: &TypeAttributes) -> Result<ExprPath, Error> {
931 let crate_name = attrs
932 .crate_name
933 .clone()
934 .unwrap_or_else(|| String::from("::tabled"));
935 let crate_name = parse_crate_name(&crate_name)?;
936 Ok(create_tabled_trait_path(crate_name))
937}
938
939fn parse_crate_name(name: &str) -> Result<ExprPath, Error> {
940 syn::parse_str(name).map_err(|_| Error::message("unexpected crate attribute type"))
941}
942
943fn create_tabled_trait_path(mut p: ExprPath) -> ExprPath {
944 p.path.segments.push(PathSegment {
945 ident: Ident::new("Tabled", proc_macro2::Span::call_site()),
946 arguments: syn::PathArguments::None,
947 });
948 p
949}