From 16d5de65ea2d9ba36e4c40df2cb65c2715974b56 Mon Sep 17 00:00:00 2001 From: Andrea Stevanato Date: Tue, 27 Aug 2019 20:53:25 +0200 Subject: [PATCH 1/3] Fixing the mess that I've made with github and removed unnecessary &self from get_field_name method --- prettytable-rs-derive/.gitignore | 4 +++ prettytable-rs-derive/Cargo.toml | 14 +++++++++ prettytable-rs-derive/src/lib.rs | 29 ++++++++++++++++++ prettytable-rs-derive/tests/derive_test.rs | 23 +++++++++++++++ src/lib.rs | 34 ++++++++++++++++++++++ 5 files changed, 104 insertions(+) create mode 100644 prettytable-rs-derive/.gitignore create mode 100644 prettytable-rs-derive/Cargo.toml create mode 100644 prettytable-rs-derive/src/lib.rs create mode 100644 prettytable-rs-derive/tests/derive_test.rs diff --git a/prettytable-rs-derive/.gitignore b/prettytable-rs-derive/.gitignore new file mode 100644 index 0000000000..93ffd2f3a4 --- /dev/null +++ b/prettytable-rs-derive/.gitignore @@ -0,0 +1,4 @@ +/target/ +/Cargo.lock + +\.vscode/ diff --git a/prettytable-rs-derive/Cargo.toml b/prettytable-rs-derive/Cargo.toml new file mode 100644 index 0000000000..057b9dd3b3 --- /dev/null +++ b/prettytable-rs-derive/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "prettytable-rs-derive" +version = "0.1.0" +authors = ["Andrea Stevanato "] +edition = "2018" + +[dependencies] +syn = {version = "0.15.42", features = ["full"]} +quote = "0.6.13" +prettytable-rs = { path = "../" } + +[lib] +proc-macro = true +name = "prettytable_derive" \ No newline at end of file diff --git a/prettytable-rs-derive/src/lib.rs b/prettytable-rs-derive/src/lib.rs new file mode 100644 index 0000000000..1f991cff18 --- /dev/null +++ b/prettytable-rs-derive/src/lib.rs @@ -0,0 +1,29 @@ +extern crate proc_macro; + +use proc_macro::TokenStream; +use quote::quote; +use syn::{parse_macro_input, ItemStruct}; + +#[proc_macro_derive(TableElem)] +pub fn derive_table_elem(input: TokenStream) -> TokenStream { + let parsed_input = parse_macro_input!(input as ItemStruct); + + let struct_name = &parsed_input.ident; + let field = &parsed_input.fields; + + // Get struct field name + let f_name: Vec = field.iter().map(|f| f.ident.clone().unwrap()).collect(); + let f_name_str: Vec = f_name.iter().map(|f| f.to_string()).collect(); + + TokenStream::from(quote! { + impl prettytable::TableElem for #struct_name { + fn get_field_name() -> Vec<&'static str> { + vec![#(#f_name_str),*] + } + + fn get_field(self) -> Vec { + vec![#(self.#f_name.into()),*] + } + } + }) +} \ No newline at end of file diff --git a/prettytable-rs-derive/tests/derive_test.rs b/prettytable-rs-derive/tests/derive_test.rs new file mode 100644 index 0000000000..37d9af69fd --- /dev/null +++ b/prettytable-rs-derive/tests/derive_test.rs @@ -0,0 +1,23 @@ +use prettytable_derive::TableElem; +use prettytable::TableElem; + +#[derive(TableElem)] +struct NameStruct { + name: String, + surname: String, +} + +#[test] +fn test_get_field_name() { + assert_eq!(vec!["name", "surname"], NameStruct::get_field_name()); +} + +#[test] +fn test_get_field() { + let t = NameStruct { + name: "real_name".to_string(), + surname: "real_surname".to_string(), + }; + + assert_eq!(vec!["real_name", "real_surname"], t.get_field()); +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 977f58d306..0ae23e49c5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -388,6 +388,40 @@ impl Table { pub fn print_html(&self, out: &mut T) -> Result<(), Error> { self.as_ref().print_html(out) } + + /// Construct the table from the `TableElem` elements vector. + /// + /// A struct may implements the `TableElem` trait using the derive proc-macro + /// from `prettytable-rs-derive` crate + pub fn from_vec(v: Vec) -> Self { + // Create the table + let mut table = Self::new(); + + // Set the table titles with the name of the struct fields + table.set_titles(Row::new( + T::get_field_name() + .iter() + .map(|f| Cell::new(f)) + .collect(), + )); + + v.into_iter().for_each(|r| { + table.add_row(Row::new( + r.get_field().iter().map(|elem| Cell::new(elem)).collect(), + )); + }); + + table + } +} + +/// A table may be costructed from a vector of elements that implements the +/// `TableElem` trait +pub trait TableElem { + /// Returns a vector containing the name of the struct fields + fn get_field_name() -> Vec<&'static str>; + /// Returns a vector that contains the contents of the struct's fields + fn get_field(self) -> Vec; } impl Index for Table { From f05482e4eac8a3fcc5db2f014cbf5ca1e24637b4 Mon Sep 17 00:00:00 2001 From: Andrea Stevanato Date: Wed, 28 Aug 2019 12:26:15 +0200 Subject: [PATCH 2/3] adding test case of from_vec --- src/lib.rs | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 0ae23e49c5..cf86ba9c03 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1095,4 +1095,42 @@ mod tests { assert!(table.print_html(&mut writer).is_ok()); assert_eq!(writer.as_string().replace("\r\n", "\n"), out); } + + #[test] + fn from_vec() { + struct Test { + t1: String, + t2: String, + t3: String, + }; + + impl crate::TableElem for Test { + fn get_field_name() -> Vec<&'static str> { + vec!["t1", "t2", "t3"] + } + + fn get_field(self) -> Vec { + vec![self.t1.into(), self.t2.into(), self.t3.into()] + } + } + + let v = vec![ + Test {t1: "a".to_string(), t2: "bc".to_string(), t3: "def".to_string()}, + Test {t1: "def".to_string(), t2: "bc".to_string(), t3: "a".to_string()}, + ]; + + let table = Table::from_vec(v); + let out = "\ ++-----+----+-----+ +| t1 | t2 | t3 | ++=====+====+=====+ +| a | bc | def | ++-----+----+-----+ +| def | bc | a | ++-----+----+-----+ +"; + + assert_eq!(table.to_string().replace("\r\n", "\n"), out); + assert_eq!(7, table.print(&mut StringWriter::new()).unwrap()); + } } From 58fa5e7e0170f6a5681688438a3dc4b0272b59d0 Mon Sep 17 00:00:00 2001 From: Andrea Stevanato Date: Tue, 10 Sep 2019 00:16:01 +0200 Subject: [PATCH 3/3] reference instead of borrowing --- prettytable-rs-derive/src/lib.rs | 4 ++-- src/lib.rs | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/prettytable-rs-derive/src/lib.rs b/prettytable-rs-derive/src/lib.rs index 1f991cff18..41c20a7944 100644 --- a/prettytable-rs-derive/src/lib.rs +++ b/prettytable-rs-derive/src/lib.rs @@ -21,8 +21,8 @@ pub fn derive_table_elem(input: TokenStream) -> TokenStream { vec![#(#f_name_str),*] } - fn get_field(self) -> Vec { - vec![#(self.#f_name.into()),*] + fn get_field(&self) -> Vec { + vec![#(self.#f_name.to_string()),*] } } }) diff --git a/src/lib.rs b/src/lib.rs index cf86ba9c03..3a35eadec8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -393,7 +393,7 @@ impl Table { /// /// A struct may implements the `TableElem` trait using the derive proc-macro /// from `prettytable-rs-derive` crate - pub fn from_vec(v: Vec) -> Self { + pub fn from_vec(v: &Vec) -> Self { // Create the table let mut table = Self::new(); @@ -421,7 +421,7 @@ pub trait TableElem { /// Returns a vector containing the name of the struct fields fn get_field_name() -> Vec<&'static str>; /// Returns a vector that contains the contents of the struct's fields - fn get_field(self) -> Vec; + fn get_field(&self) -> Vec; } impl Index for Table { @@ -1109,8 +1109,8 @@ mod tests { vec!["t1", "t2", "t3"] } - fn get_field(self) -> Vec { - vec![self.t1.into(), self.t2.into(), self.t3.into()] + fn get_field(&self) -> Vec { + vec![self.t1.to_string(), self.t2.to_string(), self.t3.to_string()] } } @@ -1119,7 +1119,7 @@ mod tests { Test {t1: "def".to_string(), t2: "bc".to_string(), t3: "a".to_string()}, ]; - let table = Table::from_vec(v); + let table = Table::from_vec(&v); let out = "\ +-----+----+-----+ | t1 | t2 | t3 |