16 个版本 (6 个稳定版)
2.1.0 | 2023 年 5 月 31 日 |
---|---|
2.0.1 | 2023 年 2 月 17 日 |
2.0.0-rc.0 | 2022 年 7 月 16 日 |
1.1.2 | 2022 年 1 月 3 日 |
0.4.3 | 2018 年 2 月 19 日 |
在 数据库接口 中排名 164
每月下载量 101,082
被 28 个 crate 使用(直接使用 18 个)
28KB
404 行(不包括注释)
diesel-derive-enum
使用 Rust 枚举类型直接与 diesel
ORM 交互。
Diesel 是很棒的工具,但如果它能这样做会更好吗?
use crate::schema::my_table;
pub enum MyEnum {
Foo,
Bar,
BazQuxx,
}
fn do_some_work(data: MyEnum, connection: &mut Connection) {
insert_into(my_table)
.values(&data)
.execute(connection)
.unwrap();
}
遗憾的是,它不会自动工作,因为我们希望与 Diesel 一起使用的任何类型都必须实现各种特质。手动实现很麻烦,但使用 derive
宏会容易得多——这就是 diesel-derive-enum
的作用。
最新版本 2.1.0
已通过 diesel 2.1.0
和 rustc 1.65
的测试(我们试图与 diesel 保持同步)。对于 diesel 的早期版本,请查看此 crate 的 2.0.1
和 1.*
版本。
从 2.0.x
升级到 2.1.0
使用 diesel-cli
?由于上游更改,您可能需要修改现有的 diesel.toml
文件。从版本 2.1.0
开始,它 必须 包含以下行
[print-schema]
# ... other config ...
custom_type_derives = ["diesel::query_builder::QueryId"]
如果它还没有,请添加它!
使用 Diesel CLI 设置
此 crate 与 diesel-cli 集成良好——这是推荐的流程。请注意,目前这 仅 与 Postgres 一起工作——对于其他数据库或未使用 Diesel CLI 的情况,请参阅下一节。
Cargo.toml
[dependencies]
diesel-derive-enum = { version = "2.1.0", features = ["postgres"] }
假设我们的项目有以下 diesel.toml
(由 diesel setup
生成)
[print_schema]
file = "src/schema.rs"
custom_type_derives = ["diesel::query_builder::QueryId"]
以及以下 SQL
CREATE TYPE my_enum AS ENUM ('foo', 'bar', 'baz_quxx');
CREATE TABLE my_table (
id SERIAL PRIMARY KEY,
some_enum my_enum NOT NULL
);
然后运行 $ diesel migration run
将生成类似以下代码
// src/schema.rs -- autogenerated
pub mod sql_types {
#[derive(diesel::sql_types::SqlType, diesel::query_builder::QueryId)]
#[diesel(postgres_type(name = "my_enum"))]
pub struct MyEnum;
}
table! {
use diesel::types::Integer;
use super::sql_types::MyEnum;
my_table {
id -> Integer,
some_enum -> MyEnum
}
}
现在我们可以使用 diesel-derive-enum
来链接我们自己的枚举
// src/my_code.rs
#[derive(diesel_derive_enum::DbEnum)]
#[ExistingTypePath = "crate::schema::sql_types::MyEnum"]
pub enum MyEnum {
Foo,
Bar,
BazQuxx,
}
注意 ExistingTypePath
属性。这指示这个包导入(远程、自动生成的)类型,并在其上实现各种特质。就这样!现在我们可以使用 MyEnum
与 diesel
一起使用(见下方的 'Usage')。
无 Diesel CLI 的设置
如果你使用 mysql
或 sqlite
,或者你没有使用 diesel-cli
来生成你的模式,设置稍有不同。
Postgres
Cargo.toml
[dependencies]
diesel-derive-enum = { version = "2.1.0", features = ["postgres"] }
SQL
CREATE TYPE my_enum AS ENUM ('foo', 'bar', 'baz_quxx');
CREATE TABLE my_table (
id SERIAL PRIMARY KEY,
some_enum my_enum NOT NULL
);
Rust
#[derive(diesel_derive_enum::DbEnum)]
pub enum MyEnum {
Foo,
Bar,
BazQuxx,
}
// define your table
table! {
use diesel::types::Integer;
use super::MyEnumMapping;
my_table {
id -> Integer,
some_enum -> MyEnumMapping, // Generated Diesel type - see below for explanation
}
}
MySQL
Cargo.toml
[dependencies]
diesel-derive-enum = { version = "2.1.0", features = ["mysql"] }
SQL
CREATE TABLE my_table (
id SERIAL PRIMARY KEY,
my_enum enum('foo', 'bar', 'baz_quxx') NOT NULL -- note: snake_case
);
Rust
#[derive(diesel_derive_enum::DbEnum)]
pub enum MyEnum {
Foo,
Bar,
BazQuxx,
}
// define your table
table! {
use diesel::types::Integer;
use super::MyEnumMapping;
my_table {
id -> Integer,
some_enum -> MyEnumMapping, // Generated Diesel type - see below for explanation
}
}
sqlite
Cargo.toml
[dependencies]
diesel-derive-enum = { version = "2.1.0", features = ["sqlite"] }
SQL
CREATE TABLE my_table (
id SERIAL PRIMARY KEY,
my_enum TEXT CHECK(my_enum IN ('foo', 'bar', 'baz_quxx')) NOT NULL -- note: snake_case
);
Rust
#[derive(diesel_derive_enum::DbEnum)]
pub enum MyEnum {
Foo,
Bar,
BazQuxx,
}
// define your table
table! {
use diesel::types::Integer;
use super::MyEnumMapping;
my_table {
id -> Integer,
some_enum -> MyEnumMapping, // Generated Diesel type - see below for explanation
}
}
用法
一旦设置完毕,使用方法与你的选择数据库类似。我们可以定义一个结构体来填充/查询表
#[derive(Insertable, Queryable, Identifiable, Debug, PartialEq)]
#[diesel(table_name = my_table)]
struct MyRow {
id: i32,
some_enum: MyEnum,
}
并以自然的方式使用它
let data = vec![
MyRow {
id: 1,
some_enum: MyEnum::Foo,
},
MyRow {
id: 2,
some_enum: MyEnum::BazQuxx,
},
];
let connection = PgConnection::establish(/*...*/).unwrap();
let inserted = insert_into(my_table::table)
.values(&data)
.get_results(&connection)
.unwrap();
assert_eq!(data, inserted);
Postgres 数组也行!请参见 此示例。
枚举表示
枚举不是 SQL 标准的一部分,并且具有数据库特定的实现。
-
在 Postgres 中,我们将在模式内声明一个枚举作为单独的类型(
CREATE TYPE ...
),然后可以在多个表中使用。内部,枚举值被编码为 int(四字节)并在行内存储(比字符串更高效的表示)。 -
MySQL 与此类似,除了枚举不是作为单独的类型声明,而是对其父表的本地声明。它被编码为一到两个字节。
-
sqlite 没有枚举——实际上,它甚至 没有类型;你可以在任何列中存储任何类型的数据。相反,我们通过添加
CHECK
命令来模拟静态检查,如上所示。这并不提供更紧凑的编码,但确实确保了更好的数据完整性。请注意,如果你以某种方式检索了其他无效的文本作为枚举,diesel
将在反序列化时出错。
工作原理
Diesel 维护一组内部类型,这些类型与各种关系型数据库中可用的类型一一对应。每个内部类型反过来又映射到某种 Rust 本地类型。例如,Postgres 的 INTEGER
映射到 diesel::types::Integer
映射到 i32
。
仅限 postgres
,自 diesel-2.0.0
以来,diesel-cli 将在模式生成过程中创建 'dummy' 内部枚举映射类型。然后我们使用 ExistingTypePath
属性指定此类型的位置。
在没有指定 ExistingTypePath
的情况下,我们假设内部类型尚未生成,因此此宏将使用默认名称 {enum_name}Mapping
创建它。此名称可以用 DieselType
属性覆盖。
在任何情况下,此宏将对内部类型实施各种特性。此宏还将对用户定义的 枚举
类型实施各种特性。因此,用户定义的枚举可以直接插入(并从中检索)diesel数据库。
注意,默认情况下,我们假设可能的SQL枚举变体只是将Rust枚举变体翻译为 snake_case
。这些可以通过内联注解 #[db_rename = "..."]
进行重命名。
请参阅 此测试 了解重命名的示例。
您可以使用 #[DbValueStyle = "..."]
属性覆盖整个枚举的 snake_case
假设。仍然可以使用 #[db_rename = "..."]
对单个变体进行重命名。
DbValueStyle | 变体 | 值 |
---|---|---|
camelCase | BazQuxx | "bazQuxx" |
kebab-case | BazQuxx | "baz-quxx" |
PascalCase | BazQuxx | "BazQuxx" |
SCREAMING_SNAKE_CASE | BazQuxx | "BAZ_QUXX" |
UPPERCASE | BazQuxx | "BAZQUXX" |
snake_case | BazQuxx | "baz_quxx" |
verbatim | Baz__quxx | "Baz__quxx" |
请参阅 此测试 了解更改输出样式的示例。
许可证
根据以下之一获得许可
- Apache许可证第2版,(LICENSE-APACHE 或 https://www.apache.org/licenses/LICENSE-2.0)
- MIT许可证 (LICENSE-MIT 或 https://opensource.org/licenses/MIT)
依赖关系
~0.3–0.8MB
~19K SLoC