cli_table_derive/context/
fields.rs

1use proc_macro2::{Span, TokenStream};
2use quote::ToTokens;
3use syn::{
4    Data, DeriveInput, Error, Expr, Field as SynField, Fields as SynFields, Ident, Index, Lit,
5    LitBool, LitStr, Result, spanned::Spanned,
6};
7
8use crate::utils::get_attributes;
9
10pub struct Fields {
11    fields: Vec<Field>,
12}
13
14impl Fields {
15    pub fn new(input: &DeriveInput) -> Result<Self> {
16        match input.data {
17            Data::Struct(ref data_struct) => Self::from_fields(&data_struct.fields),
18            _ => Err(Error::new_spanned(
19                input,
20                "`cli_table` derive macros can only be used on structs",
21            )),
22        }
23    }
24
25    pub fn into_iter(self) -> impl Iterator<Item = Field> {
26        self.fields.into_iter()
27    }
28
29    fn from_fields(syn_fields: &SynFields) -> Result<Self> {
30        let mut fields = Vec::new();
31
32        for (index, syn_field) in syn_fields.into_iter().enumerate() {
33            let field = Field::new(syn_field, index)?;
34
35            if let Some(field) = field {
36                fields.push(field);
37            }
38        }
39
40        fields.sort_by(|left, right| left.order.cmp(&right.order));
41
42        Ok(Fields { fields })
43    }
44}
45
46pub struct Field {
47    pub ident: TokenStream,
48    pub title: LitStr,
49    pub justify: Option<Expr>,
50    pub align: Option<Expr>,
51    pub color: Option<Expr>,
52    pub bold: Option<LitBool>,
53    pub order: usize,
54    pub display_fn: Option<Ident>,
55    pub customize_fn: Option<Ident>,
56    pub span: Span,
57}
58
59impl Field {
60    pub fn new(field: &SynField, index: usize) -> Result<Option<Self>> {
61        let ident = field
62            .ident
63            .as_ref()
64            .map(ToTokens::into_token_stream)
65            .unwrap_or_else(|| Index::from(index).into_token_stream());
66        let span = field.span();
67
68        let mut title = None;
69        let mut justify = None;
70        let mut align = None;
71        let mut color = None;
72        let mut bold = None;
73        let mut order = None;
74        let mut display_fn = None;
75        let mut customize_fn = None;
76        let mut skip = None;
77
78        let field_attributes = get_attributes(&field.attrs)?;
79
80        for (key, value) in field_attributes {
81            if key.is_ident("name") || key.is_ident("title") {
82                title = Some(match value {
83                    Lit::Str(lit_str) => Ok(lit_str),
84                    bad => Err(Error::new_spanned(
85                        bad,
86                        "Invalid value for #[table(title = \"field_name\")]",
87                    )),
88                }?);
89            } else if key.is_ident("justify") {
90                justify = Some(match value {
91                    Lit::Str(lit_str) => lit_str.parse::<Expr>(),
92                    bad => Err(Error::new_spanned(
93                        bad,
94                        "Invalid value for #[table(justify = \"value\")]",
95                    )),
96                }?);
97            } else if key.is_ident("align") {
98                align = Some(match value {
99                    Lit::Str(lit_str) => lit_str.parse::<Expr>(),
100                    bad => Err(Error::new_spanned(
101                        bad,
102                        "Invalid value for #[table(align = \"value\")]",
103                    )),
104                }?);
105            } else if key.is_ident("color") {
106                color = Some(match value {
107                    Lit::Str(lit_str) => lit_str.parse::<Expr>(),
108                    bad => Err(Error::new_spanned(
109                        bad,
110                        "Invalid value for #[table(color = \"value\")]",
111                    )),
112                }?);
113            } else if key.is_ident("bold") {
114                bold = Some(match value {
115                    Lit::Bool(lit_bool) => Ok(lit_bool),
116                    bad => Err(Error::new_spanned(bad, "Invalid value for #[table(bold)]")),
117                }?);
118            } else if key.is_ident("order") {
119                order = Some(match value {
120                    Lit::Int(lit_int) => lit_int.base10_parse::<usize>(),
121                    bad => Err(Error::new_spanned(
122                        bad,
123                        "Invalid value for #[table(order = <usize>)]",
124                    )),
125                }?);
126            } else if key.is_ident("display_fn") {
127                display_fn = Some(match value {
128                    Lit::Str(lit_str) => lit_str.parse::<Ident>(),
129                    bad => Err(Error::new_spanned(
130                        bad,
131                        "Invalid value for #[table(display_fn = \"value\")]",
132                    )),
133                }?);
134            } else if key.is_ident("customize_fn") {
135                customize_fn = Some(match value {
136                    Lit::Str(lit_str) => lit_str.parse::<Ident>(),
137                    bad => Err(Error::new_spanned(
138                        bad,
139                        "Invalid value for #[table(display_fn = \"value\")]",
140                    )),
141                }?);
142            } else if key.is_ident("skip") {
143                skip = Some(match value {
144                    Lit::Bool(lit_bool) => Ok(lit_bool),
145                    bad => Err(Error::new_spanned(bad, "Invalid value for #[table(bold)]")),
146                }?);
147            }
148        }
149
150        if let Some(skip) = skip {
151            if skip.value {
152                return Ok(None);
153            }
154        }
155
156        let mut field_builder = Self::builder(ident, span);
157
158        if let Some(title) = title {
159            field_builder.title(title);
160        }
161
162        if let Some(justify) = justify {
163            field_builder.justify(justify);
164        }
165
166        if let Some(align) = align {
167            field_builder.align(align);
168        }
169
170        if let Some(color) = color {
171            field_builder.color(color);
172        }
173
174        if let Some(bold) = bold {
175            field_builder.bold(bold);
176        }
177
178        if let Some(order) = order {
179            field_builder.order(order);
180        }
181
182        if let Some(display_fn) = display_fn {
183            field_builder.display_fn(display_fn);
184        }
185
186        if let Some(customize_fn) = customize_fn {
187            field_builder.customize_fn(customize_fn);
188        }
189
190        Ok(Some(field_builder.build()))
191    }
192
193    fn builder(ident: TokenStream, span: Span) -> FieldBuilder {
194        FieldBuilder::new(ident, span)
195    }
196}
197
198struct FieldBuilder {
199    ident: TokenStream,
200    title: Option<LitStr>,
201    justify: Option<Expr>,
202    align: Option<Expr>,
203    color: Option<Expr>,
204    bold: Option<LitBool>,
205    order: Option<usize>,
206    display_fn: Option<Ident>,
207    customize_fn: Option<Ident>,
208    span: Span,
209}
210
211impl FieldBuilder {
212    fn new(ident: TokenStream, span: Span) -> Self {
213        Self {
214            ident,
215            title: None,
216            justify: None,
217            align: None,
218            color: None,
219            bold: None,
220            order: None,
221            display_fn: None,
222            customize_fn: None,
223            span,
224        }
225    }
226
227    fn title(&mut self, title: LitStr) -> &mut Self {
228        self.title = Some(title);
229        self
230    }
231
232    fn justify(&mut self, justify: Expr) -> &mut Self {
233        self.justify = Some(justify);
234        self
235    }
236
237    fn align(&mut self, align: Expr) -> &mut Self {
238        self.align = Some(align);
239        self
240    }
241
242    fn color(&mut self, color: Expr) -> &mut Self {
243        self.color = Some(color);
244        self
245    }
246
247    fn bold(&mut self, bold: LitBool) -> &mut Self {
248        self.bold = Some(bold);
249        self
250    }
251
252    fn order(&mut self, order: usize) -> &mut Self {
253        self.order = Some(order);
254        self
255    }
256
257    fn display_fn(&mut self, display_fn: Ident) -> &mut Self {
258        self.display_fn = Some(display_fn);
259        self
260    }
261
262    fn customize_fn(&mut self, customize_fn: Ident) -> &mut Self {
263        self.customize_fn = Some(customize_fn);
264        self
265    }
266
267    fn build(self) -> Field {
268        let ident = self.ident;
269        let justify = self.justify;
270        let align = self.align;
271        let color = self.color;
272        let bold = self.bold;
273        let order = self.order.unwrap_or(usize::MAX);
274        let display_fn = self.display_fn;
275        let customize_fn = self.customize_fn;
276        let span = self.span;
277
278        let title = self
279            .title
280            .unwrap_or_else(|| LitStr::new(&ident.to_string(), span));
281
282        Field {
283            ident,
284            title,
285            justify,
286            align,
287            color,
288            bold,
289            order,
290            display_fn,
291            customize_fn,
292            span,
293        }
294    }
295}