#table #macro #proc-macro

simple_tables

一个易于使用的Rust库,用于创建表格结构。包括用于轻松创建这些表格结构的宏。

5个不稳定版本

0.3.0 2022年1月17日
0.2.1 2022年1月12日
0.2.0 2022年1月12日
0.1.1 2022年1月11日
0.1.0 2022年1月11日

#417 in 可视化


用于 newsly

MIT/Apache

19KB
152

CirlceCI Language Licenses Crates.io Docs.rs

Simple Tables 是一个易于创建表格结构的Rust库。这是通过宏来实现的。

目录

概述

创建表格

要创建表格,您可以使用宏 table_row 来表示行的结构,并使用 table 来将结构体用作表格。

示例

use simple_tables::macros::{table_row, table};

#[table_row]
struct MyTableRow {
  id: u32,
  name: String,
  email: String,
  address: String
}

#[table(rows = MyTableRow)]
struct MyTable {}

这些宏将分别实现 TableRowTable 特性。您也可以手动实现它们。

函数

TableRowTable 特性定义了一组函数,其中大部分具有默认实现。使用 table_rowtable 宏将实现这些特性和相应的函数,针对您要针对的结构体。这些宏还提供了一些额外的函数。

ToString

这些宏还实现了表格的 ToString 特性,因此您可以使用 to_string 函数。

示例

use simple_tables::Table;

let rows: Vec<MyTableRow> = vec![
  MyTableRow{ id: 0, name: "David Bowie".to_string(), email: "[email protected]".to_string(), address: "England".to_string()},
  MyTableRow{ id: 1, name: "David Gilmour".to_string(), email: "[email protected]".to_string(), address: "England".to_string()},
  MyTableRow{ id: 2, name: "Opeth".to_string(), email: "[email protected]".to_string(), address: "Sweden".to_string()},
  MyTableRow{ id: 3, name: "The Beatles".to_string(), email: "[email protected]".to_string(), address: "England".to_string()}
];

let table = MyTable::from_vec(&rows);
let s = table.to_string();
println!("{}", s);

输出将如下

+----+---------------+-------------------+---------+
| 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 |
+----+---------------+-------------------+---------+

创建新的表格实例

let empty_table = MyTable::new();
let populated_table = MyTable::from_vec(&vec);

获取行

您可以使用以下获取器之一获取行

  • table.get_rows()
  • table.get_rows_mut()
  • table.get_row_at(index)

获取列

您可以使用 get_column 函数获取列的所有值。此函数接受一个闭包来获取每个条目的值。

示例

#[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);

插入行

示例

let row = MyTableRow { id: 4, name: "Pink Floyd", email: "[email protected]", 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_count() 函数来获取列的数量。您还可以使用 row_count() 获取行数。

具有UID的表格

我们可以指定具有唯一标识符的表格。以下示例展示了如何做到这一点

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 
  }
}

注意:如果你的IDE抱怨说 the trait bound MyTable: Table<MyTableRow> is not satisfied,你可以简单地忽略它。Table 特性在 table 宏中实现,但你的IDE只是不知道这一点。

要消除这些警告,你必须启用 org.rust.cargo.evaluate.build.scriptsorg.rust.macros.proc 实验特性。在 IntelliJ 中,你可以通过按 ⇧⌘A(macOs)或 ⌃⇧A(Linux/Windows)并搜索 Experimental Features 来完成此操作,然后启用前面提到的两个特性。

根据UID获取行

你可以使用 get_row() 函数来获取与 uid 匹配的行。

示例

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());

你还可以使用 get_row_mut(id) 方法以可变方式获取行。

示例

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());

添加 derive 属性

你可以向你的表格添加 derive 属性,但你应该将它们放在 #[table] 之下。

示例

#[table(MyTableRow)
#[derive(PartialEq)]
struct MyTable {}

安装

只需将软件包添加到你的 cargo.toml

[dependencies]
simple_tables = "0.3.0"

你可以在 crates.io 上看到该软件包。

贡献

贡献和建议总是受欢迎的。

如果你知道如何改进这个软件包,请让我知道。

你还可以在问题选项卡中声明一个问题。

当你做出贡献时,请确保你的提交通过了测试。你可以通过进入 cd tables 目录并运行测试 cargo test 来运行测试。我还为这个存储库设置了 CircleCI,它将测试所有提交。如果你添加了新功能,请确保也为这个功能编写测试。

文档

文档可以在 这里 找到。

它也可以在 docs.rs 上找到,你也可以在这里找到以前版本的文档。

用例

这个项目开始是因为我在开发一个从 SQL 数据库检索数据的应用程序。在制作了我表格的调试表示之后,我想这可能是一个好主意,使其更通用,所以我开始着手这个软件包。

如果你感兴趣,这里有一个片段

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
  }
}

如果你用这个软件包构建了某些东西,请告诉我!

有问题?

请随意打开一个问题或直接与我联系。

许可

此软件包受 MIT 许可证 许可。详情请参阅 LICENSE 文件。

或者,此软件包还可以在 Apache 2.0 许可证 下许可。

依赖项

~1.5MB
~34K SLoC