cli_table_derive/
table.rs

1use proc_macro2::TokenStream;
2use quote::{quote, quote_spanned};
3use syn::{DeriveInput, Result};
4
5use crate::context::Context;
6
7pub fn table(input: DeriveInput) -> Result<TokenStream> {
8    // Create context for generating expressions
9    let context = Context::new(&input)?;
10
11    // Used in the quasi-quotation below as `#name`
12    let name = context.container.name;
13
14    // Fetch cli_table crate name
15    let cli_table = &context.container.crate_name;
16
17    // Split a type's generics into the pieces required for implementing a trait for that type
18    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
19
20    let mut field_titles = Vec::new();
21    let mut field_rows = Vec::new();
22
23    for field in context.fields.into_iter() {
24        field_titles.push(field.title);
25
26        let ident = field.ident;
27        let justify = field.justify;
28        let align = field.align;
29        let color = field.color;
30        let bold = field.bold;
31        let display_fn = field.display_fn;
32        let customize_fn = field.customize_fn;
33        let span = field.span;
34
35        let cell = match display_fn {
36            None => quote_spanned! {span=>
37                &self. #ident
38            },
39            Some(display_fn) => {
40                let span = display_fn.span();
41                quote_spanned! {span=>
42                    #display_fn (&self. #ident)
43                }
44            }
45        };
46
47        let mut row = quote_spanned! {span=>
48            #cli_table ::Cell::cell(#cell)
49        };
50
51        if let Some(justify) = justify {
52            row = quote_spanned! {span=>
53                #row .justify(#justify)
54            };
55        }
56
57        if let Some(align) = align {
58            row = quote_spanned! {span=>
59                #row .align(#align)
60            };
61        }
62
63        if let Some(color) = color {
64            row = quote_spanned! {span=>
65                #cli_table ::Style::foreground_color(#row, ::core::convert::From::from(#color))
66            };
67        }
68
69        if let Some(bold) = bold {
70            row = quote_spanned! {span=>
71                #cli_table ::Style::bold(#row, #bold)
72            };
73        }
74
75        if let Some(customize_fn) = customize_fn {
76            row = quote_spanned! {span=>
77                #customize_fn (#row, &self. #ident)
78            };
79        }
80
81        field_rows.push(row);
82    }
83
84    // Build the output, possibly using quasi-quotation
85    Ok(quote! {
86        #[automatically_derived]
87        impl #impl_generics #cli_table ::Title for #name #ty_generics # where_clause{
88            fn title() -> #cli_table ::RowStruct {
89                let title: ::std::vec::Vec<#cli_table ::CellStruct> = ::std::vec![
90                    #(#cli_table ::Style::bold(#cli_table ::Cell::cell(#field_titles), true),)*
91                ];
92
93                #cli_table ::Row::row(title)
94            }
95        }
96
97        #[automatically_derived]
98        impl #impl_generics #cli_table ::Row for & #name #ty_generics # where_clause{
99            fn row(self) -> #cli_table ::RowStruct {
100                let row: ::std::vec::Vec<#cli_table ::CellStruct> = ::std::vec![
101                    #(#field_rows,)*
102                ];
103
104                #cli_table ::Row::row(row)
105            }
106        }
107
108        #[automatically_derived]
109        impl #impl_generics #cli_table ::Row for #name #ty_generics # where_clause{
110            fn row(self) -> #cli_table ::RowStruct {
111                #cli_table ::Row::row(&self)
112            }
113        }
114    })
115}