#diesel #postgresql #mysql #sql #sqlite

diesel-derive-enum

为在数据库中使用枚举类型生成柴油样板代码

16 个版本 (6 个稳定版)

2.1.0 2023 年 5 月 31 日
2.0.1 2023 年 2 月 17 日
2.0.0-rc.02022 年 7 月 16 日
1.1.2 2022 年 1 月 3 日
0.4.3 2018 年 2 月 19 日

数据库接口 中排名 164

Download history 27338/week @ 2024-03-14 27970/week @ 2024-03-21 28931/week @ 2024-03-28 25599/week @ 2024-04-04 29932/week @ 2024-04-11 28886/week @ 2024-04-18 26743/week @ 2024-04-25 29336/week @ 2024-05-02 31245/week @ 2024-05-09 31498/week @ 2024-05-16 24675/week @ 2024-05-23 25469/week @ 2024-05-30 25886/week @ 2024-06-06 25239/week @ 2024-06-13 25475/week @ 2024-06-20 19459/week @ 2024-06-27

每月下载量 101,082
28 crate 使用(直接使用 18 个)

MIT/Apache 许可证

28KB
404 行(不包括注释)

diesel-derive-enum

crates.io Build Status

使用 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.0rustc 1.65 的测试(我们试图与 diesel 保持同步)。对于 diesel 的早期版本,请查看此 crate 的 2.0.11.* 版本。

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 属性。这指示这个包导入(远程、自动生成的)类型,并在其上实现各种特质。就这样!现在我们可以使用 MyEnumdiesel 一起使用(见下方的 'Usage')。

无 Diesel CLI 的设置

如果你使用 mysqlsqlite,或者你没有使用 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"

请参阅 此测试 了解更改输出样式的示例。

许可证

根据以下之一获得许可

依赖关系

~0.3–0.8MB
~19K SLoC