simple_tables_derive/
lib.rs1extern crate proc_macro;
2extern crate proc_macro2;
3extern crate syn;
5#[macro_use]
7extern crate quote;
8
9use proc_macro::TokenStream;
10use syn::{ItemStruct, parse_macro_input};
11use syn::parse::Parser;
12use proc_macro2::{Ident as Ident2, TokenStream as TokenStream2};
13use quote::ToTokens;
14
15#[proc_macro_attribute]
18pub fn table_row(_attrs: TokenStream, input: TokenStream) -> TokenStream {
19 let item_struct = parse_macro_input!(input as ItemStruct);
20
21 let struct_name = &item_struct.ident;
22
23 let fields: Vec<(String, syn::Type)>;
24 let mut ident_fields: Vec<(Ident2, syn::Type)> = Vec::new();
25 if let syn::Fields::Named(ref _fields) = item_struct.fields {
26 let _fields = &_fields.named;
27 fields = _fields.iter().map(|field| {
28 if let Some(ident) = &field.ident {
29 let field_name: String = ident.to_string();
30 let field_type = &field.ty;
31 let entry = (field_name, field_type.clone());
32
33 ident_fields.push((ident.clone(), field_type.clone()));
34
35 entry
36 } else {
37 panic!("Only named fields are supported.")
38 }
40 }).collect();
41 } else {
42 panic!("The row struct has no fields.");
43 }
44
45 let mut field_names: Vec<String> = Vec::new();
46 let mut field_types: Vec<syn::Type> = Vec::new();
47
48 fields.iter()
50 .for_each(|field| {
51 let (_name, _type) = field;
52 field_names.push(_name.to_owned());
53 field_types.push(_type.to_owned());
54 });
55 let field_types_strings: Vec<String> = field_types.iter().map(|v| v.to_token_stream().to_string()).collect();
56
57 let field_len = fields.iter().count();
58 let mut get_field_str_elements: Vec<proc_macro2::TokenStream> = Vec::new();
59 for ident_field in ident_fields {
60 let ident = ident_field.0;
61 let field = quote!( self.#ident );
62 get_field_str_elements.push(field);
63 }
64 let get_field_str = quote!(
65 fn get_field_str(&self) -> Vec<String> {
66 vec![ #(#get_field_str_elements.to_string(),)* ]
67 }
68 );
69 TokenStream::from (
70 quote! (
71 use simple_tables::core::TableRow as TableRowTrait;
72
73 #[derive(Debug, Clone)]
74 #item_struct
75
76 impl #struct_name {
77 const FIELDS: [&'static str; #field_len] = [#(#field_names),*];
78 const TYPES: [&'static str; #field_len] = [#(#field_types_strings),*];
80
81 #get_field_str
82 }
83
84 impl TableRowTrait for #struct_name {
85 fn get_fields() -> Vec<&'static str> {
86 Self::FIELDS.to_vec()
87 }
88 fn get_field_types() -> Vec<&'static str> {
89 Self::TYPES.to_vec()
90 }
91 fn field_count() -> usize {
92 return #field_len;
93 }
94 }
95 )
96 )
97}
98
99#[proc_macro_attribute]
114pub fn table(attrs: TokenStream, input: TokenStream) -> TokenStream {
115 let mut item_struct = parse_macro_input!(input as ItemStruct);
116
117 let mut current_attr: Option<&str> = None;
122 let mut table_row_struct: Option<Ident2> = None;
123 let mut uid_field_name: Option<String> = None;
124 attrs.into_iter().for_each(|token| {
125 match token {
126 proc_macro::TokenTree::Group(group) => panic!("Unexpected attribute: {}", group),
128 proc_macro::TokenTree::Ident(ident) => {
129 match &ident.to_string().as_str() {
130 &"rows" => current_attr = Some("rows"),
131 &"uid" => current_attr = Some("uid"),
132 val => {
133 if current_attr == Some("rows") {
134 table_row_struct = Some(Ident2::new(val, proc_macro2::Span::call_site()));
135 } else {
136 panic!("Unexpected token: {}", val);
137 }
138 }
139 }
140 },
141 proc_macro::TokenTree::Punct(punct) => {
142 if punct.as_char() == '=' && current_attr.is_some() {
143 } else if punct.as_char() == ',' {
145 if current_attr == None {
146 panic!("Unexpected character: {}", punct.as_char());
147 } else {
148 current_attr = None;
149 }
150 } else {
151 panic!("Unkown character: {}", punct);
152 }
153 },
154 proc_macro::TokenTree::Literal(literal) => {
155 if current_attr == Some("uid") {
156 uid_field_name = Some(literal.to_string().trim_matches(|c| c == '\"').to_string());
158 }
159 }
160 }
161 });
162
163 if let Some(table_row_struct) = table_row_struct {
164 let field_to_add = quote!(rows: Vec<#table_row_struct>);
165 let struct_name = &item_struct.ident;
166 if let syn::Fields::Named(ref mut fields) = item_struct.fields {
168 fields.named.push(
169 syn::Field::parse_named
170 .parse2(field_to_add)
171 .unwrap(),
172 );
173 }
174
175 let uid_code: TokenStream2;
176 if let Some(uid) = uid_field_name {
177 uid_code = quote!(const UID: &'static str = #uid;);
178 } else {
179 uid_code = quote!();
180 }
181
182 let to_string = quote!(
183 let field_names = dev_simple_tables_core_table_row_type::get_fields();
185 let mut row_values: Vec<Vec<String>> = Vec::new();
187 for row in &self.rows {
188 row_values.push(row.get_field_str());
189 }
190 let mut column_sizes: Vec<usize> = vec![0; field_names.len()];
192 row_values.iter().for_each(|(row_val)| {
193 row_val.iter().enumerate().for_each(|(col, col_val)| {
194 let len = col_val.to_string().chars().count();
195 if column_sizes[col] < len {
196 column_sizes[col] = len;
197 }
198 });
199 });
200
201 let mut top_line: String = String::from("+-");
202 let mut headers: String = String::from("| ");
203 let mut bottom_line: String = String::from("+=");
204 let mut actual_column_sizes: Vec<usize> = column_sizes.clone();
205 let total_columns = column_sizes.len();
206 column_sizes.into_iter().enumerate().for_each(|(col, col_size)| {
207 let mut local_col_size = col_size.clone();
208 let field_name = field_names[col];
209 let field_len = field_name.chars().count();
210 let left_over = if field_len > local_col_size {
212 local_col_size = field_len;
213 actual_column_sizes[col] = field_len;
214 0
215 } else {
216 local_col_size - field_len
217 };
218 top_line.push_str(format!("{}-+", "-".repeat(local_col_size)).as_str());
219 headers.push_str(format!("{}{} |", field_name, " ".repeat(left_over)).as_str());
220 bottom_line.push_str(format!("{}=+", "=".repeat(local_col_size)).as_str());
221 if col != total_columns - 1 {
222 top_line.push_str("-");
223 headers.push_str(" ");
224 bottom_line.push_str("=");
225 }
226 });
227
228 let mut cells: String = String::from("| ");
230 row_values.into_iter().enumerate().for_each(|(row, row_val)| {
231 if row != 0 {
232 cells.push_str("\n| ");
233 }
234 row_val.into_iter().enumerate().for_each(|(col, cell_val)| {
235 let left_over = actual_column_sizes[col] - cell_val.chars().count();
236 cells.push_str(format!("{}{} |", cell_val, " ".repeat(left_over)).as_str());
237 if col != total_columns - 1 {
238 cells.push_str(" ");
239 }
240 });
241 cells.push_str(format!("\n{}", top_line).as_str());
243 });
244 );
245 let impl_to_string = quote!(
246 impl ToString for #struct_name {
247 fn to_string(&self) -> String {
248 #to_string
249 format!("{}\n{}\n{}\n{}", top_line, headers, bottom_line, cells)
250 }
251 }
252 );
253
254 let output = quote! (
255 #[automatically_derived]
256 #item_struct
257
258 impl #struct_name {
259 #uid_code
260 }
261
262 type dev_simple_tables_core_table_row_type = #table_row_struct; impl simple_tables::core::Table<#table_row_struct> for #struct_name {
265 fn new() -> #struct_name {
266 #struct_name { rows: Vec::new() }
267 }
268
269 fn from_vec(vec: &Vec<#table_row_struct>) -> #struct_name {
270 #struct_name { rows: vec.to_vec() }
271 }
272
273 fn get_rows(&self) -> &Vec<#table_row_struct> {
274 &self.rows
275 }
276
277 fn get_rows_mut(&mut self) -> &mut Vec<#table_row_struct> {
278 &mut self.rows
279 }
280
281 fn push(&mut self, row: #table_row_struct) {
282 self.rows.push(row);
283 }
284
285 fn insert_top(&mut self, row: #table_row_struct) {
286 self.rows.insert(0, row);
287 }
288
289 fn insert(&mut self, i: usize, row: #table_row_struct) {
290 self.rows.insert(i, row);
291 }
292 }
293
294 #impl_to_string
295
296 impl std::fmt::Debug for #struct_name {
297 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
298 #to_string
299 let output = format!("{}\n{}\n{}\n{}", top_line, headers, bottom_line, cells);
300 write!(f, "{}", output)
301 }
302 }
303 );
304
305 TokenStream::from(output)
306 } else {
307 panic!("Please specify a struct to use as the data type for the table rows. \
308 e.g. `#[table(rows = TableRowStruct)]`. Refer to the `table` macro documentation for more info.")
309 }
310}
311
312