cli_table_derive/context/
fields.rs1use 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}