5 unstable releases
0.3.0 | Jan 17, 2022 |
---|---|
0.2.1 | Jan 12, 2022 |
0.2.0 | Jan 12, 2022 |
0.1.1 | Jan 11, 2022 |
0.1.0 | Jan 11, 2022 |
#500 in Visualization
Used in newsly
19KB
152 lines
Simple Tables is a rust crate for easily creating table structures. This is made easy using macros.
Table of Contents
Overview
Creating tables
To create a table, you use the macros table_row
to indicate the structure of a row and table
to use a struct as a
table for that row.
Example
use simple_tables::macros::{table_row, table};
#[table_row]
struct MyTableRow {
id: u32,
name: String,
email: String,
address: String
}
#[table(rows = MyTableRow)]
struct MyTable {}
These macros will implement the TableRow
and Table
trait respectively. You could also implement these manually.
Functions
The traits TableRow
and Table
define a collection a functions, most of them with default implementations. Using the
table_row
and table
macros will implement these traits and their respective functions for the struct you are targetting.
The macros also provide some additional functions.
ToString
The macros also implement the ToString
trait for tables, so you can use the to_string
function.
Example
use simple_tables::Table;
let rows: Vec<MyTableRow> = vec![
MyTableRow{ id: 0, name: "David Bowie".to_string(), email: "david@bowie.com".to_string(), address: "England".to_string()},
MyTableRow{ id: 1, name: "David Gilmour".to_string(), email: "david@gilmour.com".to_string(), address: "England".to_string()},
MyTableRow{ id: 2, name: "Opeth".to_string(), email: "info@opeth.com".to_string(), address: "Sweden".to_string()},
MyTableRow{ id: 3, name: "The Beatles".to_string(), email: "info@beatles.com".to_string(), address: "England".to_string()}
];
let table = MyTable::from_vec(&rows);
let s = table.to_string();
println!("{}", s);
The output will be:
+----+---------------+-------------------+---------+
| id | name | email | address |
+====+===============+===================+=========+
| 0 | David Bowie | david@bowie.com | England |
+----+---------------+-------------------+---------+
| 1 | David Gilmour | david@gilmour.com | England |
+----+---------------+-------------------+---------+
| 2 | Opeth | info@opeth.com | Sweden |
+----+---------------+-------------------+---------+
| 3 | The Beatles | info@beatles.com | England |
+----+---------------+-------------------+---------+
Creating a new table instance
let empty_table = MyTable::new();
let populated_table = MyTable::from_vec(&vec);
Get rows
You can get the rows using one of the getters:
table.get_rows()
table.get_rows_mut()
table.get_row_at(index)
Get columns
You can get all values of a column using the get_column
function. This function takes in a closure to get the value
of each entry.
Example
#[table_row]
struct MyTableRow2 {
id: u32,
name: String
}
#[table(rows = TableRow)]
struct MyTable2 {}
let vec: Vec<MyTableRow2> = vec![
MyTableRow2{id: 1, name: String::from("Metallica")},
MyTableRow2{id: 2, name: String::from("Slipknot")}
];
let table2 = MyTable2::from_vec(&vec);
let ids: Vec<u32> = table2.get_column(|row| row.id); // The function takes in a closure
assert_eq!(vec![1,2], ids);
Inserting rows
Examples
let row = MyTableRow { id: 4, name: "Pink Floyd", email: "pink@floyd.com", address: "England"};
// Appending the row to the end of the table
table.push(row);
// Inserting a row at the top of the table
table.insert_top(row);
// Inserting a row in the second position
table.insert(2, row);
Column and row count
You can get the amount of columns using the column_count()
function on your table. You can also get the amount of rows
using the row_count()
.
Tables with UID's
We can specify a table with a unique identifier. The following example shows how to do this:
use simple_tables::IdTable;
#[table_row]
struct MyTableRow {
id: u32,
name: String
}
#[table(rows = MyTableRow)]
struct MyTable {}
// When you need a table with rows containing uid's, you will have to manually implement the
// `get_id_from_row` function
// The `IdTable` takes in 2 type parameters, one for the id's type and one for the rows's type
impl IdTable<u32, MyTableRow> for MyTable {
// This function will simply return the id field from the row
fn get_id_from_row(row: &MyTableRow) -> u32 {
row.id
}
}
NOTE: If your IDE complains saying the trait bound MyTable: Table<MyTableRow> is not satisfied
, you can simply
ignore this. The Table
trait is implemented in the table
macro, but your IDE just doesn't know this.
To get rid of these warnings, you will have to enable the org.rust.cargo.evaluate.build.scripts
and org.rust.macros.proc
experimental features. In IntelliJ, you can accomplish this by pressing ⇧⌘A
(macOs) or ⌃⇧A
(Linux/Windows) and searching
for Experimental Features
, then enabling the two before-mentioned features.
Getting a row based on the uid
You can get a row that matches the uid using the get_row()
function.
Example
let vec = vec![
MyTableRow { id: 1, name: "Jimmy Page".to_string() },
MyTableRow { id: 2, name: "Slayer".to_string() },
MyTableRow { id: 3, name: "MGMT".to_string() }
];
let table = MyTable::from_vec(&vec);
let table_row = table.get_row(2).unwrap();
assert_eq!(vec[1], table_row.clone());
You can also get a row mutably using its uid using the get_row_mut(id)
method.
Example
let vec = vec![
MyTableRow { id: 1, name: "Swedish House Mafia".to_string() },
MyTableRow { id: 2, name: "Pink Floyd".to_string() },
MyTableRow { id: 3, name: "Nick Cave & The Bad Seeds".to_string() }
];
let vec_unedited = vec![
MyTableRow { id: 1, name: "Swedish House Mafia".to_string() },
MyTableRow { id: 2, name: "Pink Floyd".to_string() },
MyTableRow { id: 3, name: "Nick Cave".to_string() }
];
let table = MyTable::from_vec(&vec);
let mut table2 = MyTable::from_vec(&vec_unedited);
let row = table2.get_row_mut(3).unwrap();
row.name = format!("{} {}", row.name, "& The Bad Seeds");
assert_eq!(table2.get_rows(), table.get_rows());
Adding derive attributes
You can add derive attributes to your table, but you should put them beneath the #[table]
.
Example
#[table(MyTableRow)
#[derive(PartialEq)]
struct MyTable {}
Installing
Simply add the crate to your cargo.toml
.
[dependencies]
simple_tables = "0.3.0"
You can see the crate on crates.io
Contributing
Contributions and suggestions are always welcome.
If you know how to improve the crate just let me know.
You can also claim one of the issues in the issues tab.
When you contribute, make sure your commit passes the tests. You can run the tests by going into the tables directory
cd tables
and then running the tests with cargo test
. I also have a CircleCI set up for this
repository that will test all commits. If you add a new feature, make sure to also write a test for this feature.
Documentation
The documentation can be found here.
It is also available on docs.rs, here you can also find documentation of previous versions.
Use cases
This project started because I was working on an application that retrieved data from an SQL database. After making a debug representation of my table, I thought that it might be a good idea to make this more generic, and so I started working on this crate.
Here's a snippet if you are interested:
use mysql::PooledConn;
use mysql::prelude::Queryable;
use simple_tables::{IdTable, Table};
use simple_tables::macros::*;
#[table_row]
pub struct DatabaseEntry {
id: u32,
name: String,
year: u32,
ban_id: u32,
ban: String,
emails: Emails
}
#[table(rows = DatabaseEntry)]
pub struct Database {}
impl IdTable<u32, DatabaseEntry> for Database {
fn get_id_from_row(row: &DatabaseEntry) -> u32 {
row.id
}
}
impl Database {
pub fn fetch(conn: &mut PooledConn) -> Database {
let mut database: Database = Database::new();
let query = "\
SELECT Users.User_Id as id, Users.Name as name, Years.Year_Id as year, Bannen.Ban_Id as ban_id, Bannen.Naam as ban, Emails.Email as email FROM Users \
INNER JOIN Years ON Users.Year_Id = Years.Year_Id \
INNER JOIN Bannen ON Years.Ban_Id = Bannen.Ban_Id \
LEFT JOIN Emails ON Emails.Email_Id = Users.User_Id\
";
conn.query_iter(query)
.unwrap()
.for_each(|row| {
let (user_id, name, years_id, ban_id, ban_naam, email):
(u32, String, u32, u32, String, Option<String>)
= mysql::from_row(row.unwrap());
if let Some(email) = email {
if let Some(entry) = database.get_row_mut(user_id) {
entry.emails.push(email);
} else {
// Create new entry
database.push(
DatabaseEntry {
id: user_id,
name,
year: years_id,
ban_id,
ban: ban_naam,
emails: Emails { emails: Some(vec![email]) }
}
);
}
} else {
// Create new entry
database.push(
DatabaseEntry {
id: user_id,
name,
year: years_id,
ban_id,
ban: ban_naam,
emails: if email.is_some() { Emails { emails: Some(vec![email.unwrap()]) } } else { Emails { emails: None } }
}
);
}
});
database
}
}
#[derive(Clone, Debug)]
pub struct Emails {
emails: Option<Vec<String>>
}
impl Emails {
fn push(&mut self, s: String) {
if let Some(ref mut v) = self.emails {
v.push(s);
}
}
}
impl ToString for Emails {
fn to_string(&self) -> String {
let mut s = String::new();
if self.emails.is_some() {
let len = self.emails.as_ref().unwrap().len();
for (i, email) in self.emails.as_ref().unwrap().iter().enumerate() {
if i != len - 1 {
s.push_str(format!("{}{}", email, ", ").as_str());
} else {
s.push_str(email);
}
}
} else {
s.push_str("None");
}
s
}
}
If you end up building something with this crate, let me know!
Questions?
Feel free to open an issue or contact me directly with any questions.
License
This crate is licensed under the MIT License. Details in the LICENSE file.
Alternatively, this crate can also be licensed under the Apache 2.0-license.
Dependencies
~1.5MB
~37K SLoC