tabled_derive/
lib.rs

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
192// todo: refactoring. instead of using a lambda + prefix
193// we could just not emit `self.` `_x` inside
194// So the called would prefix it on its own
195fn 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                // NOTE: A panic already must have been raised
317                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    // reorder variants according to order (if set)
358    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, &quote!(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    // we need exactly string because of it must be inlined as string
471    let headers = quote! { vec![::std::borrow::Cow::Borrowed(#variant_name)] };
472    // we need exactly string because of it must be inlined as string
473    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    // NOTICE:
612    // We do iteration in a back order to satisfy a later set argument first.
613    //
614    // TODO: Maybe we shall change the data structure for it rather then doing a reverse iteration?
615    // I am just saying it's dirty a little.
616    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        // NOTICE:
623        // There's a specical case where we wanna match a type without a generic,
624        // e.g. 'Option' with which we wanna match all 'Option's.
625        //
626        // Because in the scope we can only have 1 type name, it's considered to be good,
627        // and nothing must be broken.
628        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        // To be able to insert variant fields in proper places we do this MAGIC with offset.
718        //
719        // We check headers output as it's static and has an information
720        // about length of each field header if it was inlined.
721        //
722        // It's a bit strange trick but I haven't found any better
723        // how to calculate a size and offset.
724        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], // variant is hidden so we return an empty vector
736        };
737
738        out_vec
739    }
740}
741
742fn variant_idents(v: &Variant) -> Vec<TokenStream> {
743    v.fields
744        .iter()
745        .enumerate()
746        // we intentionally not ignore these fields to be able to build a pattern correctly
747        // .filter(|(_, field)| !Attr::parse(&field.attrs).is_ignored())
748        .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
811// to resolve invocation issues withing a macros calls
812// we need such a workround
813struct 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                // treating a enum variant structs which we can't reference by 'self'
829                if let Some((fields, field_name)) = &self.0 {
830                    // check that it's plain self reference
831
832                    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                    // We find the corresponding field in the local object fields instead of using self,
849                    // which would be a higher level object. This is for nested structures.
850
851                    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                // NOTE: Can we parse inners of Macros?
866                //
867                // A next example will fail on `self.f1` usage
868                // with such error 'expected value, found module `self`'
869                //
870                // ```
871                // some_macro! {
872                //     struct Something {
873                //         #[tabled(display("_", format!("", self.f1)))]
874                //         field: Option<sstr>,
875                //         f1: usize,
876                //     }
877                // }
878                // ```
879            }
880            _ => (),
881        }
882
883        // Delegate to the default impl to visit nested expressions.
884        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}